1 package dbus
2
3 import (
4 "fmt"
5 "regexp"
6 "strings"
7 "testing"
8 )
9
10 type lowerCaseExport struct{}
11
12 func (export lowerCaseExport) foo() (string, *Error) {
13 return "bar", nil
14 }
15
16 type fooExport struct {
17 message Message
18 }
19
20 func (export *fooExport) Foo(message Message, param string) (string, *Error) {
21 export.message = message
22 return "foo", nil
23 }
24
25 type barExport struct{}
26
27 func (export barExport) Foo(param string) (string, *Error) {
28 return "bar", nil
29 }
30
31 type badExport struct{}
32
33 func (export badExport) Foo(param string) string {
34 return "bar"
35 }
36
37 type errorExport struct {
38 message Message
39 }
40
41 func (export *errorExport) Run(message Message, param string) (string, error) {
42 export.message = message
43 return "pass", nil
44 }
45
46 type noErrorExport struct {
47 message Message
48 }
49
50 func (export *noErrorExport) Run(message Message, param string) (string) {
51 export.message = message
52 return "cool"
53 }
54
55
56 func TestExport(t *testing.T) {
57 connection, err := ConnectSessionBus()
58 if err != nil {
59 t.Fatalf("Unexpected error connecting to session bus: %s", err)
60 }
61 defer connection.Close()
62
63 name := connection.Names()[0]
64
65 connection.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
66 object := connection.Object(name, "/org/guelfey/DBus/Test")
67 subtreeObject := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
68
69 var response int64
70 err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
71 if err != nil {
72 t.Errorf("Unexpected error calling Double: %s", err)
73 }
74
75 if response != 4 {
76 t.Errorf("Response was %d, expected 4", response)
77 }
78
79
80
81 err = subtreeObject.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
82 if err == nil {
83 t.Error("Expected error due to no object being exported on that path")
84 }
85
86
87 connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
88 err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
89 if err == nil {
90 t.Error("Expected an error since the export was removed")
91 }
92 }
93
94
95 func TestExport_goerror(t *testing.T) {
96 connection, err := ConnectSessionBus()
97 if err != nil {
98 t.Fatalf("Unexpected error connecting to session bus: %s", err)
99 }
100 defer connection.Close()
101
102 name := connection.Names()[0]
103
104 export := &errorExport{}
105 connection.ExportAll(export, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
106 object := connection.Object(name, "/org/guelfey/DBus/Test")
107
108 var response string
109 err = object.Call("org.guelfey.DBus.Test.Run", 0, "qux").Store(&response)
110 if err != nil {
111 t.Errorf("Unexpected error calling Foo: %s", err)
112 }
113
114 if response != "pass" {
115 t.Errorf(`Response was %s, expected "foo"`, response)
116 }
117
118 if export.message.serial == 0 {
119 t.Error("Expected a valid message to be given to handler")
120 }
121 }
122
123
124 func TestExport_noerror(t *testing.T) {
125 connection, err := ConnectSessionBus()
126 if err != nil {
127 t.Fatalf("Unexpected error connecting to session bus: %s", err)
128 }
129 defer connection.Close()
130
131 name := connection.Names()[0]
132
133 export := &noErrorExport{}
134 connection.ExportAll(export, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
135 object := connection.Object(name, "/org/guelfey/DBus/Test")
136
137 var response string
138 err = object.Call("org.guelfey.DBus.Test.Run", 0, "qux").Store(&response)
139 if err != nil {
140 t.Errorf("Unexpected error calling Foo: %s", err)
141 }
142
143 if response != "cool" {
144 t.Errorf(`Response was %s, expected "foo"`, response)
145 }
146
147 if export.message.serial == 0 {
148 t.Error("Expected a valid message to be given to handler")
149 }
150 }
151
152
153 func TestExport_message(t *testing.T) {
154 connection, err := ConnectSessionBus()
155 if err != nil {
156 t.Fatalf("Unexpected error connecting to session bus: %s", err)
157 }
158 defer connection.Close()
159
160 name := connection.Names()[0]
161
162 export := &fooExport{}
163 connection.Export(export, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
164 object := connection.Object(name, "/org/guelfey/DBus/Test")
165
166 var response string
167 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
168 if err != nil {
169 t.Errorf("Unexpected error calling Foo: %s", err)
170 }
171
172 if response != "foo" {
173 t.Errorf(`Response was %s, expected "foo"`, response)
174 }
175
176 if export.message.serial == 0 {
177 t.Error("Expected a valid message to be given to handler")
178 }
179 }
180
181
182 func TestExport_invalidPath(t *testing.T) {
183 connection, err := ConnectSessionBus()
184 if err != nil {
185 t.Fatalf("Unexpected error connecting to session bus: %s", err)
186 }
187 defer connection.Close()
188
189 err = connection.Export(nil, "foo", "bar")
190 if err == nil {
191 t.Error("Expected an error due to exporting with an invalid path")
192 }
193 }
194
195
196
197 func TestExport_unexportedMethod(t *testing.T) {
198 connection, err := ConnectSessionBus()
199 if err != nil {
200 t.Fatalf("Unexpected error connecting to session bus: %s", err)
201 }
202 defer connection.Close()
203
204 name := connection.Names()[0]
205
206 connection.Export(lowerCaseExport{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
207 object := connection.Object(name, "/org/guelfey/DBus/Test")
208
209 var response string
210 call := object.Call("org.guelfey.DBus.Test.foo", 0)
211 err = call.Store(&response)
212 if err == nil {
213 t.Errorf("Expected an error due to calling unexported method")
214 }
215 }
216
217
218
219 func TestExport_badSignature(t *testing.T) {
220 connection, err := ConnectSessionBus()
221 if err != nil {
222 t.Fatalf("Unexpected error connecting to session bus: %s", err)
223 }
224 defer connection.Close()
225
226 name := connection.Names()[0]
227
228 connection.Export(badExport{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
229 object := connection.Object(name, "/org/guelfey/DBus/Test")
230
231 var response string
232 call := object.Call("org.guelfey.DBus.Test.Foo", 0)
233 err = call.Store(&response)
234 if err == nil {
235 t.Errorf("Expected an error due to the method lacking the right signature")
236 }
237 }
238
239
240 func TestExportWithMap(t *testing.T) {
241 connection, err := ConnectSessionBus()
242 if err != nil {
243 t.Fatalf("Unexpected error connecting to session bus: %s", err)
244 }
245 defer connection.Close()
246
247 name := connection.Names()[0]
248
249 mapping := make(map[string]string)
250 mapping["Double"] = "double"
251
252 connection.ExportWithMap(server{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
253 object := connection.Object(name, "/org/guelfey/DBus/Test")
254
255 var response int64
256 err = object.Call("org.guelfey.DBus.Test.double", 0, int64(2)).Store(&response)
257 if err != nil {
258 t.Errorf("Unexpected error calling double: %s", err)
259 }
260
261 if response != 4 {
262 t.Errorf("Response was %d, expected 4", response)
263 }
264 }
265
266
267 func TestExportWithMap_bypassAlias(t *testing.T) {
268 connection, err := ConnectSessionBus()
269 if err != nil {
270 t.Fatalf("Unexpected error connecting to session bus: %s", err)
271 }
272 defer connection.Close()
273
274 name := connection.Names()[0]
275
276 mapping := make(map[string]string)
277 mapping["Double"] = "double"
278
279 connection.ExportWithMap(server{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
280 object := connection.Object(name, "/org/guelfey/DBus/Test")
281
282 var response int64
283
284 err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
285 if err == nil {
286 t.Error("Expected an error due to calling actual method, not alias")
287 }
288 }
289
290
291 func TestExportSubtree(t *testing.T) {
292 connection, err := ConnectSessionBus()
293 if err != nil {
294 t.Fatalf("Unexpected error connecting to session bus: %s", err)
295 }
296 defer connection.Close()
297
298 name := connection.Names()[0]
299
300 export := &fooExport{}
301 connection.ExportSubtree(export, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
302
303
304 object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
305
306 var response string
307 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
308 if err != nil {
309 t.Errorf("Unexpected error calling Foo: %s", err)
310 }
311
312 if response != "foo" {
313 t.Errorf(`Response was %s, expected "foo"`, response)
314 }
315
316 if export.message.serial == 0 {
317 t.Error("Expected the raw message, got an invalid one")
318 }
319
320
321 connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
322 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
323 if err == nil {
324 t.Error("Expected an error since the export was removed")
325 }
326 }
327
328
329
330 func TestExportSubtree_noMessage(t *testing.T) {
331 connection, err := ConnectSessionBus()
332 if err != nil {
333 t.Fatalf("Unexpected error connecting to session bus: %s", err)
334 }
335 defer connection.Close()
336
337 name := connection.Names()[0]
338
339 connection.ExportSubtree(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
340
341
342 object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
343
344 var response int64
345 err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
346 if err != nil {
347 t.Errorf("Unexpected error calling Double: %s", err)
348 }
349
350 if response != 4 {
351 t.Errorf("Response was %d, expected 4", response)
352 }
353
354
355 connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
356 err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
357 if err == nil {
358 t.Error("Expected an error since the export was removed")
359 }
360 }
361
362
363 func TestExportSubtree_exportPrecedence(t *testing.T) {
364 connection, err := ConnectSessionBus()
365 if err != nil {
366 t.Fatalf("Unexpected error connecting to session bus: %s", err)
367 }
368 defer connection.Close()
369
370 name := connection.Names()[0]
371
372
373 connection.ExportSubtree(&fooExport{},
374 "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
375
376
377 connection.Export(&barExport{}, "/org/guelfey/DBus/Test/Foo",
378 "org.guelfey.DBus.Test")
379
380
381 object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
382
383 var response string
384 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
385 if err != nil {
386 t.Errorf("Unexpected error calling Foo: %s", err)
387 }
388
389 if response != "bar" {
390 t.Errorf(`Response was %s, expected "bar"`, response)
391 }
392
393 response = ""
394
395
396 connection.Export(nil, "/org/guelfey/DBus/Test/Foo", "org.guelfey.DBus.Test")
397 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
398 if err != nil {
399 t.Errorf("Unexpected error calling Foo: %s", err)
400 }
401
402
403 if response != "foo" {
404 t.Errorf(`Response was %s, expected "foo"`, response)
405 }
406 }
407
408
409 func TestExportSubtreeWithMap(t *testing.T) {
410 connection, err := ConnectSessionBus()
411 if err != nil {
412 t.Fatalf("Unexpected error connecting to session bus: %s", err)
413 }
414 defer connection.Close()
415
416 name := connection.Names()[0]
417
418 mapping := make(map[string]string)
419 mapping["Foo"] = "foo"
420
421 connection.ExportSubtreeWithMap(&fooExport{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
422
423
424 object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
425
426 var response string
427
428 err = object.Call("org.guelfey.DBus.Test.foo", 0, "qux").Store(&response)
429 if err != nil {
430 t.Errorf("Unexpected error calling Foo: %s", err)
431 }
432
433 if response != "foo" {
434 t.Errorf(`Response was %s, expected "foo"`, response)
435 }
436
437
438 connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
439 err = object.Call("org.guelfey.DBus.Test.foo", 0, "qux").Store(&response)
440 if err == nil {
441 t.Error("Expected an error since the export was removed")
442 }
443 }
444
445
446 func TestExportSubtreeWithMap_bypassAlias(t *testing.T) {
447 connection, err := ConnectSessionBus()
448 if err != nil {
449 t.Fatalf("Unexpected error connecting to session bus: %s", err)
450 }
451 defer connection.Close()
452
453 name := connection.Names()[0]
454
455 mapping := make(map[string]string)
456 mapping["Foo"] = "foo"
457
458 connection.ExportSubtreeWithMap(&fooExport{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
459 object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
460
461 var response string
462
463 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
464 if err == nil {
465 t.Error("Expected an error due to calling actual method, not alias")
466 }
467 }
468
469 func TestExportMethodTable(t *testing.T) {
470 connection, err := ConnectSessionBus()
471 if err != nil {
472 t.Fatalf("Unexpected error connecting to session bus: %s", err)
473 }
474 defer connection.Close()
475
476 name := connection.Names()[0]
477 export := &fooExport{}
478 tbl := make(map[string]interface{})
479 tbl["Foo"] = func(message Message, param string) (string, *Error) {
480 return export.Foo(message, param)
481 }
482 tbl["Foo2"] = export.Foo
483 connection.ExportMethodTable(tbl, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
484
485 object := connection.Object(name, "/org/guelfey/DBus/Test")
486
487 var response string
488 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
489 if err != nil {
490 t.Errorf("Unexpected error calling Foo: %s", err)
491 }
492
493 if response != "foo" {
494 t.Errorf(`Response was %s, expected "foo"`, response)
495 }
496
497 if export.message.serial == 0 {
498 t.Error("Expected the raw message, got an invalid one")
499 }
500
501 err = object.Call("org.guelfey.DBus.Test.Foo2", 0, "qux").Store(&response)
502 if err != nil {
503 t.Errorf("Unexpected error calling Foo: %s", err)
504 }
505
506 if response != "foo" {
507 t.Errorf(`Response was %s, expected "foo"`, response)
508 }
509
510 if export.message.serial == 0 {
511 t.Error("Expected the raw message, got an invalid one")
512 }
513
514
515 connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
516 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
517 if err == nil {
518 t.Error("Expected an error since the export was removed")
519 }
520 }
521
522 func TestExportSubtreeMethodTable(t *testing.T) {
523 connection, err := ConnectSessionBus()
524 if err != nil {
525 t.Fatalf("Unexpected error connecting to session bus: %s", err)
526 }
527 defer connection.Close()
528
529 name := connection.Names()[0]
530
531 export := &fooExport{}
532 tbl := make(map[string]interface{})
533 tbl["Foo"] = func(message Message, param string) (string, *Error) {
534 return export.Foo(message, param)
535 }
536 tbl["Foo2"] = export.Foo
537 connection.ExportSubtreeMethodTable(tbl, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
538
539
540 object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
541
542 var response string
543 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
544 if err != nil {
545 t.Errorf("Unexpected error calling Foo: %s", err)
546 }
547
548 if response != "foo" {
549 t.Errorf(`Response was %s, expected "foo"`, response)
550 }
551
552 if export.message.serial == 0 {
553 t.Error("Expected the raw message, got an invalid one")
554 }
555
556 err = object.Call("org.guelfey.DBus.Test.Foo2", 0, "qux").Store(&response)
557 if err != nil {
558 t.Errorf("Unexpected error calling Foo: %s", err)
559 }
560
561 if response != "foo" {
562 t.Errorf(`Response was %s, expected "foo"`, response)
563 }
564
565 if export.message.serial == 0 {
566 t.Error("Expected the raw message, got an invalid one")
567 }
568
569
570 connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
571 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
572 if err == nil {
573 t.Error("Expected an error since the export was removed")
574 }
575 }
576
577 func TestExportMethodTable_NotFunc(t *testing.T) {
578 connection, err := ConnectSessionBus()
579 if err != nil {
580 t.Fatalf("Unexpected error connecting to session bus: %s", err)
581 }
582 defer connection.Close()
583
584 name := connection.Names()[0]
585 export := &fooExport{}
586 tbl := make(map[string]interface{})
587 tbl["Foo"] = func(message Message, param string) (string, *Error) {
588 return export.Foo(message, param)
589 }
590 tbl["Foo2"] = "foobar"
591
592 connection.ExportMethodTable(tbl, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
593 object := connection.Object(name, "/org/guelfey/DBus/Test")
594
595 var response string
596 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
597 if err != nil {
598 t.Errorf("Unexpected error calling Foo: %s", err)
599 }
600
601 if response != "foo" {
602 t.Errorf(`Response was %s, expected "foo"`, response)
603 }
604
605 if export.message.serial == 0 {
606 t.Error("Expected the raw message, got an invalid one")
607 }
608
609 err = object.Call("org.guelfey.DBus.Test.Foo2", 0, "qux").Store(&response)
610 if err == nil {
611 t.Errorf("Expected an error since the Foo2 was not a function")
612 }
613 }
614
615 func TestExportMethodTable_ReturnNotError(t *testing.T) {
616 connection, err := ConnectSessionBus()
617 if err != nil {
618 t.Fatalf("Unexpected error connecting to session bus: %s", err)
619 }
620 defer connection.Close()
621
622 name := connection.Names()[0]
623 export := &fooExport{}
624 tbl := make(map[string]interface{})
625 tbl["Foo"] = func(message Message, param string) (string, string) {
626 out, _ := export.Foo(message, param)
627 return out, out
628 }
629
630 connection.ExportMethodTable(tbl, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
631 object := connection.Object(name, "/org/guelfey/DBus/Test")
632
633 var response string
634 err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
635 if err == nil {
636 t.Errorf("Expected an error since the Foo did not have a final return as *dbus.Error")
637 }
638 }
639
640
641 func TestExportSubPathIntrospection(t *testing.T) {
642 const (
643 introIntf = "org.freedesktop.DBus.Introspectable"
644 respTmpl = `^<node>\s*<node\s+name="%s"\s*/>\s*</node>$`
645 pathstr = "/org/guelfey/DBus/Test"
646 foopathstr = pathstr + "/Foo"
647 barpathstr = pathstr + "/Bar"
648 test1intfstr = "org.guelfey.DBus.Test1"
649 test2intfstr = "org.guelfey.DBus.Test2"
650 intro = `
651 <node>
652 <interface name="` + test1intfstr + `">
653 <method name="Foo">
654 <arg direction="out" type="s"/>
655 </method>
656 </interface>
657 <interface name="` + test2intfstr + `">
658 <method name="Foo">
659 <arg direction="out" type="s"/>
660 </method>
661 <method name="Bar">
662 <arg direction="out" type="s"/>
663 </method>
664 </interface>
665 <interface name="` + introIntf + `">
666 <method name="Introspect">
667 <arg name="out" direction="out" type="s"/>
668 </method>
669 </interface>
670 </node>`
671 )
672 connection, err := ConnectSessionBus()
673 if err != nil {
674 t.Fatalf("Unexpected error connecting to session bus: %s", err)
675 }
676 defer connection.Close()
677
678 name := connection.Names()[0]
679
680 foo := &fooExport{}
681 bar := &barExport{}
682 connection.Export(foo, foopathstr, test1intfstr)
683 connection.Export(foo, foopathstr, test2intfstr)
684 connection.Export(bar, barpathstr, test2intfstr)
685 connection.Export(intro, pathstr, introIntf)
686
687 var response string
688 var match bool
689 path := strings.Split(pathstr, "/")
690 for i := 0; i < len(path)-1; i++ {
691 var subpath string
692 if i == 0 {
693 subpath = "/"
694 } else {
695 subpath = strings.Join(path[:i+1], "/")
696 }
697
698 object := connection.Object(name, ObjectPath(subpath))
699 err = object.Call(introIntf+".Introspect", 0).Store(&response)
700 if err != nil {
701 t.Errorf("Unexpected error calling Introspect on %s: %s", subpath, err)
702 }
703
704 exp := fmt.Sprintf(respTmpl, path[i+1])
705 match, err = regexp.MatchString(exp, response)
706 if err != nil {
707 t.Fatalf("Error calling MatchString: %s", err)
708 }
709 if !match {
710 t.Errorf("Unexpected introspection response for %s: %s", subpath, response)
711 }
712 }
713
714
715 invalSubpath := "/org/guelfey/DBus/Test/Nonexistent"
716 object := connection.Object(name, ObjectPath(invalSubpath))
717 err = object.Call(introIntf+".Introspect", 0).Store(&response)
718 if err != nil {
719 t.Errorf("Unexpected error calling Introspect on %s: %s", invalSubpath, err)
720 }
721 match, err = regexp.MatchString(`^<node>\s*</node>$`, response)
722 if err != nil {
723 t.Fatalf("Error calling MatchString: %s", err)
724 }
725 if !match {
726 t.Errorf("Unexpected introspection response for %s: %s", invalSubpath, response)
727 }
728 }
729
View as plain text