Merge lp:~chipaca/ubuntu-push/simple-bus-interface into lp:ubuntu-push
- simple-bus-interface
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | John Lenton |
Approved revision: | 14 |
Merged at revision: | 10 |
Proposed branch: | lp:~chipaca/ubuntu-push/simple-bus-interface |
Merge into: | lp:ubuntu-push |
Diff against target: |
837 lines (+744/-4) 12 files modified
.bzrignore (+1/-0) Makefile (+5/-4) bus/bus.go (+87/-0) bus/bus_test.go (+67/-0) bus/endpoint.go (+121/-0) bus/endpoint_test.go (+32/-0) bus/testing/testing_bus.go (+63/-0) bus/testing/testing_endpoint.go (+76/-0) bus/testing/testing_endpoint_test.go (+73/-0) dependencies.tsv (+1/-0) testing/condition/condition.go (+142/-0) testing/condition/condition_test.go (+76/-0) |
To merge this branch: | bzr merge lp:~chipaca/ubuntu-push/simple-bus-interface |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Samuele Pedroni | Approve | ||
Review via email:
|
Commit message
A simplified (and more testable) dbus api
Description of the change
A very simple interface to dbus, probably not usable outside of what
we need to do. Aiming at being easier to test things that use it.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samuele Pedroni (pedronis) wrote : | # |
+ conn.log.
is this really a error situation? or just expected and then should be a Debugf
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samuele Pedroni (pedronis) wrote : | # |
does connection need to be its own package? calling Interface interface is a bit atypical, compare with stdlib net for example, in general wondering when the concrete type should have the less nice name and the interface should have the name of the concrete type
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samuele Pedroni (pedronis) wrote : | # |
I would have at least one more
0 + // c.Check(cond.OK(), gocheck.Equals, true)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samuele Pedroni (pedronis) wrote : | # |
you don't need the _ in _iter
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samuele Pedroni (pedronis) wrote : | # |
go doesn't care too much about order, readers a bit more,
I think typical declaration order is
type,
new function(s),
methods,
here sometimes I see
new function(s)
type
this confused me a couple of times, especially when type is something like work
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samuele Pedroni (pedronis) wrote : | # |
test suites are wrongly named sometimes, like I see
NMTSuite
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samuele Pedroni (pedronis) wrote : | # |
I see:
bus/connection/
bus/connection/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
John Lenton (chipaca) wrote : | # |
☑ GODEPS.
☑ bus type encapsulation (nice suggestion, switching to that)
☑ Deferred the close.
☐ I believe that that error is a sign of something Really Bad happening outside of this service, so I wanted to log it as early as possible and as loudly as possible.
☐ connection does not need its own package; if you can suggest a naming scheme that works for the callee, I'll switch (have tried several things already---but I do suck at naming remember) will discuss more on IRC.
☐ the _ in _iter is to signal to the reader that it is a minor detail (in a more dynamic language it wouldn't even be there). I could probably move it out into its own little file to make it invisible (like I did with networkmanager's state).
☑ I was wondering about the order. Thanks.
☑ Good catch about NMTSuite. Signs of bits I missed during refactoring (there are probably more of these).
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samuele Pedroni (pedronis) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~chipaca/ubuntu-push/simple-bus-interface into lp:ubuntu-push failed. Below is the output from the failed tests.
mkdir -p /mnt/tarmac/
mkdir -p /mnt/tarmac/
go get -u launchpad.
go get -d -u launchpad.
/mnt/tarmac/
go install launchpad.
go test launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
scripts/check_fmt launchpad.
pkg launchpad.
bus.go
endpoint.go
endpoint_test.go
pkg launchpad.
testing_bus.go
testing_endpoint.go
make: *** [check-format] Error 1
- 14. By John Lenton
-
make format
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2014-01-14 15:35:20 +0000 |
3 | +++ .bzrignore 2014-01-20 13:45:30 +0000 |
4 | @@ -1,3 +1,4 @@ |
5 | acceptanceclient |
6 | testserver |
7 | coverhtml |
8 | +coverage.out |
9 | |
10 | === modified file 'Makefile' |
11 | --- Makefile 2014-01-16 19:37:57 +0000 |
12 | +++ Makefile 2014-01-20 13:45:30 +0000 |
13 | @@ -8,6 +8,7 @@ |
14 | endif |
15 | |
16 | GODEPS = launchpad.net/gocheck |
17 | +GODEPS += launchpad.net/go-dbus/v1 |
18 | |
19 | bootstrap: |
20 | mkdir -p $(GOPATH)/bin |
21 | @@ -18,20 +19,20 @@ |
22 | go install $(GODEPS) |
23 | |
24 | check: |
25 | - go test $(PROJECT)/... |
26 | + go test $(TESTFLAGS) $(PROJECT)/... |
27 | |
28 | check-race: |
29 | - go test -race $(PROJECT)/... |
30 | + go test $(TESTFLAGS) -race $(PROJECT)/... |
31 | |
32 | coverage-summary: |
33 | - go test -a -cover $(PROJECT)/... |
34 | + go test $(TESTFLAGS) -a -cover $(PROJECT)/... |
35 | |
36 | coverage-html: |
37 | mkdir -p coverhtml |
38 | for pkg in $$(go list $(PROJECT)/...|grep -v acceptance ); do \ |
39 | relname="$${pkg#$(PROJECT)/}" ; \ |
40 | mkdir -p coverhtml/$$(dirname $${relname}) ; \ |
41 | - go test -a -coverprofile=coverhtml/$${relname}.out $$pkg ; \ |
42 | + go test $(TESTFLAGS) -a -coverprofile=coverhtml/$${relname}.out $$pkg ; \ |
43 | if [ -f coverhtml/$${relname}.out ] ; then \ |
44 | go tool cover -html=coverhtml/$${relname}.out -o coverhtml/$${relname}.html ; \ |
45 | go tool cover -func=coverhtml/$${relname}.out -o coverhtml/$${relname}.txt ; \ |
46 | |
47 | === added directory 'bus' |
48 | === added file 'bus/bus.go' |
49 | --- bus/bus.go 1970-01-01 00:00:00 +0000 |
50 | +++ bus/bus.go 2014-01-20 13:45:30 +0000 |
51 | @@ -0,0 +1,87 @@ |
52 | +/* |
53 | + Copyright 2013-2014 Canonical Ltd. |
54 | + |
55 | + This program is free software: you can redistribute it and/or modify it |
56 | + under the terms of the GNU General Public License version 3, as published |
57 | + by the Free Software Foundation. |
58 | + |
59 | + This program is distributed in the hope that it will be useful, but |
60 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
61 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
62 | + PURPOSE. See the GNU General Public License for more details. |
63 | + |
64 | + You should have received a copy of the GNU General Public License along |
65 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
66 | +*/ |
67 | + |
68 | +// Package bus provides a simplified (and more testable?) interface to DBus. |
69 | +package bus |
70 | + |
71 | +// Here we define the Bus itself |
72 | + |
73 | +import ( |
74 | + "launchpad.net/go-dbus/v1" |
75 | + "launchpad.net/ubuntu-push/logger" |
76 | +) |
77 | + |
78 | +/***************************************************************** |
79 | + * Bus (and its implementation) |
80 | + */ |
81 | + |
82 | +// This is the Bus itself. |
83 | +type Bus interface { |
84 | + String() string |
85 | + Connect(Address, logger.Logger) (Endpoint, error) |
86 | +} |
87 | + |
88 | +type concreteBus dbus.StandardBus |
89 | + |
90 | +// ensure concreteBus implements Bus |
91 | +var _ Bus = new(concreteBus) |
92 | + |
93 | +// no bus.Bus constructor, just two standard, constant, busses |
94 | +var ( |
95 | + SessionBus Bus = concreteBus(dbus.SessionBus) |
96 | + SystemBus Bus = concreteBus(dbus.SystemBus) |
97 | +) |
98 | + |
99 | +/* |
100 | + public methods |
101 | +*/ |
102 | + |
103 | +func (bus concreteBus) String() string { |
104 | + if bus == concreteBus(dbus.SystemBus) { |
105 | + return "SystemBus" |
106 | + } else { |
107 | + return "SessionBus" |
108 | + } |
109 | +} |
110 | + |
111 | +// Connect() connects to the bus, and returns the bus endpoint (and/or error). |
112 | +func (bus concreteBus) Connect(addr Address, log logger.Logger) (Endpoint, error) { |
113 | + conn, err := dbus.Connect(bus.dbusType()) |
114 | + if err != nil { |
115 | + return nil, err |
116 | + } else { |
117 | + return &endpoint{conn, addr.Name, addr.Path, addr.Interface, log}, nil |
118 | + } |
119 | +} |
120 | + |
121 | +/* |
122 | + private methods |
123 | +*/ |
124 | + |
125 | +func (bus concreteBus) dbusType() dbus.StandardBus { |
126 | + return dbus.StandardBus(bus) |
127 | +} |
128 | + |
129 | +/***************************************************************** |
130 | + * Address |
131 | + */ |
132 | + |
133 | +// bus.Address is just a bag of configuration |
134 | +type Address struct { |
135 | + Name string |
136 | + Path string |
137 | + Interface string |
138 | +} |
139 | |
140 | === added file 'bus/bus_test.go' |
141 | --- bus/bus_test.go 1970-01-01 00:00:00 +0000 |
142 | +++ bus/bus_test.go 2014-01-20 13:45:30 +0000 |
143 | @@ -0,0 +1,67 @@ |
144 | +/* |
145 | + Copyright 2013-2014 Canonical Ltd. |
146 | + |
147 | + This program is free software: you can redistribute it and/or modify it |
148 | + under the terms of the GNU General Public License version 3, as published |
149 | + by the Free Software Foundation. |
150 | + |
151 | + This program is distributed in the hope that it will be useful, but |
152 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
153 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
154 | + PURPOSE. See the GNU General Public License for more details. |
155 | + |
156 | + You should have received a copy of the GNU General Public License along |
157 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
158 | +*/ |
159 | + |
160 | +package bus |
161 | + |
162 | +import ( |
163 | + "fmt" |
164 | + "io/ioutil" |
165 | + "launchpad.net/go-dbus/v1" |
166 | + . "launchpad.net/gocheck" |
167 | + "launchpad.net/ubuntu-push/logger" |
168 | + "os" |
169 | + "testing" |
170 | +) |
171 | + |
172 | +// hook up gocheck |
173 | +func Test(t *testing.T) { TestingT(t) } |
174 | + |
175 | +type BusSuite struct{} |
176 | + |
177 | +var nullog = logger.NewSimpleLogger(ioutil.Discard, "error") |
178 | +var _ = Suite(&BusSuite{}) |
179 | + |
180 | +// Test we stringify sanely |
181 | +func (s *BusSuite) TestBusType(c *C) { |
182 | + c.Check(fmt.Sprintf("%s", SystemBus), Equals, "SystemBus") |
183 | + c.Check(fmt.Sprintf("%s", SessionBus), Equals, "SessionBus") |
184 | +} |
185 | + |
186 | +// Test we get the right DBus bus |
187 | +func (s *BusSuite) TestDBusType(c *C) { |
188 | + c.Check(SystemBus.(concreteBus).dbusType(), DeepEquals, dbus.SystemBus) |
189 | + c.Check(SessionBus.(concreteBus).dbusType(), DeepEquals, dbus.SessionBus) |
190 | +} |
191 | + |
192 | +// Tests that we can connect to the *actual* system bus. |
193 | +// XXX maybe connect to a mock/fake/etc bus? |
194 | +func (s *BusSuite) TestConnect(c *C) { |
195 | + b, err := SystemBus.Connect(Address{"", "", ""}, nullog) |
196 | + c.Assert(err, IsNil) |
197 | + defer b.Close() |
198 | +} |
199 | + |
200 | +// Test that if we try to connect to the session bus when no session |
201 | +// bus is available, we get a reasonable result (i.e., an error). |
202 | +func (s *BusSuite) TestConnectCanFail(c *C) { |
203 | + db := "DBUS_SESSION_BUS_ADDRESS" |
204 | + odb := os.Getenv(db) |
205 | + defer os.Setenv(db, odb) |
206 | + os.Setenv(db, "") |
207 | + |
208 | + _, err := SessionBus.Connect(Address{"", "", ""}, nullog) |
209 | + c.Check(err, NotNil) |
210 | +} |
211 | |
212 | === added file 'bus/endpoint.go' |
213 | --- bus/endpoint.go 1970-01-01 00:00:00 +0000 |
214 | +++ bus/endpoint.go 2014-01-20 13:45:30 +0000 |
215 | @@ -0,0 +1,121 @@ |
216 | +/* |
217 | + Copyright 2013-2014 Canonical Ltd. |
218 | + |
219 | + This program is free software: you can redistribute it and/or modify it |
220 | + under the terms of the GNU General Public License version 3, as published |
221 | + by the Free Software Foundation. |
222 | + |
223 | + This program is distributed in the hope that it will be useful, but |
224 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
225 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
226 | + PURPOSE. See the GNU General Public License for more details. |
227 | + |
228 | + You should have received a copy of the GNU General Public License along |
229 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
230 | +*/ |
231 | + |
232 | +package bus |
233 | + |
234 | +// Here we define the Endpoint, which represents the DBus connection itself. |
235 | + |
236 | +import ( |
237 | + "launchpad.net/go-dbus/v1" |
238 | + "launchpad.net/ubuntu-push/logger" |
239 | +) |
240 | + |
241 | +/***************************************************************** |
242 | + * Endpoint (and its implementation) |
243 | + */ |
244 | + |
245 | +// bus.Endpoint represents the DBus connection itself. |
246 | +type Endpoint interface { |
247 | + WatchSignal(member string, f func(interface{}), d func()) error |
248 | + Call(member string, args ...interface{}) (interface{}, error) |
249 | + Close() |
250 | +} |
251 | + |
252 | +type endpoint struct { |
253 | + bus *dbus.Connection |
254 | + name string |
255 | + path string |
256 | + iface string |
257 | + log logger.Logger |
258 | +} |
259 | + |
260 | +// ensure endpoint implements Endpoint |
261 | +var _ Endpoint = endpoint{} |
262 | + |
263 | +/* |
264 | + public methods |
265 | +*/ |
266 | + |
267 | +// WatchSignal() takes a member name and sets up a watch for it (on the name, |
268 | +// path and interface provided when creating the endpoint), and then calls f() |
269 | +// with the unpacked value. If it's unable to set up the watch it'll return an |
270 | +// error. If the watch fails once established, d() is called. Typically f() |
271 | +// sends the values over a channel, and d() would close the channel. |
272 | +func (endp endpoint) WatchSignal(member string, f func(interface{}), d func()) error { |
273 | + watch, err := endp.bus.WatchSignal(&dbus.MatchRule{ |
274 | + Type: dbus.TypeSignal, |
275 | + Sender: endp.name, |
276 | + Path: dbus.ObjectPath(endp.path), |
277 | + Interface: endp.iface, |
278 | + Member: member, |
279 | + }) |
280 | + if err != nil { |
281 | + endp.log.Debugf("Failed to set up the watch: %s", err) |
282 | + return err |
283 | + } |
284 | + |
285 | + go endp.unpackMessages(watch, f, d, member) |
286 | + |
287 | + return nil |
288 | +} |
289 | + |
290 | +// Call() invokes the provided member method (on the name, path and interface |
291 | +// provided when creating the endpoint). The return value is unpacked before |
292 | +// being returned. |
293 | +func (endp endpoint) Call(member string, args ...interface{}) (interface{}, error) { |
294 | + proxy := endp.bus.Object(endp.name, dbus.ObjectPath(endp.path)) |
295 | + if msg, err := proxy.Call(endp.iface, member, args...); err == nil { |
296 | + return endp.unpackOneMsg(msg, member) |
297 | + } else { |
298 | + return 0, err |
299 | + } |
300 | +} |
301 | + |
302 | +// Close the connection to dbus. |
303 | +func (endp endpoint) Close() { |
304 | + endp.bus.Close() |
305 | +} |
306 | + |
307 | +/* |
308 | + private methods |
309 | +*/ |
310 | + |
311 | +// unpackOneMsg unpacks the value from the response msg |
312 | +func (endp endpoint) unpackOneMsg(msg *dbus.Message, member string) (interface{}, error) { |
313 | + var v interface{} |
314 | + if err := msg.Args(&v); err != nil { |
315 | + endp.log.Errorf("Decoding %s: %s", member, err) |
316 | + return 0, err |
317 | + } else { |
318 | + return v, nil |
319 | + } |
320 | +} |
321 | + |
322 | +// unpackMessages unpacks the value from the watch |
323 | +func (endp endpoint) unpackMessages(watch *dbus.SignalWatch, f func(interface{}), d func(), member string) { |
324 | + for { |
325 | + msg, ok := <-watch.C |
326 | + if !ok { |
327 | + break |
328 | + } |
329 | + if val, err := endp.unpackOneMsg(msg, member); err == nil { |
330 | + // errors are ignored at this level |
331 | + f(val) |
332 | + } |
333 | + } |
334 | + endp.log.Errorf("Got not-OK from %s watch", member) |
335 | + d() |
336 | +} |
337 | |
338 | === added file 'bus/endpoint_test.go' |
339 | --- bus/endpoint_test.go 1970-01-01 00:00:00 +0000 |
340 | +++ bus/endpoint_test.go 2014-01-20 13:45:30 +0000 |
341 | @@ -0,0 +1,32 @@ |
342 | +/* |
343 | + Copyright 2013-2014 Canonical Ltd. |
344 | + |
345 | + This program is free software: you can redistribute it and/or modify it |
346 | + under the terms of the GNU General Public License version 3, as published |
347 | + by the Free Software Foundation. |
348 | + |
349 | + This program is distributed in the hope that it will be useful, but |
350 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
351 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
352 | + PURPOSE. See the GNU General Public License for more details. |
353 | + |
354 | + You should have received a copy of the GNU General Public License along |
355 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
356 | +*/ |
357 | + |
358 | +package bus |
359 | + |
360 | +import ( |
361 | + . "launchpad.net/gocheck" |
362 | + "testing" |
363 | +) |
364 | + |
365 | +// hook up gocheck |
366 | +func EndpointTest(t *testing.T) { TestingT(t) } |
367 | + |
368 | +type EndpointSuite struct{} |
369 | + |
370 | +var _ = Suite(&EndpointSuite{}) |
371 | + |
372 | +// TODO: this is going to remain empty until go-dbus grows some |
373 | +// testing amenities (already talked about it with jamesh) |
374 | |
375 | === added directory 'bus/testing' |
376 | === added file 'bus/testing/testing_bus.go' |
377 | --- bus/testing/testing_bus.go 1970-01-01 00:00:00 +0000 |
378 | +++ bus/testing/testing_bus.go 2014-01-20 13:45:30 +0000 |
379 | @@ -0,0 +1,63 @@ |
380 | +/* |
381 | + Copyright 2013-2014 Canonical Ltd. |
382 | + |
383 | + This program is free software: you can redistribute it and/or modify it |
384 | + under the terms of the GNU General Public License version 3, as published |
385 | + by the Free Software Foundation. |
386 | + |
387 | + This program is distributed in the hope that it will be useful, but |
388 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
389 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
390 | + PURPOSE. See the GNU General Public License for more details. |
391 | + |
392 | + You should have received a copy of the GNU General Public License along |
393 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
394 | +*/ |
395 | + |
396 | +// Package bus/testing provides an implementation of bus.Bus and bus.Endpoint |
397 | +// suitable for testing. |
398 | +package testing |
399 | + |
400 | +// Here, the bus.Bus implementation. |
401 | + |
402 | +import ( |
403 | + "errors" |
404 | + "launchpad.net/ubuntu-push/bus" |
405 | + "launchpad.net/ubuntu-push/logger" |
406 | + "launchpad.net/ubuntu-push/testing/condition" |
407 | +) |
408 | + |
409 | +/***************************************************************** |
410 | + * TestingBus |
411 | + */ |
412 | + |
413 | +type testingBus struct { |
414 | + TestCond condition.Interface |
415 | + TestEndp *testingEndpoint |
416 | +} |
417 | + |
418 | +// Build a bus.Bus that takes a condition to determine whether it should work, |
419 | +// as well as a condition and series of return values for the testing |
420 | +// bus.Endpoint it builds. |
421 | +func NewTestingBus(clientTC condition.Interface, busTC condition.Interface, retvals ...interface{}) *testingBus { |
422 | + return &testingBus{clientTC, NewTestingEndpoint(busTC, retvals...)} |
423 | +} |
424 | + |
425 | +// ensure testingBus implements bus.Interface |
426 | +var _ bus.Bus = &testingBus{} |
427 | + |
428 | +/* |
429 | + public methods |
430 | +*/ |
431 | + |
432 | +func (tb *testingBus) Connect(info bus.Address, log logger.Logger) (bus.Endpoint, error) { |
433 | + if tb.TestCond.OK() { |
434 | + return tb.TestEndp, nil |
435 | + } else { |
436 | + return nil, errors.New(tb.TestCond.String()) |
437 | + } |
438 | +} |
439 | + |
440 | +func (tb *testingBus) String() string { |
441 | + return "<TestingBus>" |
442 | +} |
443 | |
444 | === added file 'bus/testing/testing_endpoint.go' |
445 | --- bus/testing/testing_endpoint.go 1970-01-01 00:00:00 +0000 |
446 | +++ bus/testing/testing_endpoint.go 2014-01-20 13:45:30 +0000 |
447 | @@ -0,0 +1,76 @@ |
448 | +/* |
449 | + Copyright 2013-2014 Canonical Ltd. |
450 | + |
451 | + This program is free software: you can redistribute it and/or modify it |
452 | + under the terms of the GNU General Public License version 3, as published |
453 | + by the Free Software Foundation. |
454 | + |
455 | + This program is distributed in the hope that it will be useful, but |
456 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
457 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
458 | + PURPOSE. See the GNU General Public License for more details. |
459 | + |
460 | + You should have received a copy of the GNU General Public License along |
461 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
462 | +*/ |
463 | + |
464 | +package testing |
465 | + |
466 | +// Here, the bus.Endpoint implementation. |
467 | + |
468 | +import ( |
469 | + "errors" |
470 | + "launchpad.net/ubuntu-push/bus" |
471 | + "launchpad.net/ubuntu-push/testing/condition" |
472 | + "time" |
473 | +) |
474 | + |
475 | +type testingEndpoint struct { |
476 | + cond condition.Interface |
477 | + retvals []interface{} |
478 | +} |
479 | + |
480 | +// Build a bus.Endpoint that calls OK() on its condition before returning |
481 | +// the provided return values. |
482 | +// |
483 | +// NOTE: Call() always returns the first return value; Watch() will provide |
484 | +// each of them intern, irrespective of whether Call has been called. |
485 | +func NewTestingEndpoint(cond condition.Interface, retvals ...interface{}) *testingEndpoint { |
486 | + return &testingEndpoint{cond, retvals} |
487 | +} |
488 | + |
489 | +// See Endpoint's WatchSignal. This WatchSignal will check its condition to |
490 | +// decide whether to return an error, or provide each of its return values |
491 | +func (tc *testingEndpoint) WatchSignal(member string, f func(interface{}), d func()) error { |
492 | + if tc.cond.OK() { |
493 | + go func() { |
494 | + for _, v := range tc.retvals { |
495 | + f(v) |
496 | + time.Sleep(10 * time.Millisecond) |
497 | + } |
498 | + d() |
499 | + }() |
500 | + return nil |
501 | + } else { |
502 | + return errors.New("no way") |
503 | + } |
504 | +} |
505 | + |
506 | +// See Endpoint's Call. This Call will check its condition to decide whether |
507 | +// to return an error, or the first of its return values |
508 | +func (tc *testingEndpoint) Call(member string, args ...interface{}) (interface{}, error) { |
509 | + if tc.cond.OK() { |
510 | + if len(tc.retvals) == 0 { |
511 | + panic("No return values provided!") |
512 | + } |
513 | + return tc.retvals[0], nil |
514 | + } else { |
515 | + return 0, errors.New("no way") |
516 | + } |
517 | +} |
518 | + |
519 | +// see Endpoint's Close. This one does nothing. |
520 | +func (tc *testingEndpoint) Close() {} |
521 | + |
522 | +// ensure testingEndpoint implements bus.Endpoint |
523 | +var _ bus.Endpoint = &testingEndpoint{} |
524 | |
525 | === added file 'bus/testing/testing_endpoint_test.go' |
526 | --- bus/testing/testing_endpoint_test.go 1970-01-01 00:00:00 +0000 |
527 | +++ bus/testing/testing_endpoint_test.go 2014-01-20 13:45:30 +0000 |
528 | @@ -0,0 +1,73 @@ |
529 | +/* |
530 | + Copyright 2013-2014 Canonical Ltd. |
531 | + |
532 | + This program is free software: you can redistribute it and/or modify it |
533 | + under the terms of the GNU General Public License version 3, as published |
534 | + by the Free Software Foundation. |
535 | + |
536 | + This program is distributed in the hope that it will be useful, but |
537 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
538 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
539 | + PURPOSE. See the GNU General Public License for more details. |
540 | + |
541 | + You should have received a copy of the GNU General Public License along |
542 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
543 | +*/ |
544 | + |
545 | +package testing |
546 | + |
547 | +import ( |
548 | + . "launchpad.net/gocheck" |
549 | + "launchpad.net/ubuntu-push/testing/condition" |
550 | + "testing" |
551 | +) |
552 | + |
553 | +// hook up gocheck |
554 | +func Test(t *testing.T) { TestingT(t) } |
555 | + |
556 | +type TestingEndpointSuite struct{} |
557 | + |
558 | +var _ = Suite(&TestingEndpointSuite{}) |
559 | + |
560 | +// Test that Call() with a positive condition returns the first return value |
561 | +// provided, as advertised. |
562 | +func (s *TestingEndpointSuite) TestCallReturnsFirstRetval(c *C) { |
563 | + var m, n uint32 = 42, 17 |
564 | + endp := NewTestingEndpoint(condition.Work(true), m, n) |
565 | + v, e := endp.Call("what") |
566 | + c.Check(e, IsNil) |
567 | + c.Check(v, Equals, m) |
568 | +} |
569 | + |
570 | +// Test that Call() with a negative condition returns an error. |
571 | +func (s *TestingEndpointSuite) TestCallFails(c *C) { |
572 | + endp := NewTestingEndpoint(condition.Work(false)) |
573 | + _, e := endp.Call("what") |
574 | + c.Check(e, NotNil) |
575 | +} |
576 | + |
577 | +// Test that Call() with a positive condition and no return values panics with |
578 | +// a helpful message. |
579 | +func (s *TestingEndpointSuite) TestCallPanicsWithNiceMessage(c *C) { |
580 | + endp := NewTestingEndpoint(condition.Work(true)) |
581 | + c.Check(func() { endp.Call("") }, PanicMatches, "No return values provided!") |
582 | +} |
583 | + |
584 | +// Test that WatchSignal() with a positive condition sends the provided return |
585 | +// values over the channel. |
586 | +func (s *TestingEndpointSuite) TestWatch(c *C) { |
587 | + var m, n uint32 = 42, 17 |
588 | + endp := NewTestingEndpoint(condition.Work(true), m, n) |
589 | + ch := make(chan uint32) |
590 | + e := endp.WatchSignal("what", func(u interface{}) { ch <- u.(uint32) }, func() { close(ch) }) |
591 | + c.Check(e, IsNil) |
592 | + c.Check(<-ch, Equals, m) |
593 | + c.Check(<-ch, Equals, n) |
594 | +} |
595 | + |
596 | +// Test that WatchSignal() with a negative condition returns an error. |
597 | +func (s *TestingEndpointSuite) TestWatchFails(c *C) { |
598 | + endp := NewTestingEndpoint(condition.Work(false)) |
599 | + e := endp.WatchSignal("what", func(u interface{}) {}, func() {}) |
600 | + c.Check(e, NotNil) |
601 | +} |
602 | |
603 | === modified file 'dependencies.tsv' |
604 | --- dependencies.tsv 2014-01-14 15:35:20 +0000 |
605 | +++ dependencies.tsv 2014-01-20 13:45:30 +0000 |
606 | @@ -1,1 +1,2 @@ |
607 | +launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140117100040-mmhdtiz5w9pxqtcr 124 |
608 | launchpad.net/gocheck bzr gustavo@niemeyer.net-20130302024745-6ikofwq2c03h7giu 85 |
609 | |
610 | === added directory 'testing/condition' |
611 | === added file 'testing/condition/condition.go' |
612 | --- testing/condition/condition.go 1970-01-01 00:00:00 +0000 |
613 | +++ testing/condition/condition.go 2014-01-20 13:45:30 +0000 |
614 | @@ -0,0 +1,142 @@ |
615 | +/* |
616 | + Copyright 2013-2014 Canonical Ltd. |
617 | + |
618 | + This program is free software: you can redistribute it and/or modify it |
619 | + under the terms of the GNU General Public License version 3, as published |
620 | + by the Free Software Foundation. |
621 | + |
622 | + This program is distributed in the hope that it will be useful, but |
623 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
624 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
625 | + PURPOSE. See the GNU General Public License for more details. |
626 | + |
627 | + You should have received a copy of the GNU General Public License along |
628 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
629 | +*/ |
630 | + |
631 | +// Package testing/condition implements a strategy family for use in testing. |
632 | +package condition |
633 | + |
634 | +import ( |
635 | + "fmt" |
636 | + "strings" |
637 | +) |
638 | + |
639 | +type Interface interface { |
640 | + OK() bool |
641 | + String() string |
642 | +} |
643 | + |
644 | +// Work is a simple boolean condition; either it works all the time |
645 | +// (when true), or it fails all the time (when false). |
646 | +func Work(wk bool) work { |
647 | + return work(wk) |
648 | +} |
649 | + |
650 | +type work bool |
651 | + |
652 | +func (c work) OK() bool { |
653 | + if c { |
654 | + return true |
655 | + } else { |
656 | + return false |
657 | + } |
658 | +} |
659 | +func (c work) String() string { |
660 | + if c { |
661 | + return "Always Working." |
662 | + } else { |
663 | + return "Never Working." |
664 | + } |
665 | +} |
666 | + |
667 | +var _ Interface = work(false) |
668 | + |
669 | +// Fail2Work fails for the first n times its OK() method is checked, |
670 | +// and then mysteriously starts working. |
671 | +func Fail2Work(left int32) *fail2Work { |
672 | + c := new(fail2Work) |
673 | + c.Left = left |
674 | + return c |
675 | +} |
676 | + |
677 | +type fail2Work struct { |
678 | + Left int32 |
679 | +} |
680 | + |
681 | +func (c *fail2Work) OK() bool { |
682 | + if c.Left > 0 { |
683 | + c.Left-- |
684 | + return false |
685 | + } else { |
686 | + return true |
687 | + } |
688 | +} |
689 | + |
690 | +func (c *fail2Work) String() string { |
691 | + if c.Left > 0 { |
692 | + return fmt.Sprintf("Still Broken, %d to go.", c.Left) |
693 | + } else { |
694 | + return "Working." |
695 | + } |
696 | +} |
697 | + |
698 | +var _ Interface = &fail2Work{} |
699 | + |
700 | +// Not builds a condition that negates the one passed in. |
701 | +func Not(sub Interface) *not { |
702 | + return ¬{sub} |
703 | +} |
704 | + |
705 | +type not struct{ sub Interface } |
706 | + |
707 | +func (c *not) OK() bool { return !c.sub.OK() } |
708 | +func (c *not) String() string { return fmt.Sprintf("Not %s", c.sub) } |
709 | + |
710 | +var _ Interface = ¬{} |
711 | + |
712 | +type _iter struct { |
713 | + cond Interface |
714 | + remaining int |
715 | +} |
716 | + |
717 | +func (i _iter) String() string { return fmt.Sprintf("%d of %s", i.remaining, i.cond) } |
718 | + |
719 | +type chain struct { |
720 | + subs []*_iter |
721 | +} |
722 | + |
723 | +func (c *chain) OK() bool { |
724 | + var sub *_iter |
725 | + for _, sub = range c.subs { |
726 | + if sub.remaining > 0 { |
727 | + sub.remaining-- |
728 | + return sub.cond.OK() |
729 | + } |
730 | + } |
731 | + return sub.cond.OK() |
732 | +} |
733 | + |
734 | +func (c *chain) String() string { |
735 | + ss := make([]string, len(c.subs)) |
736 | + for i, sub := range c.subs { |
737 | + ss[i] = sub.String() |
738 | + } |
739 | + return strings.Join(ss, " Then: ") |
740 | +} |
741 | + |
742 | +var _ Interface = new(chain) |
743 | + |
744 | +// Chain(n1, cond1, n2, cond2, ...) returns cond1.OK() the first n1 |
745 | +// times OK() is called, cond2.OK() the following n2 times, etc. |
746 | +func Chain(args ...interface{}) *chain { |
747 | + iters := make([]*_iter, 0, len(args)/2) |
748 | + for len(args) > 1 { |
749 | + rem := args[0].(int) |
750 | + sub := args[1].(Interface) |
751 | + iters = append(iters, &_iter{sub, rem}) |
752 | + args = args[2:] |
753 | + } |
754 | + |
755 | + return &chain{iters} |
756 | +} |
757 | |
758 | === added file 'testing/condition/condition_test.go' |
759 | --- testing/condition/condition_test.go 1970-01-01 00:00:00 +0000 |
760 | +++ testing/condition/condition_test.go 2014-01-20 13:45:30 +0000 |
761 | @@ -0,0 +1,76 @@ |
762 | +/* |
763 | + Copyright 2013-2014 Canonical Ltd. |
764 | + |
765 | + This program is free software: you can redistribute it and/or modify it |
766 | + under the terms of the GNU General Public License version 3, as published |
767 | + by the Free Software Foundation. |
768 | + |
769 | + This program is distributed in the hope that it will be useful, but |
770 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
771 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
772 | + PURPOSE. See the GNU General Public License for more details. |
773 | + |
774 | + You should have received a copy of the GNU General Public License along |
775 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
776 | +*/ |
777 | + |
778 | +package condition |
779 | + |
780 | +import ( |
781 | + "launchpad.net/gocheck" // not into . because we have our own Not |
782 | + "testing" |
783 | +) |
784 | + |
785 | +// hook up gocheck |
786 | +func Test(t *testing.T) { gocheck.TestingT(t) } |
787 | + |
788 | +type CondSuite struct{} |
789 | + |
790 | +var _ = gocheck.Suite(&CondSuite{}) |
791 | + |
792 | +func (s *CondSuite) TestConditionWorkTrue(c *gocheck.C) { |
793 | + cond := Work(true) |
794 | + c.Check(cond.OK(), gocheck.Equals, true) |
795 | + c.Check(cond.String(), gocheck.Equals, "Always Working.") |
796 | +} |
797 | + |
798 | +func (s *CondSuite) TestConditionWorkFalse(c *gocheck.C) { |
799 | + cond := Work(false) |
800 | + c.Check(cond.OK(), gocheck.Equals, false) |
801 | + c.Check(cond.String(), gocheck.Equals, "Never Working.") |
802 | +} |
803 | + |
804 | +func (s *CondSuite) TestConditionFail2Work(c *gocheck.C) { |
805 | + cond := Fail2Work(2) |
806 | + c.Check(cond.String(), gocheck.Equals, "Still Broken, 2 to go.") |
807 | + c.Check(cond.OK(), gocheck.Equals, false) |
808 | + c.Check(cond.String(), gocheck.Equals, "Still Broken, 1 to go.") |
809 | + c.Check(cond.OK(), gocheck.Equals, false) |
810 | + c.Check(cond.String(), gocheck.Equals, "Working.") |
811 | + c.Check(cond.OK(), gocheck.Equals, true) |
812 | + c.Check(cond.String(), gocheck.Equals, "Working.") |
813 | + c.Check(cond.OK(), gocheck.Equals, true) |
814 | + c.Check(cond.String(), gocheck.Equals, "Working.") |
815 | +} |
816 | + |
817 | +func (s *CondSuite) TestConditionNot(c *gocheck.C) { |
818 | + cond := Not(Fail2Work(1)) |
819 | + c.Check(cond.String(), gocheck.Equals, "Not Still Broken, 1 to go.") |
820 | + c.Check(cond.OK(), gocheck.Equals, true) |
821 | + c.Check(cond.String(), gocheck.Equals, "Not Working.") |
822 | + c.Check(cond.OK(), gocheck.Equals, false) |
823 | +} |
824 | + |
825 | +func (s *CondSuite) TestConditionChain(c *gocheck.C) { |
826 | + cond := Chain(2, Work(true), 3, Work(false), 0, Work(true)) |
827 | + c.Check(cond.String(), gocheck.Equals, "2 of Always Working. Then: 3 of Never Working. Then: 0 of Always Working.") |
828 | + c.Check(cond.OK(), gocheck.Equals, true) |
829 | + c.Check(cond.OK(), gocheck.Equals, true) |
830 | + c.Check(cond.OK(), gocheck.Equals, false) |
831 | + c.Check(cond.OK(), gocheck.Equals, false) |
832 | + c.Check(cond.OK(), gocheck.Equals, false) |
833 | + c.Check(cond.OK(), gocheck.Equals, true) |
834 | + // c.Check(cond.OK(), gocheck.Equals, true) |
835 | + // c.Check(cond.OK(), gocheck.Equals, true) |
836 | + // c.Check(cond.OK(), gocheck.Equals, true) |
837 | +} |
this is needed (godeps doesn't grab things just fixes revnos):
-GODEPS = launchpad. net/gocheck net/gocheck launchpad. net/go- dbus/v1
+GODEPS = launchpad.
wondering if
type Bus dbs.StandardBus
wouldn't be a more natural encapsulation?
TestConnect needs to defer .Close