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	b.ReportAllocs()
164	var s string
165	bus, err := SessionBus()
166	if err != nil {
167		b.Fatal(err)
168	}
169	name := bus.Names()[0]
170	obj := bus.BusObject()
171	b.StartTimer()
172	for i := 0; i < b.N; i++ {
173		err := obj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&s)
174		if err != nil {
175			b.Fatal(err)
176		}
177		if s != name {
178			b.Errorf("got %s, wanted %s", s, name)
179		}
180	}
181}
182
183func BenchmarkCallAsync(b *testing.B) {
184	b.StopTimer()
185	b.ReportAllocs()
186	bus, err := SessionBus()
187	if err != nil {
188		b.Fatal(err)
189	}
190	name := bus.Names()[0]
191	obj := bus.BusObject()
192	c := make(chan *Call, 50)
193	done := make(chan struct{})
194	go func() {
195		for i := 0; i < b.N; i++ {
196			v := <-c
197			if v.Err != nil {
198				b.Error(v.Err)
199			}
200			s := v.Body[0].(string)
201			if s != name {
202				b.Errorf("got %s, wanted %s", s, name)
203			}
204		}
205		close(done)
206	}()
207	b.StartTimer()
208	for i := 0; i < b.N; i++ {
209		obj.Go("org.freedesktop.DBus.GetNameOwner", 0, c, name)
210	}
211	<-done
212}
213
214func BenchmarkServe(b *testing.B) {
215	b.StopTimer()
216	srv, err := SessionBus()
217	if err != nil {
218		b.Fatal(err)
219	}
220	cli, err := SessionBusPrivate()
221	if err != nil {
222		b.Fatal(err)
223	}
224	if err = cli.Auth(nil); err != nil {
225		b.Fatal(err)
226	}
227	if err = cli.Hello(); err != nil {
228		b.Fatal(err)
229	}
230	benchmarkServe(b, srv, cli)
231}
232
233func BenchmarkServeAsync(b *testing.B) {
234	b.StopTimer()
235	srv, err := SessionBus()
236	if err != nil {
237		b.Fatal(err)
238	}
239	cli, err := SessionBusPrivate()
240	if err != nil {
241		b.Fatal(err)
242	}
243	if err = cli.Auth(nil); err != nil {
244		b.Fatal(err)
245	}
246	if err = cli.Hello(); err != nil {
247		b.Fatal(err)
248	}
249	benchmarkServeAsync(b, srv, cli)
250}
251
252func BenchmarkServeSameConn(b *testing.B) {
253	b.StopTimer()
254	bus, err := SessionBus()
255	if err != nil {
256		b.Fatal(err)
257	}
258
259	benchmarkServe(b, bus, bus)
260}
261
262func BenchmarkServeSameConnAsync(b *testing.B) {
263	b.StopTimer()
264	bus, err := SessionBus()
265	if err != nil {
266		b.Fatal(err)
267	}
268
269	benchmarkServeAsync(b, bus, bus)
270}
271
272func benchmarkServe(b *testing.B, srv, cli *Conn) {
273	var r int64
274	var err error
275	dest := srv.Names()[0]
276	srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
277	obj := cli.Object(dest, "/org/guelfey/DBus/Test")
278	b.StartTimer()
279	for i := 0; i < b.N; i++ {
280		err = obj.Call("org.guelfey.DBus.Test.Double", 0, int64(i)).Store(&r)
281		if err != nil {
282			b.Fatal(err)
283		}
284		if r != 2*int64(i) {
285			b.Errorf("got %d, wanted %d", r, 2*int64(i))
286		}
287	}
288}
289
290func benchmarkServeAsync(b *testing.B, srv, cli *Conn) {
291	dest := srv.Names()[0]
292	srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
293	obj := cli.Object(dest, "/org/guelfey/DBus/Test")
294	c := make(chan *Call, 50)
295	done := make(chan struct{})
296	go func() {
297		for i := 0; i < b.N; i++ {
298			v := <-c
299			if v.Err != nil {
300				b.Fatal(v.Err)
301			}
302			i, r := v.Args[0].(int64), v.Body[0].(int64)
303			if 2*i != r {
304				b.Errorf("got %d, wanted %d", r, 2*i)
305			}
306		}
307		close(done)
308	}()
309	b.StartTimer()
310	for i := 0; i < b.N; i++ {
311		obj.Go("org.guelfey.DBus.Test.Double", 0, c, int64(i))
312	}
313	<-done
314}
315
316func TestGetKey(t *testing.T) {
317	keys := "host=1.2.3.4,port=5678,family=ipv4"
318	if host := getKey(keys, "host"); host != "1.2.3.4" {
319		t.Error(`Expected "1.2.3.4", got`, host)
320	}
321	if port := getKey(keys, "port"); port != "5678" {
322		t.Error(`Expected "5678", got`, port)
323	}
324	if family := getKey(keys, "family"); family != "ipv4" {
325		t.Error(`Expected "ipv4", got`, family)
326	}
327}
328