Merge lp:~pedronis/ubuntu-push/kindpool-test-derace into lp:ubuntu-push

Proposed by Samuele Pedroni
Status: Superseded
Proposed branch: lp:~pedronis/ubuntu-push/kindpool-test-derace
Merge into: lp:ubuntu-push
Diff against target: 1106 lines (+405/-177)
18 files modified
bus/notifications/raw.go (+1/-1)
bus/notifications/raw_test.go (+13/-0)
click/click.go (+13/-0)
click/click_test.go (+18/-0)
client/client_test.go (+6/-2)
dependencies.tsv (+1/-1)
launch_helper/kindpool.go (+1/-1)
launch_helper/kindpool_test.go (+10/-3)
launch_helper/legacy/legacy.go (+20/-6)
launch_helper/legacy/legacy_test.go (+16/-2)
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:~pedronis/ubuntu-push/kindpool-test-derace
Reviewer Review Type Date Requested Status
Ubuntu Push Hackers Pending
Review via email: mp+231905@code.launchpad.net

Commit message

avoid rare race in kindpool_test.go

Description of the change

avoid rare race in test,

better to change globals once in SetUpSuite than again and again in SetUpTest

To post a comment you must log in.
314. By Samuele Pedroni

avoid rare race in test

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bus/notifications/raw.go'
2--- bus/notifications/raw.go 2014-07-27 00:58:08 +0000
3+++ bus/notifications/raw.go 2014-08-22 14:47:03 +0000
4@@ -144,7 +144,7 @@
5 }
6
7 hints := make(map[string]*dbus.Variant)
8- hints["x-canonical-secondary-icon"] = &dbus.Variant{app.Icon()}
9+ hints["x-canonical-secondary-icon"] = &dbus.Variant{app.SymbolicIcon()}
10
11 appId := app.Original()
12 actions := make([]string, 2*len(card.Actions))
13
14=== modified file 'bus/notifications/raw_test.go'
15--- bus/notifications/raw_test.go 2014-07-27 00:58:08 +0000
16+++ bus/notifications/raw_test.go 2014-08-22 14:47:03 +0000
17@@ -210,6 +210,19 @@
18 c.Check(hints["x-canonical-secondary-icon"], NotNil)
19 }
20
21+func (s *RawSuite) TestPresentUsesSymbolic(c *C) {
22+ endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
23+ raw := Raw(endp, s.log)
24+ worked := raw.Present(s.app, "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary", Popup: true}})
25+ c.Assert(worked, Equals, true)
26+ callArgs := testibus.GetCallArgs(endp)
27+ c.Assert(callArgs, HasLen, 1)
28+ c.Assert(callArgs[0].Args, HasLen, 8)
29+ hints, ok := callArgs[0].Args[6].(map[string]*dbus.Variant)
30+ c.Assert(ok, Equals, true)
31+ c.Check(hints["x-canonical-secondary-icon"].Value.(string), Equals, "-symbolic")
32+}
33+
34 func (s *RawSuite) TestPresentNoNotificationPanics(c *C) {
35 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
36 raw := Raw(endp, s.log)
37
38=== modified file 'click/click.go'
39--- click/click.go 2014-08-06 02:44:06 +0000
40+++ click/click.go 2014-08-22 14:47:03 +0000
41@@ -136,6 +136,19 @@
42 return cappinfo.AppIconFromDesktopId(app.DesktopId())
43 }
44
45+func _symbolic(icon string) string {
46+ if strings.ContainsRune(icon, '/') {
47+ return icon
48+ }
49+ return icon + "-symbolic"
50+}
51+
52+var symbolic = _symbolic
53+
54+func (app *AppId) SymbolicIcon() string {
55+ return symbolic(app.Icon())
56+}
57+
58 func (app *AppId) MarshalJSON() ([]byte, error) {
59 return json.Marshal(app.Original())
60 }
61
62=== modified file 'click/click_test.go'
63--- click/click_test.go 2014-08-06 03:50:14 +0000
64+++ click/click_test.go 2014-08-22 14:47:03 +0000
65@@ -182,3 +182,21 @@
66 c.Check(app, NotNil)
67 c.Check(app.Original(), Equals, "_non-existent-app")
68 }
69+
70+func (s *clickSuite) TestSymbolicAppendsSymbolicIfIconIsName(c *C) {
71+ symb := symbolic("foo")
72+ c.Check(symb, Equals, "foo-symbolic")
73+}
74+
75+func (s *clickSuite) TestSymbolicLeavesAloneIfIconIsPath(c *C) {
76+ symb := symbolic("foo/bar")
77+ c.Check(symb, Equals, "foo/bar")
78+}
79+
80+func (s *clickSuite) TestSymbolicIconCallsSymbolic(c *C) {
81+ symbolic = func(string) string { return "xyzzy" }
82+ defer func() { symbolic = _symbolic }()
83+ app, err := ParseAppId("_python3.4")
84+ c.Assert(err, IsNil)
85+ c.Check(app.SymbolicIcon(), Equals, "xyzzy")
86+}
87
88=== modified file 'client/client_test.go'
89--- client/client_test.go 2014-08-12 00:32:32 +0000
90+++ client/client_test.go 2014-08-22 14:47:03 +0000
91@@ -143,7 +143,11 @@
92
93 func (cs *clientSuite) SetUpSuite(c *C) {
94 config.IgnoreParsedFlags = true // because configure() uses <flags>
95- newIdentifier = func() (identifier.Id, error) { return idtesting.Settable(), nil }
96+ newIdentifier = func() (identifier.Id, error) {
97+ id := idtesting.Settable()
98+ id.Set("42") // must be hex of len 32
99+ return id, nil
100+ }
101 cs.timeouts = util.SwapTimeouts([]time.Duration{0})
102 cs.leveldbPath = ""
103 }
104@@ -1141,7 +1145,7 @@
105 // and now everthing is better! We have a config,
106 c.Check(string(cli.config.Addr), Equals, ":0")
107 // and a device id,
108- c.Check(cli.deviceId, HasLen, 32)
109+ c.Check(cli.deviceId, HasLen, 40)
110 // and a session,
111 c.Check(cli.session, NotNil)
112 // and a bus,
113
114=== modified file 'dependencies.tsv'
115--- dependencies.tsv 2014-07-26 07:28:59 +0000
116+++ dependencies.tsv 2014-08-22 14:47:03 +0000
117@@ -1,5 +1,5 @@
118 code.google.com/p/go-uuid hg 7dda39b2e7d5e265014674c5af696ba4186679e9 11
119 code.google.com/p/gosqlite hg 74691fb6f83716190870cde1b658538dd4b18eb0 15
120-launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140530132806-hpqbkxczif6o1dpz 127
121+launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140801110939-lzfql7fk76dt6ckd 128
122 launchpad.net/go-xdg/v0 bzr john.lenton@canonical.com-20140208094800-gubd5md7cro3mtxa 10
123 launchpad.net/gocheck bzr gustavo@niemeyer.net-20140225173054-xu9zlkf9kxhvow02 87
124
125=== modified file 'launch_helper/kindpool.go'
126--- launch_helper/kindpool.go 2014-07-29 15:24:01 +0000
127+++ launch_helper/kindpool.go 2014-08-22 14:47:03 +0000
128@@ -72,7 +72,7 @@
129 func DefaultLaunchers(log logger.Logger) map[string]HelperLauncher {
130 return map[string]HelperLauncher{
131 "click": cual.New(log),
132- "legacy": legacy.New(),
133+ "legacy": legacy.New(log),
134 }
135 }
136
137
138=== modified file 'launch_helper/kindpool_test.go'
139--- launch_helper/kindpool_test.go 2014-08-08 09:03:42 +0000
140+++ launch_helper/kindpool_test.go 2014-08-22 14:47:03 +0000
141@@ -102,16 +102,22 @@
142 return args
143 }
144
145+func (s *poolSuite) SetUpSuite(c *C) {
146+ xdgCacheHome = c.MkDir
147+}
148+
149+func (s *poolSuite) TearDownSuite(c *C) {
150+ xdgCacheHome = xdg.Cache.Home
151+}
152+
153 func (s *poolSuite) SetUpTest(c *C) {
154 s.log = helpers.NewTestLogger(c, "debug")
155 s.fakeLauncher = &fakeHelperLauncher{argCh: make(chan [5]string, 10)}
156 s.pool = NewHelperPool(map[string]HelperLauncher{"fake": s.fakeLauncher}, s.log)
157- xdgCacheHome = c.MkDir
158 }
159
160 func (s *poolSuite) TearDownTest(c *C) {
161 s.pool = nil
162- xdgCacheHome = xdg.Cache.Home
163 }
164
165 func (s *poolSuite) TestDefaultLaunchers(c *C) {
166@@ -379,8 +385,9 @@
167 }
168
169 func (s *poolSuite) TestGetTempFilename(c *C) {
170+ tmpDir := c.MkDir()
171 GetTempDir = func(pkgName string) (string, error) {
172- return c.MkDir(), nil
173+ return tmpDir, nil
174 }
175 // restore it when we are done
176 defer func() {
177
178=== modified file 'launch_helper/legacy/legacy.go'
179--- launch_helper/legacy/legacy.go 2014-07-18 20:45:21 +0000
180+++ launch_helper/legacy/legacy.go 2014-08-22 14:47:03 +0000
181@@ -18,20 +18,23 @@
182 package legacy
183
184 import (
185+ "io"
186 "os"
187 "os/exec"
188 "path/filepath"
189 "strconv"
190
191 "launchpad.net/ubuntu-push/click"
192+ "launchpad.net/ubuntu-push/logger"
193 )
194
195 type legacyHelperLauncher struct {
196+ log logger.Logger
197 done func(string)
198 }
199
200-func New() *legacyHelperLauncher {
201- return new(legacyHelperLauncher)
202+func New(log logger.Logger) *legacyHelperLauncher {
203+ return &legacyHelperLauncher{log: log}
204 }
205
206 func (lhl *legacyHelperLauncher) InstallObserver(done func(string)) error {
207@@ -50,9 +53,10 @@
208 func (lhl *legacyHelperLauncher) Launch(_, progname, f1, f2 string) (string, error) {
209 cmd := exec.Command(progname, f1, f2)
210 cmd.Stdin = nil
211- cmd.Stdout = nil
212- cmd.Stderr = nil
213-
214+ var stdout_r, stdout_w = io.Pipe()
215+ var stderr_r, stderr_w = io.Pipe()
216+ cmd.Stdout = stdout_w
217+ cmd.Stderr = stderr_w
218 err := cmd.Start()
219 if err != nil {
220 return "", err
221@@ -63,7 +67,17 @@
222 }
223 id := strconv.FormatInt((int64)(proc.Pid), 36)
224 go func() {
225- proc.Wait()
226+ state, p_err := proc.Wait()
227+ if p_err != nil || !state.Success() {
228+ // Helper failed, log output
229+ var data []byte
230+ stdout_w.Close()
231+ stdout_r.Read(data)
232+ lhl.log.Errorf("Legacy helper failed. Stdout: %s", data)
233+ stderr_w.Close()
234+ stderr_r.Read(data)
235+ lhl.log.Errorf("Legacy helper failed. Stderr: %s", data)
236+ }
237 lhl.done(id)
238 }()
239
240
241=== modified file 'launch_helper/legacy/legacy_test.go'
242--- launch_helper/legacy/legacy_test.go 2014-07-18 20:45:21 +0000
243+++ launch_helper/legacy/legacy_test.go 2014-08-22 14:47:03 +0000
244@@ -32,7 +32,7 @@
245 select {
246 case s := <-ch:
247 return s
248- case <-time.After(time.Second):
249+ case <-time.After(5 * time.Second):
250 c.Fatal("timed out waiting for value")
251 return ""
252 }
253@@ -42,12 +42,14 @@
254
255 type legacySuite struct {
256 lhl *legacyHelperLauncher
257+ log *helpers.TestLogger
258 }
259
260 var _ = Suite(&legacySuite{})
261
262 func (ls *legacySuite) SetUpTest(c *C) {
263- ls.lhl = New()
264+ ls.log = helpers.NewTestLogger(c, "info")
265+ ls.lhl = New(ls.log)
266 }
267
268 func (ls *legacySuite) TestInstallObserver(c *C) {
269@@ -94,12 +96,24 @@
270 c.Assert(err, NotNil)
271 }
272
273+func (ls *legacySuite) TestHelperFails(c *C) {
274+ ch := make(chan string, 1)
275+ c.Assert(ls.lhl.InstallObserver(func(id string) { ch <- id }), IsNil)
276+
277+ _, err := ls.lhl.Launch("", "/bin/false", "", "")
278+ c.Assert(err, IsNil)
279+
280+ takeNext(ch, c)
281+ c.Check(ls.log.Captured(), Matches, "(?s).*Legacy helper failed.*")
282+}
283+
284 func (ls *legacySuite) TestStop(c *C) {
285 ch := make(chan string, 1)
286 c.Assert(ls.lhl.InstallObserver(func(id string) { ch <- id }), IsNil)
287
288 exe := helpers.ScriptAbsPath("slow-helper.sh")
289 id, err := ls.lhl.Launch("", exe, "", "")
290+ c.Assert(err, IsNil)
291
292 err = ls.lhl.Stop("", "===")
293 c.Check(err, NotNil) // not a valid id
294
295=== modified file 'server/acceptance/acceptanceclient.go'
296--- server/acceptance/acceptanceclient.go 2014-06-05 09:32:43 +0000
297+++ server/acceptance/acceptanceclient.go 2014-08-22 14:47:03 +0000
298@@ -19,9 +19,7 @@
299
300 import (
301 "crypto/tls"
302- "crypto/x509"
303 "encoding/json"
304- "errors"
305 "fmt"
306 "net"
307 "strings"
308@@ -40,35 +38,36 @@
309 ImageChannel string
310 ServerAddr string
311 ExchangeTimeout time.Duration
312- CertPEMBlock []byte
313 ReportPings bool
314 Levels map[string]int64
315- Insecure bool // don't verify certs
316+ TLSConfig *tls.Config
317 Prefix string // prefix for events
318 Auth string
319 // connection
320 Connection net.Conn
321 }
322
323-// Dial connects to a server using the configuration in the ClientSession
324-// and sets up the connection.
325+// Dial connects to a server using the configuration in the
326+// ClientSession and sets up the connection.
327 func (sess *ClientSession) Dial() error {
328 conn, err := net.DialTimeout("tcp", sess.ServerAddr, sess.ExchangeTimeout)
329 if err != nil {
330 return err
331 }
332- tlsConfig := &tls.Config{}
333- if sess.CertPEMBlock != nil {
334- cp := x509.NewCertPool()
335- ok := cp.AppendCertsFromPEM(sess.CertPEMBlock)
336- if !ok {
337- return errors.New("dial: could not parse certificate")
338- }
339- tlsConfig.RootCAs = cp
340+ sess.TLSWrapAndSet(conn)
341+ return nil
342+}
343+
344+// TLSWrapAndSet wraps a socket connection in tls and sets it as
345+// session.Connection. For use instead of Dial().
346+func (sess *ClientSession) TLSWrapAndSet(conn net.Conn) {
347+ var tlsConfig *tls.Config
348+ if sess.TLSConfig != nil {
349+ tlsConfig = sess.TLSConfig
350+ } else {
351+ tlsConfig = &tls.Config{}
352 }
353- tlsConfig.InsecureSkipVerify = sess.Insecure
354 sess.Connection = tls.Client(conn, tlsConfig)
355- return nil
356 }
357
358 type serverMsg struct {
359
360=== added file 'server/acceptance/cmd/acceptanceclient.go'
361--- server/acceptance/cmd/acceptanceclient.go 1970-01-01 00:00:00 +0000
362+++ server/acceptance/cmd/acceptanceclient.go 2014-08-22 14:47:03 +0000
363@@ -0,0 +1,54 @@
364+/*
365+ Copyright 2013-2014 Canonical Ltd.
366+
367+ This program is free software: you can redistribute it and/or modify it
368+ under the terms of the GNU General Public License version 3, as published
369+ by the Free Software Foundation.
370+
371+ This program is distributed in the hope that it will be useful, but
372+ WITHOUT ANY WARRANTY; without even the implied warranties of
373+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
374+ PURPOSE. See the GNU General Public License for more details.
375+
376+ You should have received a copy of the GNU General Public License along
377+ with this program. If not, see <http://www.gnu.org/licenses/>.
378+*/
379+
380+// acceptanceclient command for playing.
381+package main
382+
383+import (
384+ "log"
385+ "os/exec"
386+ "strings"
387+
388+ "launchpad.net/ubuntu-push/server/acceptance"
389+ "launchpad.net/ubuntu-push/server/acceptance/kit"
390+)
391+
392+type configuration struct {
393+ kit.Configuration
394+ AuthHelper string `json:"auth_helper"`
395+ WaitFor string `json:"wait_for"`
396+}
397+
398+func main() {
399+ kit.Defaults["auth_helper"] = ""
400+ kit.Defaults["wait_for"] = ""
401+ cfg := &configuration{}
402+ kit.CliLoop(cfg, &cfg.Configuration, func(session *acceptance.ClientSession, cfgDir string) {
403+ log.Printf("with: %#v", session)
404+ }, func(url string) string {
405+ if cfg.AuthHelper == "" {
406+ return ""
407+ }
408+ auth, err := exec.Command(cfg.AuthHelper, url).Output()
409+ if err != nil {
410+ log.Fatalf("auth helper: %v", err)
411+ }
412+ return strings.TrimSpace(string(auth))
413+ }, func() string {
414+ return cfg.WaitFor
415+ }, func() {
416+ })
417+}
418
419=== added directory 'server/acceptance/kit'
420=== added file 'server/acceptance/kit/api.go'
421--- server/acceptance/kit/api.go 1970-01-01 00:00:00 +0000
422+++ server/acceptance/kit/api.go 2014-08-22 14:47:03 +0000
423@@ -0,0 +1,82 @@
424+/*
425+ Copyright 2013-2014 Canonical Ltd.
426+
427+ This program is free software: you can redistribute it and/or modify it
428+ under the terms of the GNU General Public License version 3, as published
429+ by the Free Software Foundation.
430+
431+ This program is distributed in the hope that it will be useful, but
432+ WITHOUT ANY WARRANTY; without even the implied warranties of
433+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
434+ PURPOSE. See the GNU General Public License for more details.
435+
436+ You should have received a copy of the GNU General Public License along
437+ with this program. If not, see <http://www.gnu.org/licenses/>.
438+*/
439+
440+// Package kit contains reusable building blocks for acceptance.
441+package kit
442+
443+import (
444+ "bytes"
445+ "crypto/tls"
446+ "encoding/json"
447+ "errors"
448+ "io/ioutil"
449+ "net/http"
450+)
451+
452+// APIClient helps making api requests.
453+type APIClient struct {
454+ ServerAPIURL string
455+ // hook to adjust requests
456+ MassageRequest func(req *http.Request, message interface{}) *http.Request
457+ // other state
458+ httpClient *http.Client
459+}
460+
461+// SetupClient sets up the http client to make requests.
462+func (api *APIClient) SetupClient(tlsConfig *tls.Config) {
463+ api.httpClient = &http.Client{
464+ Transport: &http.Transport{TLSClientConfig: tlsConfig},
465+ }
466+}
467+
468+var ErrNOk = errors.New("not ok")
469+
470+// Post a API request.
471+func (api *APIClient) PostRequest(path string, message interface{}) (map[string]interface{}, error) {
472+ packedMessage, err := json.Marshal(message)
473+ if err != nil {
474+ panic(err)
475+ }
476+ reader := bytes.NewReader(packedMessage)
477+
478+ url := api.ServerAPIURL + path
479+ request, _ := http.NewRequest("POST", url, reader)
480+ request.ContentLength = int64(reader.Len())
481+ request.Header.Set("Content-Type", "application/json")
482+
483+ if api.MassageRequest != nil {
484+ request = api.MassageRequest(request, message)
485+ }
486+
487+ resp, err := api.httpClient.Do(request)
488+ if err != nil {
489+ return nil, err
490+ }
491+ defer resp.Body.Close()
492+ body, err := ioutil.ReadAll(resp.Body)
493+ if err != nil {
494+ return nil, err
495+ }
496+ var res map[string]interface{}
497+ err = json.Unmarshal(body, &res)
498+ if err != nil {
499+ return nil, err
500+ }
501+ if ok, _ := res["ok"].(bool); !ok {
502+ return res, ErrNOk
503+ }
504+ return res, nil
505+}
506
507=== renamed file 'server/acceptance/cmd/acceptanceclient.go' => 'server/acceptance/kit/cliloop.go'
508--- server/acceptance/cmd/acceptanceclient.go 2014-07-11 21:24:21 +0000
509+++ server/acceptance/kit/cliloop.go 2014-08-22 14:47:03 +0000
510@@ -14,15 +14,13 @@
511 with this program. If not, see <http://www.gnu.org/licenses/>.
512 */
513
514-// acceptanceclient command for playing.
515-package main
516+package kit
517
518 import (
519 "flag"
520 "fmt"
521 "log"
522 "os"
523- "os/exec"
524 "path/filepath"
525 "regexp"
526 "strings"
527@@ -32,27 +30,58 @@
528 "launchpad.net/ubuntu-push/server/acceptance"
529 )
530
531-var (
532- insecureFlag = flag.Bool("insecure", false, "disable checking of server certificate and hostname")
533- reportPingsFlag = flag.Bool("reportPings", true, "report each Ping from the server")
534- deviceModel = flag.String("model", "?", "device image model")
535- imageChannel = flag.String("imageChannel", "?", "image channel")
536-)
537-
538-type configuration struct {
539+type Configuration struct {
540 // session configuration
541 ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"`
542 // server connection config
543- Addr config.ConfigHostPort
544- CertPEMFile string `json:"cert_pem_file"`
545- AuthHelper string `json:"auth_helper"`
546- RunTimeout config.ConfigTimeDuration `json:"run_timeout"`
547- WaitFor string `json:"wait_for"`
548-}
549-
550-func main() {
551+ Target string `json:"target"`
552+ Addr config.ConfigHostPort `json:"addr"`
553+ CertPEMFile string `json:"cert_pem_file"`
554+ Insecure bool `json:"insecure" help:"disable checking of server certificate and hostname"`
555+ Domain string `json:"domain" help:"domain for tls connect"`
556+ // run timeout
557+ RunTimeout config.ConfigTimeDuration `json:"run_timeout"`
558+ // flags
559+ ReportPings bool `json:"reportPings" help:"report each Ping from the server"`
560+ DeviceModel string `json:"model" help:"device image model"`
561+ ImageChannel string `json:"imageChannel" help:"image channel"`
562+}
563+
564+func (cfg *Configuration) PickByTarget(what, productionValue, stagingValue string) (value string) {
565+ switch cfg.Target {
566+ case "production":
567+ value = productionValue
568+ case "staging":
569+ value = stagingValue
570+ case "":
571+ log.Fatalf("either %s or target must be given", what)
572+ default:
573+ log.Fatalf("if specified target should be production|staging")
574+ }
575+ return
576+}
577+
578+// Control.
579+var (
580+ Name = "acceptanceclient"
581+ Defaults = map[string]interface{}{
582+ "target": "",
583+ "addr": ":0",
584+ "exchange_timeout": "5s",
585+ "cert_pem_file": "",
586+ "insecure": false,
587+ "domain": "",
588+ "run_timeout": "0s",
589+ "reportPings": true,
590+ "model": "?",
591+ "imageChannel": "?",
592+ }
593+)
594+
595+// CliLoop parses command line arguments and runs a client loop.
596+func CliLoop(totalCfg interface{}, cfg *Configuration, onSetup func(sess *acceptance.ClientSession, cfgDir string), auth func(string) string, waitFor func() string, onConnect func()) {
597 flag.Usage = func() {
598- fmt.Fprintf(os.Stderr, "Usage: acceptancclient [options] <device id>\n")
599+ fmt.Fprintf(os.Stderr, "Usage: %s [options] <device id>\n", Name)
600 flag.PrintDefaults()
601 }
602 missingArg := func(what string) {
603@@ -60,14 +89,7 @@
604 flag.Usage()
605 os.Exit(2)
606 }
607- cfg := &configuration{}
608- err := config.ReadFilesDefaults(cfg, map[string]interface{}{
609- "exchange_timeout": "5s",
610- "cert_pem_file": "",
611- "auth_helper": "",
612- "run_timeout": "0s",
613- "wait_for": "",
614- }, "<flags>")
615+ err := config.ReadFilesDefaults(totalCfg, Defaults, "<flags>")
616 if err != nil {
617 log.Fatalf("reading config: %v", err)
618 }
619@@ -76,36 +98,34 @@
620 case narg < 1:
621 missingArg("device-id")
622 }
623+ addr := ""
624+ if cfg.Addr == ":0" {
625+ addr = cfg.PickByTarget("addr", "push-delivery.ubuntu.com:443",
626+ "push-delivery.staging.ubuntu.com:443")
627+ } else {
628+ addr = cfg.Addr.HostPort()
629+ }
630 session := &acceptance.ClientSession{
631 ExchangeTimeout: cfg.ExchangeTimeout.TimeDuration(),
632- ServerAddr: cfg.Addr.HostPort(),
633+ ServerAddr: addr,
634 DeviceId: flag.Arg(0),
635 // flags
636- Model: *deviceModel,
637- ImageChannel: *imageChannel,
638- ReportPings: *reportPingsFlag,
639- Insecure: *insecureFlag,
640- }
641- log.Printf("with: %#v", session)
642- if !*insecureFlag && cfg.CertPEMFile != "" {
643- cfgDir := filepath.Dir(flag.Lookup("cfg@").Value.String())
644- log.Printf("cert: %v relToDir: %v", cfg.CertPEMFile, cfgDir)
645- session.CertPEMBlock, err = config.LoadFile(cfg.CertPEMFile, cfgDir)
646- if err != nil {
647- log.Fatalf("reading CertPEMFile: %v", err)
648- }
649- }
650- if len(cfg.AuthHelper) != 0 {
651- auth, err := exec.Command(cfg.AuthHelper, "https://push.ubuntu.com/").Output()
652- if err != nil {
653- log.Fatalf("auth helper: %v", err)
654- }
655- session.Auth = strings.TrimSpace(string(auth))
656- }
657+ Model: cfg.DeviceModel,
658+ ImageChannel: cfg.ImageChannel,
659+ ReportPings: cfg.ReportPings,
660+ }
661+ cfgDir := filepath.Dir(flag.Lookup("cfg@").Value.String())
662+ onSetup(session, cfgDir)
663+ session.TLSConfig, err = MakeTLSConfig(cfg.Domain, cfg.Insecure, cfg.CertPEMFile, cfgDir)
664+ if err != nil {
665+ log.Fatalf("tls config: %v", err)
666+ }
667+ session.Auth = auth("https://push.ubuntu.com/")
668 var waitForRegexp *regexp.Regexp
669- if cfg.WaitFor != "" {
670+ waitForStr := waitFor()
671+ if waitForStr != "" {
672 var err error
673- waitForRegexp, err = regexp.Compile(cfg.WaitFor)
674+ waitForRegexp, err = regexp.Compile(waitForStr)
675 if err != nil {
676 log.Fatalf("wait_for regexp: %v", err)
677 }
678@@ -118,6 +138,9 @@
679 go func() {
680 for {
681 ev := <-events
682+ if strings.HasPrefix(ev, "connected") {
683+ onConnect()
684+ }
685 if waitForRegexp != nil && waitForRegexp.MatchString(ev) {
686 log.Println("<matching-event>:", ev)
687 os.Exit(0)
688
689=== added file 'server/acceptance/kit/helpers.go'
690--- server/acceptance/kit/helpers.go 1970-01-01 00:00:00 +0000
691+++ server/acceptance/kit/helpers.go 2014-08-22 14:47:03 +0000
692@@ -0,0 +1,46 @@
693+/*
694+ Copyright 2013-2014 Canonical Ltd.
695+
696+ This program is free software: you can redistribute it and/or modify it
697+ under the terms of the GNU General Public License version 3, as published
698+ by the Free Software Foundation.
699+
700+ This program is distributed in the hope that it will be useful, but
701+ WITHOUT ANY WARRANTY; without even the implied warranties of
702+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
703+ PURPOSE. See the GNU General Public License for more details.
704+
705+ You should have received a copy of the GNU General Public License along
706+ with this program. If not, see <http://www.gnu.org/licenses/>.
707+*/
708+
709+package kit
710+
711+import (
712+ "crypto/tls"
713+ "crypto/x509"
714+ "fmt"
715+
716+ "launchpad.net/ubuntu-push/config"
717+)
718+
719+// MakeTLSConfig makes a tls.Config, optionally reading a cert from
720+// disk, possibly relative to relDir.
721+func MakeTLSConfig(domain string, insecure bool, certPEMFile string, relDir string) (*tls.Config, error) {
722+ tlsConfig := &tls.Config{}
723+ tlsConfig.ServerName = domain
724+ tlsConfig.InsecureSkipVerify = insecure
725+ if !insecure && certPEMFile != "" {
726+ certPEMBlock, err := config.LoadFile(certPEMFile, relDir)
727+ if err != nil {
728+ return nil, fmt.Errorf("reading cert: %v", err)
729+ }
730+ cp := x509.NewCertPool()
731+ ok := cp.AppendCertsFromPEM(certPEMBlock)
732+ if !ok {
733+ return nil, fmt.Errorf("could not parse certificate")
734+ }
735+ tlsConfig.RootCAs = cp
736+ }
737+ return tlsConfig, nil
738+}
739
740=== modified file 'server/acceptance/suites/broadcast.go'
741--- server/acceptance/suites/broadcast.go 2014-06-25 11:00:15 +0000
742+++ server/acceptance/suites/broadcast.go 2014-08-22 14:47:03 +0000
743@@ -41,8 +41,7 @@
744 ExpireOn: future,
745 Data: json.RawMessage(`{"img1/m1": 42}`),
746 })
747- c.Assert(err, IsNil)
748- c.Assert(got, Matches, OK)
749+ c.Assert(err, IsNil, Commentf("%v", got))
750 c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`)
751 stop()
752 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)
753@@ -56,14 +55,13 @@
754 ExpireOn: future,
755 Data: json.RawMessage(`{"img1/m2": 10}`),
756 })
757- c.Assert(err, IsNil)
758+ c.Assert(err, IsNil, Commentf("%v", got))
759 got, err = s.PostRequest("/broadcast", &api.Broadcast{
760 Channel: "system",
761 ExpireOn: future,
762 Data: json.RawMessage(`{"img1/m1": 20}`),
763 })
764- c.Assert(err, IsNil)
765- c.Assert(got, Matches, OK)
766+ c.Assert(err, IsNil, Commentf("%v", got))
767 c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"img1/m1":20}]`)
768 stop()
769 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)
770@@ -77,8 +75,7 @@
771 ExpireOn: future,
772 Data: json.RawMessage(`{"img1/m1": 1}`),
773 })
774- c.Assert(err, IsNil)
775- c.Assert(got, Matches, OK)
776+ c.Assert(err, IsNil, Commentf("%v", got))
777
778 events, errCh, stop := s.StartClient(c, "DEVB", nil)
779 // gettting pending on connect
780@@ -97,8 +94,7 @@
781 ExpireOn: future,
782 Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)),
783 })
784- c.Assert(err, IsNil)
785- c.Assert(got, Matches, OK)
786+ c.Assert(err, IsNil, Commentf("%v", got))
787 }
788
789 events, errCh, stop := s.StartClient(c, "DEVC", nil)
790@@ -130,8 +126,7 @@
791 ExpireOn: future,
792 Data: json.RawMessage(`{"img1/m1": 42}`),
793 })
794- c.Assert(err, IsNil)
795- c.Assert(got, Matches, OK)
796+ c.Assert(err, IsNil, Commentf("%v", got))
797 c.Check(NextEvent(events1, errCh1), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`)
798 c.Check(NextEvent(events2, errCh2), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`)
799 stop1()
800@@ -149,8 +144,7 @@
801 ExpireOn: future,
802 Data: json.RawMessage(`{"img1/m1": 1}`),
803 })
804- c.Assert(err, IsNil)
805- c.Assert(got, Matches, OK)
806+ c.Assert(err, IsNil, Commentf("%v", got))
807 c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":1}]`)
808 stop()
809 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)
810@@ -161,8 +155,7 @@
811 ExpireOn: future,
812 Data: json.RawMessage(`{"img1/m1": 2}`),
813 })
814- c.Assert(err, IsNil)
815- c.Assert(got, Matches, OK)
816+ c.Assert(err, IsNil, Commentf("%v", got))
817 // reconnect, provide levels, get only later notification
818 events, errCh, stop = s.StartClient(c, "DEVD", map[string]int64{
819 protocol.SystemChannelId: 1,
820@@ -180,15 +173,13 @@
821 ExpireOn: future,
822 Data: json.RawMessage(`{"img1/m1": 1}`),
823 })
824- c.Assert(err, IsNil)
825- c.Assert(got, Matches, OK)
826+ c.Assert(err, IsNil, Commentf("%v", got))
827 got, err = s.PostRequest("/broadcast", &api.Broadcast{
828 Channel: "system",
829 ExpireOn: future,
830 Data: json.RawMessage(`{"img1/m1": 2}`),
831 })
832- c.Assert(err, IsNil)
833- c.Assert(got, Matches, OK)
834+ c.Assert(err, IsNil, Commentf("%v", got))
835
836 events, errCh, stop := s.StartClient(c, "DEVB", map[string]int64{
837 protocol.SystemChannelId: 10,
838@@ -219,15 +210,13 @@
839 ExpireOn: future,
840 Data: json.RawMessage(`{"img1/m1": 1}`),
841 })
842- c.Assert(err, IsNil)
843- c.Assert(got, Matches, OK)
844+ c.Assert(err, IsNil, Commentf("%v", got))
845 got, err = s.PostRequest("/broadcast", &api.Broadcast{
846 Channel: "system",
847 ExpireOn: future,
848 Data: json.RawMessage(`{"img1/m1": 2}`),
849 })
850- c.Assert(err, IsNil)
851- c.Assert(got, Matches, OK)
852+ c.Assert(err, IsNil, Commentf("%v", got))
853
854 events, errCh, stop := s.StartClient(c, "DEVB", map[string]int64{
855 protocol.SystemChannelId: -10,
856@@ -246,15 +235,13 @@
857 ExpireOn: future,
858 Data: json.RawMessage(`{"img1/m1": 1}`),
859 })
860- c.Assert(err, IsNil)
861- c.Assert(got, Matches, OK)
862+ c.Assert(err, IsNil, Commentf("%v", got))
863 got, err = s.PostRequest("/broadcast", &api.Broadcast{
864 Channel: "system",
865 ExpireOn: time.Now().Add(1 * time.Second).Format(time.RFC3339),
866 Data: json.RawMessage(`{"img1/m1": 2}`),
867 })
868- c.Assert(err, IsNil)
869- c.Assert(got, Matches, OK)
870+ c.Assert(err, IsNil, Commentf("%v", got))
871
872 time.Sleep(2 * time.Second)
873 // second broadcast is expired
874
875=== modified file 'server/acceptance/suites/suite.go'
876--- server/acceptance/suites/suite.go 2014-06-25 11:00:15 +0000
877+++ server/acceptance/suites/suite.go 2014-08-22 14:47:03 +0000
878@@ -18,13 +18,9 @@
879 package suites
880
881 import (
882- "bytes"
883- "encoding/json"
884 "flag"
885 "fmt"
886- "io/ioutil"
887 "net"
888- "net/http"
889 "os"
890 "runtime"
891 "time"
892@@ -32,6 +28,7 @@
893 . "launchpad.net/gocheck"
894
895 "launchpad.net/ubuntu-push/server/acceptance"
896+ "launchpad.net/ubuntu-push/server/acceptance/kit"
897 helpers "launchpad.net/ubuntu-push/testing"
898 )
899
900@@ -84,14 +81,10 @@
901 StartServer func(c *C, s *AcceptanceSuite, handle *ServerHandle)
902 // populated by StartServer
903 ServerHandle
904- ServerAPIURL string
905+ kit.APIClient // has ServerAPIURL
906 // KillGroup should be populated by StartServer with functions
907 // to kill the server process
908 KillGroup map[string]func(os.Signal)
909- // hook to adjust requests
910- MassageRequest func(req *http.Request, message interface{}) *http.Request
911- // other state
912- httpClient *http.Client
913 }
914
915 // Start a new server for each test.
916@@ -101,7 +94,7 @@
917 c.Assert(s.ServerHandle.ServerEvents, NotNil)
918 c.Assert(s.ServerHandle.ServerAddr, Not(Equals), "")
919 c.Assert(s.ServerAPIURL, Not(Equals), "")
920- s.httpClient = &http.Client{}
921+ s.SetupClient(nil)
922 }
923
924 func (s *AcceptanceSuite) TearDownTest(c *C) {
925@@ -110,45 +103,19 @@
926 }
927 }
928
929-// Post a API request.
930-func (s *AcceptanceSuite) PostRequest(path string, message interface{}) (string, error) {
931- packedMessage, err := json.Marshal(message)
932- if err != nil {
933- panic(err)
934- }
935- reader := bytes.NewReader(packedMessage)
936-
937- url := s.ServerAPIURL + path
938- request, _ := http.NewRequest("POST", url, reader)
939- request.ContentLength = int64(reader.Len())
940- request.Header.Set("Content-Type", "application/json")
941-
942- if s.MassageRequest != nil {
943- request = s.MassageRequest(request, message)
944- }
945-
946- resp, err := s.httpClient.Do(request)
947- if err != nil {
948- panic(err)
949- }
950- defer resp.Body.Close()
951- body, err := ioutil.ReadAll(resp.Body)
952- return string(body), err
953-}
954-
955 func testClientSession(addr string, deviceId, model, imageChannel string, reportPings bool) *acceptance.ClientSession {
956- certPEMBlock, err := ioutil.ReadFile(helpers.SourceRelative("../ssl/testing.cert"))
957+ tlsConfig, err := kit.MakeTLSConfig("", false, helpers.SourceRelative("../ssl/testing.cert"), "")
958 if err != nil {
959 panic(fmt.Sprintf("could not read ssl/testing.cert: %v", err))
960 }
961 return &acceptance.ClientSession{
962 ExchangeTimeout: 100 * time.Millisecond,
963 ServerAddr: addr,
964- CertPEMBlock: certPEMBlock,
965 DeviceId: deviceId,
966 Model: model,
967 ImageChannel: imageChannel,
968 ReportPings: reportPings,
969+ TLSConfig: tlsConfig,
970 }
971 }
972
973@@ -198,5 +165,3 @@
974
975 // Long after the end of the tests.
976 var future = time.Now().Add(9 * time.Hour).Format(time.RFC3339)
977-
978-const OK = `.*"ok":true.*`
979
980=== modified file 'server/acceptance/suites/unicast.go'
981--- server/acceptance/suites/unicast.go 2014-07-14 15:23:17 +0000
982+++ server/acceptance/suites/unicast.go 2014-08-22 14:47:03 +0000
983@@ -23,6 +23,7 @@
984
985 . "launchpad.net/gocheck"
986
987+ "launchpad.net/ubuntu-push/server/acceptance/kit"
988 "launchpad.net/ubuntu-push/server/api"
989 )
990
991@@ -45,20 +46,15 @@
992 DeviceId: "DEV1",
993 AppId: "app1",
994 })
995- c.Assert(err, IsNil)
996- c.Assert(res, Matches, OK)
997- var reg map[string]interface{}
998- err = json.Unmarshal([]byte(res), &reg)
999- c.Assert(err, IsNil)
1000+ c.Assert(err, IsNil, Commentf("%v", res))
1001 events, errCh, stop := s.StartClientAuth(c, "DEV1", nil, auth)
1002 got, err := s.PostRequest("/notify", &api.Unicast{
1003- Token: reg["token"].(string),
1004+ Token: res["token"].(string),
1005 AppId: "app1",
1006 ExpireOn: future,
1007 Data: json.RawMessage(`{"a": 42}`),
1008 })
1009- c.Assert(err, IsNil)
1010- c.Assert(got, Matches, OK)
1011+ c.Assert(err, IsNil, Commentf("%v", got))
1012 c.Check(NextEvent(events, errCh), Equals, `unicast app:app1 payload:{"a":42};`)
1013 stop()
1014 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)
1015@@ -80,8 +76,7 @@
1016 ExpireOn: future,
1017 Data: json.RawMessage(`{"to": 1}`),
1018 })
1019- c.Assert(err, IsNil)
1020- c.Assert(got, Matches, OK)
1021+ c.Assert(err, IsNil, Commentf("%v", got))
1022 got, err = s.PostRequest("/notify", &api.Unicast{
1023 UserId: userId2,
1024 DeviceId: "DEV2",
1025@@ -89,8 +84,7 @@
1026 ExpireOn: future,
1027 Data: json.RawMessage(`{"to": 2}`),
1028 })
1029- c.Assert(err, IsNil)
1030- c.Assert(got, Matches, OK)
1031+ c.Assert(err, IsNil, Commentf("%v", got))
1032 c.Check(NextEvent(events1, errCh1), Equals, `unicast app:app1 payload:{"to":1};`)
1033 c.Check(NextEvent(events2, errCh2), Equals, `unicast app:app1 payload:{"to":2};`)
1034 stop1()
1035@@ -111,8 +105,7 @@
1036 ExpireOn: future,
1037 Data: json.RawMessage(`{"a": 42}`),
1038 })
1039- c.Assert(err, IsNil)
1040- c.Assert(got, Matches, OK)
1041+ c.Assert(err, IsNil, Commentf("%v", got))
1042
1043 // get pending on connect
1044 events, errCh, stop := s.StartClientAuth(c, "DEV1", nil, auth)
1045@@ -134,8 +127,7 @@
1046 ExpireOn: future,
1047 Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)),
1048 })
1049- c.Assert(err, IsNil)
1050- c.Assert(got, Matches, OK)
1051+ c.Assert(err, IsNil, Commentf("%v", got))
1052 }
1053
1054 events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth)
1055@@ -168,8 +160,7 @@
1056 ExpireOn: future,
1057 Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)),
1058 })
1059- c.Assert(err, IsNil)
1060- c.Assert(got, Matches, OK)
1061+ c.Assert(err, IsNil, Commentf("%v", got))
1062 }
1063
1064 got, err := s.PostRequest("/notify", &api.Unicast{
1065@@ -179,8 +170,9 @@
1066 ExpireOn: future,
1067 Data: json.RawMessage(fmt.Sprintf(payloadFmt, MaxNotificationsPerApplication)),
1068 })
1069- c.Assert(err, IsNil)
1070- c.Assert(got, Matches, `.*"error":"too-many-pending".*`)
1071+ c.Assert(err, Equals, kit.ErrNOk, Commentf("%v", got))
1072+ errorStr, _ := got["error"].(string)
1073+ c.Assert(errorStr, Equals, "too-many-pending")
1074
1075 // clear all pending
1076 got, err = s.PostRequest("/notify", &api.Unicast{
1077@@ -191,8 +183,7 @@
1078 Data: json.RawMessage(fmt.Sprintf(payloadFmt, 1000)),
1079 ClearPending: true,
1080 })
1081- c.Assert(err, IsNil)
1082- c.Assert(got, Matches, OK)
1083+ c.Assert(err, IsNil, Commentf("%v", got))
1084
1085 events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth)
1086 // getting the 1 pending on connect
1087@@ -214,8 +205,7 @@
1088 Data: json.RawMessage(`{"m": 1}`),
1089 ReplaceTag: "tagFoo",
1090 })
1091- c.Assert(err, IsNil)
1092- c.Assert(got, Matches, OK)
1093+ c.Assert(err, IsNil, Commentf("%v", got))
1094
1095 // replace
1096 got, err = s.PostRequest("/notify", &api.Unicast{
1097@@ -226,8 +216,7 @@
1098 Data: json.RawMessage(`{"m": 2}`),
1099 ReplaceTag: "tagFoo",
1100 })
1101- c.Assert(err, IsNil)
1102- c.Assert(got, Matches, OK)
1103+ c.Assert(err, IsNil, Commentf("%v", got))
1104
1105 events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth)
1106 // getting the 1 pending on connect

Subscribers

People subscribed via source and target branches