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