1package dbus
2
3import (
4	"encoding/binary"
5	"io"
6	"io/ioutil"
7	"testing"
8	"time"
9)
10
11func TestSessionBus(t *testing.T) {
12	_, err := SessionBus()
13	if err != nil {
14		t.Error(err)
15	}
16}
17
18func TestSystemBus(t *testing.T) {
19	_, err := SystemBus()
20	if err != nil {
21		t.Error(err)
22	}
23}
24
25func ExampleSystemBusPrivate() {
26	setupPrivateSystemBus := func() (conn *Conn, err error) {
27		conn, err = SystemBusPrivate()
28		if err != nil {
29			return nil, err
30		}
31		if err = conn.Auth(nil); err != nil {
32			conn.Close()
33			conn = nil
34			return
35		}
36		if err = conn.Hello(); err != nil {
37			conn.Close()
38			conn = nil
39		}
40		return conn, nil // success
41	}
42	_, _ = setupPrivateSystemBus()
43}
44
45func TestSend(t *testing.T) {
46	bus, err := SessionBus()
47	if err != nil {
48		t.Fatal(err)
49	}
50	ch := make(chan *Call, 1)
51	msg := &Message{
52		Type:  TypeMethodCall,
53		Flags: 0,
54		Headers: map[HeaderField]Variant{
55			FieldDestination: MakeVariant(bus.Names()[0]),
56			FieldPath:        MakeVariant(ObjectPath("/org/freedesktop/DBus")),
57			FieldInterface:   MakeVariant("org.freedesktop.DBus.Peer"),
58			FieldMember:      MakeVariant("Ping"),
59		},
60	}
61	call := bus.Send(msg, ch)
62	<-ch
63	if call.Err != nil {
64		t.Error(call.Err)
65	}
66}
67
68func TestFlagNoReplyExpectedSend(t *testing.T) {
69	bus, err := SessionBus()
70	if err != nil {
71		t.Fatal(err)
72	}
73	done := make(chan struct{})
74	go func() {
75		bus.BusObject().Call("org.freedesktop.DBus.ListNames", FlagNoReplyExpected)
76		close(done)
77	}()
78	select {
79	case <-done:
80	case <-time.After(1 * time.Second):
81		t.Error("Failed to announce that the call was done")
82	}
83}
84
85func TestRemoveSignal(t *testing.T) {
86	bus, err := NewConn(nil)
87	if err != nil {
88		t.Error(err)
89	}
90	signals := bus.signalHandler.(*defaultSignalHandler).signals
91	ch := make(chan *Signal)
92	ch2 := make(chan *Signal)
93	for _, ch := range []chan *Signal{ch, ch2, ch, ch2, ch2, ch} {
94		bus.Signal(ch)
95	}
96	signals = bus.signalHandler.(*defaultSignalHandler).signals
97	if len(signals) != 6 {
98		t.Errorf("remove signal: signals length not equal: got '%d', want '6'", len(signals))
99	}
100	bus.RemoveSignal(ch)
101	signals = bus.signalHandler.(*defaultSignalHandler).signals
102	if len(signals) != 3 {
103		t.Errorf("remove signal: signals length not equal: got '%d', want '3'", len(signals))
104	}
105	signals = bus.signalHandler.(*defaultSignalHandler).signals
106	for _, bch := range signals {
107		if bch != ch2 {
108			t.Errorf("remove signal: removed signal present: got '%v', want '%v'", bch, ch2)
109		}
110	}
111}
112
113type rwc struct {
114	io.Reader
115	io.Writer
116}
117
118func (rwc) Close() error { return nil }
119
120type fakeAuth struct {
121}
122
123func (fakeAuth) FirstData() (name, resp []byte, status AuthStatus) {
124	return []byte("name"), []byte("resp"), AuthOk
125}
126
127func (fakeAuth) HandleData(data []byte) (resp []byte, status AuthStatus) {
128	return nil, AuthOk
129}
130
131func TestCloseBeforeSignal(t *testing.T) {
132	reader, pipewriter := io.Pipe()
133	defer pipewriter.Close()
134	defer reader.Close()
135
136	bus, err := NewConn(rwc{Reader: reader, Writer: ioutil.Discard})
137	if err != nil {
138		t.Fatal(err)
139	}
140	// give ch a buffer so sends won't block
141	ch := make(chan *Signal, 1)
142	bus.Signal(ch)
143
144	go func() {
145		_, err := pipewriter.Write([]byte("REJECTED name\r\nOK myuuid\r\n"))
146		if err != nil {
147			t.Errorf("error writing to pipe: %v", err)
148		}
149	}()
150
151	err = bus.Auth([]Auth{fakeAuth{}})
152	if err != nil {
153		t.Fatal(err)
154	}
155
156	err = bus.Close()
157	if err != nil {
158		t.Fatal(err)
159	}
160
161	msg := &Message{
162		Type: TypeSignal,
163		Headers: map[HeaderField]Variant{
164			FieldInterface: MakeVariant("foo.bar"),
165			FieldMember:    MakeVariant("bar"),
166			FieldPath:      MakeVariant(ObjectPath("/baz")),
167		},
168	}
169	err = msg.EncodeTo(pipewriter, binary.LittleEndian)
170	if err != nil {
171		t.Fatal(err)
172	}
173}
174
175type server struct{}
176
177func (server) Double(i int64) (int64, *Error) {
178	return 2 * i, nil
179}
180
181func BenchmarkCall(b *testing.B) {
182	b.StopTimer()
183	b.ReportAllocs()
184	var s string
185	bus, err := SessionBus()
186	if err != nil {
187		b.Fatal(err)
188	}
189	name := bus.Names()[0]
190	obj := bus.BusObject()
191	b.StartTimer()
192	for i := 0; i < b.N; i++ {
193		err := obj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&s)
194		if err != nil {
195			b.Fatal(err)
196		}
197		if s != name {
198			b.Errorf("got %s, wanted %s", s, name)
199		}
200	}
201}
202
203func BenchmarkCallAsync(b *testing.B) {
204	b.StopTimer()
205	b.ReportAllocs()
206	bus, err := SessionBus()
207	if err != nil {
208		b.Fatal(err)
209	}
210	name := bus.Names()[0]
211	obj := bus.BusObject()
212	c := make(chan *Call, 50)
213	done := make(chan struct{})
214	go func() {
215		for i := 0; i < b.N; i++ {
216			v := <-c
217			if v.Err != nil {
218				b.Error(v.Err)
219			}
220			s := v.Body[0].(string)
221			if s != name {
222				b.Errorf("got %s, wanted %s", s, name)
223			}
224		}
225		close(done)
226	}()
227	b.StartTimer()
228	for i := 0; i < b.N; i++ {
229		obj.Go("org.freedesktop.DBus.GetNameOwner", 0, c, name)
230	}
231	<-done
232}
233
234func BenchmarkServe(b *testing.B) {
235	b.StopTimer()
236	srv, err := SessionBus()
237	if err != nil {
238		b.Fatal(err)
239	}
240	cli, err := SessionBusPrivate()
241	if err != nil {
242		b.Fatal(err)
243	}
244	if err = cli.Auth(nil); err != nil {
245		b.Fatal(err)
246	}
247	if err = cli.Hello(); err != nil {
248		b.Fatal(err)
249	}
250	benchmarkServe(b, srv, cli)
251}
252
253func BenchmarkServeAsync(b *testing.B) {
254	b.StopTimer()
255	srv, err := SessionBus()
256	if err != nil {
257		b.Fatal(err)
258	}
259	cli, err := SessionBusPrivate()
260	if err != nil {
261		b.Fatal(err)
262	}
263	if err = cli.Auth(nil); err != nil {
264		b.Fatal(err)
265	}
266	if err = cli.Hello(); err != nil {
267		b.Fatal(err)
268	}
269	benchmarkServeAsync(b, srv, cli)
270}
271
272func BenchmarkServeSameConn(b *testing.B) {
273	b.StopTimer()
274	bus, err := SessionBus()
275	if err != nil {
276		b.Fatal(err)
277	}
278
279	benchmarkServe(b, bus, bus)
280}
281
282func BenchmarkServeSameConnAsync(b *testing.B) {
283	b.StopTimer()
284	bus, err := SessionBus()
285	if err != nil {
286		b.Fatal(err)
287	}
288
289	benchmarkServeAsync(b, bus, bus)
290}
291
292func benchmarkServe(b *testing.B, srv, cli *Conn) {
293	var r int64
294	var err error
295	dest := srv.Names()[0]
296	srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
297	obj := cli.Object(dest, "/org/guelfey/DBus/Test")
298	b.StartTimer()
299	for i := 0; i < b.N; i++ {
300		err = obj.Call("org.guelfey.DBus.Test.Double", 0, int64(i)).Store(&r)
301		if err != nil {
302			b.Fatal(err)
303		}
304		if r != 2*int64(i) {
305			b.Errorf("got %d, wanted %d", r, 2*int64(i))
306		}
307	}
308}
309
310func benchmarkServeAsync(b *testing.B, srv, cli *Conn) {
311	dest := srv.Names()[0]
312	srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
313	obj := cli.Object(dest, "/org/guelfey/DBus/Test")
314	c := make(chan *Call, 50)
315	done := make(chan struct{})
316	go func() {
317		for i := 0; i < b.N; i++ {
318			v := <-c
319			if v.Err != nil {
320				b.Fatal(v.Err)
321			}
322			i, r := v.Args[0].(int64), v.Body[0].(int64)
323			if 2*i != r {
324				b.Errorf("got %d, wanted %d", r, 2*i)
325			}
326		}
327		close(done)
328	}()
329	b.StartTimer()
330	for i := 0; i < b.N; i++ {
331		obj.Go("org.guelfey.DBus.Test.Double", 0, c, int64(i))
332	}
333	<-done
334}
335
336func TestGetKey(t *testing.T) {
337	keys := "host=1.2.3.4,port=5678,family=ipv4"
338	if host := getKey(keys, "host"); host != "1.2.3.4" {
339		t.Error(`Expected "1.2.3.4", got`, host)
340	}
341	if port := getKey(keys, "port"); port != "5678" {
342		t.Error(`Expected "5678", got`, port)
343	}
344	if family := getKey(keys, "family"); family != "ipv4" {
345		t.Error(`Expected "ipv4", got`, family)
346	}
347}
348