Merge lp:~chipaca/ubuntu-push/multi-valued into lp:ubuntu-push

Proposed by John Lenton
Status: Merged
Approved by: John Lenton
Approved revision: 26
Merged at revision: 15
Proposed branch: lp:~chipaca/ubuntu-push/multi-valued
Merge into: lp:ubuntu-push
Prerequisite: lp:~chipaca/ubuntu-push/move-proxy-into-endpoint
Diff against target: 355 lines (+172/-34)
6 files modified
bus/endpoint.go (+28/-24)
bus/testing/testing_bus.go (+7/-0)
bus/testing/testing_bus_test.go (+75/-0)
bus/testing/testing_endpoint.go (+17/-6)
bus/testing/testing_endpoint_test.go (+44/-3)
networkmanager/networkmanager.go (+1/-1)
To merge this branch: bzr merge lp:~chipaca/ubuntu-push/multi-valued
Reviewer Review Type Date Requested Status
Samuele Pedroni Approve
Review via email: mp+202455@code.launchpad.net

Commit message

Made the bus support multi-valued signals (preparatory for notifications). Also added tests for TestingBus.

Description of the change

Made the bus support multi-valued signals (preparatory for notifications). Also added tests for TestingBus.

To post a comment you must log in.
lp:~chipaca/ubuntu-push/multi-valued updated
25. By John Lenton

formatting

Revision history for this message
Samuele Pedroni (pedronis) wrote :

                err = fmt.Errorf("Got wrong number of arguments in Call: %d", len(rvs))
        }
        return 0, err

shouldn't it say something like "return message arguments", reading that description I would think that the length of args was wrong,

I'm not a big fan of not doing simply:

return 0, fmt.Errorf("Got wrong number of arguments in Call: %d", len(rvs))

without falltrhough there

Revision history for this message
Samuele Pedroni (pedronis) wrote :

61 + return nil, fmt.Errorf("Got wrong number of arguments in GetProperty: %d", len(variantvs))

same unclarity here

Revision history for this message
Samuele Pedroni (pedronis) wrote :

217 // each of them intern, irrespective of whether Call has been called.

*in turn*

NewTestingEndpoint

misses docs

Revision history for this message
Samuele Pedroni (pedronis) :
review: Approve
lp:~chipaca/ubuntu-push/multi-valued updated
26. By John Lenton

addressed issues raised by pedronis during peer review

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bus/endpoint.go'
2--- bus/endpoint.go 2014-01-20 19:10:21 +0000
3+++ bus/endpoint.go 2014-01-22 10:49:04 +0000
4@@ -19,7 +19,7 @@
5 // Here we define the Endpoint, which represents the DBus connection itself.
6
7 import (
8- "errors"
9+ "fmt"
10 "launchpad.net/go-dbus/v1"
11 "launchpad.net/ubuntu-push/logger"
12 )
13@@ -30,7 +30,7 @@
14
15 // bus.Endpoint represents the DBus connection itself.
16 type Endpoint interface {
17- WatchSignal(member string, f func(interface{}), d func()) error
18+ WatchSignal(member string, f func(...interface{}), d func()) error
19 Call(member string, args ...interface{}) (interface{}, error)
20 GetProperty(property string) (interface{}, error)
21 Close()
22@@ -66,7 +66,7 @@
23 // with the unpacked value. If it's unable to set up the watch it'll return an
24 // error. If the watch fails once established, d() is called. Typically f()
25 // sends the values over a channel, and d() would close the channel.
26-func (endp *endpoint) WatchSignal(member string, f func(interface{}), d func()) error {
27+func (endp *endpoint) WatchSignal(member string, f func(...interface{}), d func()) error {
28 watch, err := endp.proxy.WatchSignal(endp.iface, member)
29 if err != nil {
30 endp.log.Debugf("Failed to set up the watch: %s", err)
31@@ -82,11 +82,19 @@
32 // provided when creating the endpoint). The return value is unpacked before
33 // being returned.
34 func (endp *endpoint) Call(member string, args ...interface{}) (interface{}, error) {
35- if msg, err := endp.proxy.Call(endp.iface, member, args...); err == nil {
36- return endp.unpackOneMsg(msg, member)
37- } else {
38+ msg, err := endp.proxy.Call(endp.iface, member, args...)
39+ if err != nil {
40 return 0, err
41 }
42+ rvs := endp.unpackOneMsg(msg, member)
43+ switch len(rvs) {
44+ default:
45+ return 0, fmt.Errorf("Too many values in %s response: %d", member, len(rvs))
46+ case 0:
47+ return 0, fmt.Errorf("Not enough values in %s response: %d", member, len(rvs))
48+ case 1:
49+ return rvs[0], nil
50+ }
51 }
52
53 // GetProperty uses the org.freedesktop.DBus.Properties interface's Get method
54@@ -98,13 +106,18 @@
55 if err != nil {
56 return nil, err
57 }
58- variantv, err := endp.unpackOneMsg(msg, property)
59- if err != nil {
60- return nil, err
61+ variantvs := endp.unpackOneMsg(msg, property)
62+ switch len(variantvs) {
63+ default:
64+ return nil, fmt.Errorf("Too many values in Properties.Get response: %d", len(variantvs))
65+ case 0:
66+ return nil, fmt.Errorf("Not enough values in Properties.Get response: %d", len(variantvs))
67+ case 1:
68+ // carry on
69 }
70- variant, ok := variantv.(*dbus.Variant)
71+ variant, ok := variantvs[0].(*dbus.Variant)
72 if !ok {
73- return nil, errors.New("Response from Properties.Get wasn't a *dbus.Variant")
74+ return nil, fmt.Errorf("Response from Properties.Get wasn't a *dbus.Variant")
75 }
76 return variant.Value, nil
77 }
78@@ -119,27 +132,18 @@
79 */
80
81 // unpackOneMsg unpacks the value from the response msg
82-func (endp *endpoint) unpackOneMsg(msg *dbus.Message, member string) (interface{}, error) {
83- var v interface{}
84- if err := msg.Args(&v); err != nil {
85- endp.log.Errorf("Decoding %s: %s", member, err)
86- return 0, err
87- } else {
88- return v, nil
89- }
90+func (endp *endpoint) unpackOneMsg(msg *dbus.Message, member string) []interface{} {
91+ return msg.AllArgs()
92 }
93
94 // unpackMessages unpacks the value from the watch
95-func (endp *endpoint) unpackMessages(watch *dbus.SignalWatch, f func(interface{}), d func(), member string) {
96+func (endp *endpoint) unpackMessages(watch *dbus.SignalWatch, f func(...interface{}), d func(), member string) {
97 for {
98 msg, ok := <-watch.C
99 if !ok {
100 break
101 }
102- if val, err := endp.unpackOneMsg(msg, member); err == nil {
103- // errors are ignored at this level
104- f(val)
105- }
106+ f(endp.unpackOneMsg(msg, member)...)
107 }
108 endp.log.Errorf("Got not-OK from %s watch", member)
109 d()
110
111=== modified file 'bus/testing/testing_bus.go'
112--- bus/testing/testing_bus.go 2014-01-20 13:45:30 +0000
113+++ bus/testing/testing_bus.go 2014-01-22 10:49:04 +0000
114@@ -43,6 +43,13 @@
115 return &testingBus{clientTC, NewTestingEndpoint(busTC, retvals...)}
116 }
117
118+// Build a bus.Bus that takes a condition to determine whether it should work,
119+// as well as a condition and a series of lists of return values for the
120+// testing bus.Endpoint it builds.
121+func NewMultiValuedTestingBus(clientTC condition.Interface, busTC condition.Interface, retvalses ...[]interface{}) bus.Bus {
122+ return &testingBus{clientTC, NewMultiValuedTestingEndpoint(busTC, retvalses...)}
123+}
124+
125 // ensure testingBus implements bus.Interface
126 var _ bus.Bus = &testingBus{}
127
128
129=== added file 'bus/testing/testing_bus_test.go'
130--- bus/testing/testing_bus_test.go 1970-01-01 00:00:00 +0000
131+++ bus/testing/testing_bus_test.go 2014-01-22 10:49:04 +0000
132@@ -0,0 +1,75 @@
133+/*
134+ Copyright 2013-2014 Canonical Ltd.
135+
136+ This program is free software: you can redistribute it and/or modify it
137+ under the terms of the GNU General Public License version 3, as published
138+ by the Free Software Foundation.
139+
140+ This program is distributed in the hope that it will be useful, but
141+ WITHOUT ANY WARRANTY; without even the implied warranties of
142+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
143+ PURPOSE. See the GNU General Public License for more details.
144+
145+ You should have received a copy of the GNU General Public License along
146+ with this program. If not, see <http://www.gnu.org/licenses/>.
147+*/
148+
149+package testing
150+
151+import (
152+ . "launchpad.net/gocheck"
153+ "launchpad.net/ubuntu-push/bus"
154+ "launchpad.net/ubuntu-push/testing/condition"
155+ "testing"
156+)
157+
158+// hook up gocheck
159+func BusTest(t *testing.T) { TestingT(t) }
160+
161+type TestingBusSuite struct{}
162+
163+var _ = Suite(&TestingBusSuite{})
164+
165+// Test Connect() on a working bus returns an endpoint that looks right
166+func (s *TestingBusSuite) TestConnectWorks(c *C) {
167+ addr := bus.Address{"", "", ""}
168+ tb := NewTestingBus(condition.Work(true), condition.Work(false), 42, 42, 42)
169+ endp, err := tb.Connect(addr, nil)
170+ c.Check(err, IsNil)
171+ c.Check(endp, FitsTypeOf, &testingEndpoint{})
172+ c.Check(endp.(*testingEndpoint).cond.OK(), Equals, false)
173+ c.Check(endp.(*testingEndpoint).retvals, HasLen, 3)
174+}
175+
176+// Test Connect() on a working "multi-valued" bus returns an endpoint that looks right
177+func (s *TestingBusSuite) TestConnectMultiValued(c *C) {
178+ addr := bus.Address{"", "", ""}
179+ tb := NewMultiValuedTestingBus(condition.Work(true), condition.Work(true),
180+ []interface{}{42, 17},
181+ []interface{}{42, 17, 13},
182+ []interface{}{42},
183+ )
184+ endpp, err := tb.Connect(addr, nil)
185+ c.Check(err, IsNil)
186+ endp, ok := endpp.(*testingEndpoint)
187+ c.Check(ok, Equals, true)
188+ c.Check(endp.cond.OK(), Equals, true)
189+ c.Assert(endp.retvals, HasLen, 3)
190+ c.Check(endp.retvals[0], HasLen, 2)
191+ c.Check(endp.retvals[1], HasLen, 3)
192+ c.Check(endp.retvals[2], HasLen, 1)
193+}
194+
195+// Test Connect() with a non-working bus fails
196+func (s *TestingBusSuite) TestConnectNoWork(c *C) {
197+ addr := bus.Address{"", "", ""}
198+ tb := NewTestingBus(condition.Work(false), condition.Work(true))
199+ _, err := tb.Connect(addr, nil)
200+ c.Check(err, NotNil)
201+}
202+
203+// Test TestingBus stringifies sanely
204+func (s *TestingBusSuite) TestStringifyBus(c *C) {
205+ tb := NewTestingBus(condition.Work(false), condition.Work(true))
206+ c.Check(tb.String(), Matches, ".*TestingBus.*")
207+}
208
209=== modified file 'bus/testing/testing_endpoint.go'
210--- bus/testing/testing_endpoint.go 2014-01-20 18:09:57 +0000
211+++ bus/testing/testing_endpoint.go 2014-01-22 10:49:04 +0000
212@@ -27,25 +27,33 @@
213
214 type testingEndpoint struct {
215 cond condition.Interface
216- retvals []interface{}
217+ retvals [][]interface{}
218 }
219
220 // Build a bus.Endpoint that calls OK() on its condition before returning
221 // the provided return values.
222 //
223 // NOTE: Call() always returns the first return value; Watch() will provide
224-// each of them intern, irrespective of whether Call has been called.
225+// each of them in turn, irrespective of whether Call has been called.
226+func NewMultiValuedTestingEndpoint(cond condition.Interface, retvalses ...[]interface{}) bus.Endpoint {
227+ return &testingEndpoint{cond, retvalses}
228+}
229+
230 func NewTestingEndpoint(cond condition.Interface, retvals ...interface{}) bus.Endpoint {
231- return &testingEndpoint{cond, retvals}
232+ retvalses := make([][]interface{}, len(retvals))
233+ for i, x := range retvals {
234+ retvalses[i] = []interface{}{x}
235+ }
236+ return &testingEndpoint{cond, retvalses}
237 }
238
239 // See Endpoint's WatchSignal. This WatchSignal will check its condition to
240 // decide whether to return an error, or provide each of its return values
241-func (tc *testingEndpoint) WatchSignal(member string, f func(interface{}), d func()) error {
242+func (tc *testingEndpoint) WatchSignal(member string, f func(...interface{}), d func()) error {
243 if tc.cond.OK() {
244 go func() {
245 for _, v := range tc.retvals {
246- f(v)
247+ f(v...)
248 time.Sleep(10 * time.Millisecond)
249 }
250 d()
251@@ -63,7 +71,10 @@
252 if len(tc.retvals) == 0 {
253 panic("No return values provided!")
254 }
255- return tc.retvals[0], nil
256+ if len(tc.retvals[0]) != 1 {
257+ panic("Wrong number of values provided -- Call only returns a single value for now!")
258+ }
259+ return tc.retvals[0][0], nil
260 } else {
261 return 0, errors.New("no way")
262 }
263
264=== modified file 'bus/testing/testing_endpoint_test.go'
265--- bus/testing/testing_endpoint_test.go 2014-01-20 18:09:57 +0000
266+++ bus/testing/testing_endpoint_test.go 2014-01-22 10:49:04 +0000
267@@ -39,6 +39,15 @@
268 c.Check(v, Equals, m)
269 }
270
271+// Test the same Call() but with multi-valued endpoint
272+func (s *TestingEndpointSuite) TestMultiValuedCall(c *C) {
273+ var m, n uint32 = 42, 17
274+ endp := NewMultiValuedTestingEndpoint(condition.Work(true), []interface{}{m}, []interface{}{n})
275+ v, e := endp.Call("what")
276+ c.Check(e, IsNil)
277+ c.Check(v, Equals, m)
278+}
279+
280 // Test that Call() with a negative condition returns an error.
281 func (s *TestingEndpointSuite) TestCallFails(c *C) {
282 endp := NewTestingEndpoint(condition.Work(false))
283@@ -50,7 +59,22 @@
284 // a helpful message.
285 func (s *TestingEndpointSuite) TestCallPanicsWithNiceMessage(c *C) {
286 endp := NewTestingEndpoint(condition.Work(true))
287- c.Check(func() { endp.Call("") }, PanicMatches, "No return values provided!")
288+ c.Check(func() { endp.Call("") }, PanicMatches, "No return values provided.*")
289+}
290+
291+// Test that Call() with a positive condition and an empty return value panics
292+// with a helpful message.
293+func (s *TestingEndpointSuite) TestCallPanicsWithNiceMessage2(c *C) {
294+ endp := NewMultiValuedTestingEndpoint(condition.Work(true), []interface{}{})
295+ c.Check(func() { endp.Call("") }, PanicMatches, "Wrong number of values provided.*")
296+}
297+
298+// Test Call() with positive condition and the wrong number of arguments also
299+// fails with a helpful message
300+func (s *TestingEndpointSuite) TestMultiValuedCallPanicsWhenWrongNumberOfValues(c *C) {
301+ var m, n uint32 = 42, 17
302+ endp := NewMultiValuedTestingEndpoint(condition.Work(true), []interface{}{m, n})
303+ c.Check(func() { endp.Call("") }, PanicMatches, "Wrong number of values provided.*")
304 }
305
306 // Test that WatchSignal() with a positive condition sends the provided return
307@@ -59,16 +83,33 @@
308 var m, n uint32 = 42, 17
309 endp := NewTestingEndpoint(condition.Work(true), m, n)
310 ch := make(chan uint32)
311- e := endp.WatchSignal("what", func(u interface{}) { ch <- u.(uint32) }, func() { close(ch) })
312+ e := endp.WatchSignal("what", func(us ...interface{}) { ch <- us[0].(uint32) }, func() { close(ch) })
313 c.Check(e, IsNil)
314 c.Check(<-ch, Equals, m)
315 c.Check(<-ch, Equals, n)
316 }
317
318+// Test that WatchSignal() calls the destructor callback when it runs out values
319+func (s *TestingEndpointSuite) TestWatchDestructor(c *C) {
320+ endp := NewTestingEndpoint(condition.Work(true))
321+ ch := make(chan uint32)
322+ e := endp.WatchSignal("what", func(us ...interface{}) {}, func() { close(ch) })
323+ c.Check(e, IsNil)
324+ _, ok := <-ch
325+ c.Check(ok, Equals, false)
326+}
327+
328+// Test the endpoint can be closed
329+func (s *TestingEndpointSuite) TestCloser(c *C) {
330+ endp := NewTestingEndpoint(condition.Work(true))
331+ endp.Close()
332+ // ... yay?
333+}
334+
335 // Test that WatchSignal() with a negative condition returns an error.
336 func (s *TestingEndpointSuite) TestWatchFails(c *C) {
337 endp := NewTestingEndpoint(condition.Work(false))
338- e := endp.WatchSignal("what", func(u interface{}) {}, func() {})
339+ e := endp.WatchSignal("what", func(us ...interface{}) {}, func() {})
340 c.Check(e, NotNil)
341 }
342
343
344=== modified file 'networkmanager/networkmanager.go'
345--- networkmanager/networkmanager.go 2014-01-20 18:09:57 +0000
346+++ networkmanager/networkmanager.go 2014-01-22 10:49:04 +0000
347@@ -74,7 +74,7 @@
348 func (nm *networkManager) WatchState() (<-chan State, error) {
349 ch := make(chan State)
350 err := nm.bus.WatchSignal("StateChanged",
351- func(n interface{}) { ch <- State(n.(uint32)) },
352+ func(ns ...interface{}) { ch <- State(ns[0].(uint32)) },
353 func() { close(ch) })
354 if err != nil {
355 nm.log.Debugf("Failed to set up the watch: %s", err)

Subscribers

People subscribed via source and target branches