...

Source file src/github.com/godbus/dbus/v5/server_interfaces_test.go

Documentation: github.com/godbus/dbus/v5

     1  package dbus
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"sync/atomic"
     7  	"testing"
     8  	"time"
     9  )
    10  
    11  type tester struct {
    12  	conn *Conn
    13  	sigs chan *Signal
    14  
    15  	subSigsMu sync.Mutex
    16  	subSigs   map[string]map[string]struct{}
    17  
    18  	serial uint32
    19  }
    20  
    21  type intro struct {
    22  	path ObjectPath
    23  }
    24  
    25  func (i *intro) introspectPath(path ObjectPath) string {
    26  	switch path {
    27  	case "/":
    28  		return `<node><node name="com"></node></node>`
    29  	case "/com":
    30  		return `<node><node name="github"></node></node>`
    31  	case "/com/github":
    32  		return `<node><node name="godbus"></node></node>`
    33  	case "/com/github/godbus":
    34  		return `<node><node name="tester"></node></node>`
    35  	}
    36  	return ""
    37  }
    38  
    39  func (i *intro) LookupInterface(name string) (Interface, bool) {
    40  	if name == "org.freedesktop.DBus.Introspectable" {
    41  		return i, true
    42  	}
    43  	return nil, false
    44  }
    45  
    46  func (i *intro) LookupMethod(name string) (Method, bool) {
    47  	if name == "Introspect" {
    48  		return intro_fn(func() string {
    49  			return i.introspectPath(i.path)
    50  		}), true
    51  	}
    52  	return nil, false
    53  }
    54  
    55  func newIntro(path ObjectPath) *intro {
    56  	return &intro{path}
    57  }
    58  
    59  //Handler
    60  func (t *tester) LookupObject(path ObjectPath) (ServerObject, bool) {
    61  	if path == "/com/github/godbus/tester" {
    62  		return t, true
    63  	}
    64  	return newIntro(path), true
    65  }
    66  
    67  //ServerObject
    68  func (t *tester) LookupInterface(name string) (Interface, bool) {
    69  	switch name {
    70  	case "com.github.godbus.dbus.Tester":
    71  		return t, true
    72  	case "org.freedesktop.DBus.Introspectable":
    73  		return t, true
    74  	}
    75  
    76  	return nil, false
    77  }
    78  
    79  //Interface
    80  func (t *tester) LookupMethod(name string) (Method, bool) {
    81  	switch name {
    82  	case "Test":
    83  		return t, true
    84  	case "Error":
    85  		return terrfn(func(in string) error {
    86  			return fmt.Errorf(in)
    87  		}), true
    88  	case "Introspect":
    89  		return intro_fn(func() string {
    90  			return `<node>
    91      <interface name="org.freedesktop.DBus.Introspectable.Introspect">
    92          <method name="Introspect">
    93              <arg name="out" type="i" direction="out">
    94          </method>
    95      </interface>
    96      <interface name="com.github.godbus.dbus.Tester">
    97          <method name="Test">
    98              <arg name="in" type="i" direction="in">
    99              <arg name="out" type="i" direction="out">
   100          </method>
   101          <signal name="sig1">
   102              <arg name="out" type="i" direction="out">
   103          </signal>
   104      </interface>
   105  </node>`
   106  		}), true
   107  	}
   108  	return nil, false
   109  }
   110  
   111  //Method
   112  func (t *tester) Call(args ...interface{}) ([]interface{}, error) {
   113  	return args, nil
   114  }
   115  
   116  func (t *tester) NumArguments() int {
   117  	return 1
   118  }
   119  
   120  func (t *tester) NumReturns() int {
   121  	return 1
   122  }
   123  
   124  func (t *tester) ArgumentValue(position int) interface{} {
   125  	return ""
   126  }
   127  
   128  func (t *tester) ReturnValue(position int) interface{} {
   129  	return ""
   130  }
   131  
   132  type terrfn func(in string) error
   133  
   134  func (t terrfn) Call(args ...interface{}) ([]interface{}, error) {
   135  	return nil, t(*args[0].(*string))
   136  }
   137  
   138  func (t terrfn) NumArguments() int {
   139  	return 1
   140  }
   141  
   142  func (t terrfn) NumReturns() int {
   143  	return 0
   144  }
   145  
   146  func (t terrfn) ArgumentValue(position int) interface{} {
   147  	return ""
   148  }
   149  
   150  func (t terrfn) ReturnValue(position int) interface{} {
   151  	return ""
   152  }
   153  
   154  //SignalHandler
   155  func (t *tester) DeliverSignal(iface, name string, signal *Signal) {
   156  	t.subSigsMu.Lock()
   157  	intf, ok := t.subSigs[iface]
   158  	t.subSigsMu.Unlock()
   159  	if !ok {
   160  		return
   161  	}
   162  	if _, ok := intf[name]; !ok {
   163  		return
   164  	}
   165  	t.sigs <- signal
   166  }
   167  
   168  func (t *tester) AddSignal(iface, name string) error {
   169  	t.subSigsMu.Lock()
   170  	if i, ok := t.subSigs[iface]; ok {
   171  		i[name] = struct{}{}
   172  	} else {
   173  		t.subSigs[iface] = make(map[string]struct{})
   174  		t.subSigs[iface][name] = struct{}{}
   175  	}
   176  	t.subSigsMu.Unlock()
   177  	return t.conn.AddMatchSignal(WithMatchInterface(iface), WithMatchMember(name))
   178  }
   179  
   180  func (t *tester) Close() {
   181  	t.conn.Close()
   182  	close(t.sigs)
   183  }
   184  
   185  func (t *tester) Name() string {
   186  	return t.conn.Names()[0]
   187  }
   188  
   189  func (t *tester) GetSerial() uint32 {
   190  	return atomic.AddUint32(&t.serial, 1)
   191  }
   192  
   193  func (t *tester) RetireSerial(serial uint32) {}
   194  
   195  type intro_fn func() string
   196  
   197  func (intro intro_fn) Call(args ...interface{}) ([]interface{}, error) {
   198  	return []interface{}{intro()}, nil
   199  }
   200  
   201  func (_ intro_fn) NumArguments() int {
   202  	return 0
   203  }
   204  
   205  func (_ intro_fn) NumReturns() int {
   206  	return 1
   207  }
   208  
   209  func (_ intro_fn) ArgumentValue(position int) interface{} {
   210  	return nil
   211  }
   212  
   213  func (_ intro_fn) ReturnValue(position int) interface{} {
   214  	return ""
   215  }
   216  
   217  func newTester() (*tester, error) {
   218  	tester := &tester{
   219  		sigs:    make(chan *Signal),
   220  		subSigs: make(map[string]map[string]struct{}),
   221  	}
   222  	conn, err := ConnectSessionBus(
   223  		WithHandler(tester),
   224  		WithSignalHandler(tester),
   225  		WithSerialGenerator(tester),
   226  	)
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  	tester.conn = conn
   231  	return tester, nil
   232  }
   233  
   234  func TestHandlerCall(t *testing.T) {
   235  	tester, err := newTester()
   236  	if err != nil {
   237  		t.Errorf("Unexpected error: %s", err)
   238  	}
   239  	conn, err := ConnectSessionBus()
   240  	if err != nil {
   241  		t.Errorf("Unexpected error: %s", err)
   242  	}
   243  	defer conn.Close()
   244  	obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
   245  	var out string
   246  	in := "foo"
   247  	err = obj.Call("com.github.godbus.dbus.Tester.Test", 0, in).Store(&out)
   248  	if err != nil {
   249  		t.Errorf("Unexpected error: %s", err)
   250  	}
   251  	if out != in {
   252  		t.Errorf("Unexpected error: got %s, expected %s", out, in)
   253  	}
   254  	tester.Close()
   255  }
   256  
   257  func TestHandlerCallGenericError(t *testing.T) {
   258  	tester, err := newTester()
   259  	if err != nil {
   260  		t.Errorf("Unexpected error: %s", err)
   261  	}
   262  	conn, err := ConnectSessionBus()
   263  	if err != nil {
   264  		t.Errorf("Unexpected error: %s", err)
   265  	}
   266  	defer conn.Close()
   267  	obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
   268  	var out string
   269  	in := "foo"
   270  	err = obj.Call("com.github.godbus.dbus.Tester.Error", 0, in).Store(&out)
   271  	if err != nil && err.(Error).Body[0].(string) != "foo" {
   272  		t.Errorf("Unexpected error: %s", err)
   273  	}
   274  
   275  	tester.Close()
   276  }
   277  
   278  func TestHandlerCallNonExistent(t *testing.T) {
   279  	tester, err := newTester()
   280  	if err != nil {
   281  		t.Errorf("Unexpected error: %s", err)
   282  	}
   283  	conn, err := ConnectSessionBus()
   284  	if err != nil {
   285  		t.Errorf("Unexpected error: %s", err)
   286  	}
   287  	defer conn.Close()
   288  	obj := conn.Object(tester.Name(), "/com/github/godbus/tester/nonexist")
   289  	var out string
   290  	in := "foo"
   291  	err = obj.Call("com.github.godbus.dbus.Tester.Test", 0, in).Store(&out)
   292  	if err != nil {
   293  		if err.Error() != "Object does not implement the interface 'com.github.godbus.dbus.Tester'" {
   294  			t.Errorf("Unexpected error: %s", err)
   295  		}
   296  	}
   297  	tester.Close()
   298  }
   299  
   300  func TestHandlerInvalidFunc(t *testing.T) {
   301  	tester, err := newTester()
   302  	if err != nil {
   303  		t.Errorf("Unexpected error: %s", err)
   304  	}
   305  	conn, err := ConnectSessionBus()
   306  	if err != nil {
   307  		t.Errorf("Unexpected error: %s", err)
   308  	}
   309  	defer conn.Close()
   310  	obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
   311  	var out string
   312  	in := "foo"
   313  	err = obj.Call("com.github.godbus.dbus.Tester.Notexist", 0, in).Store(&out)
   314  	if err == nil {
   315  		t.Errorf("didn't get expected error")
   316  	}
   317  	tester.Close()
   318  }
   319  
   320  func TestHandlerInvalidNumArg(t *testing.T) {
   321  	tester, err := newTester()
   322  	if err != nil {
   323  		t.Errorf("Unexpected error: %s", err)
   324  	}
   325  	conn, err := ConnectSessionBus()
   326  	if err != nil {
   327  		t.Errorf("Unexpected error: %s", err)
   328  	}
   329  	defer conn.Close()
   330  	obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
   331  	var out string
   332  	err = obj.Call("com.github.godbus.dbus.Tester.Test", 0).Store(&out)
   333  	if err == nil {
   334  		t.Errorf("didn't get expected error")
   335  	}
   336  	tester.Close()
   337  }
   338  
   339  func TestHandlerInvalidArgType(t *testing.T) {
   340  	tester, err := newTester()
   341  	if err != nil {
   342  		t.Errorf("Unexpected error: %s", err)
   343  	}
   344  	conn, err := ConnectSessionBus()
   345  	if err != nil {
   346  		t.Errorf("Unexpected error: %s", err)
   347  	}
   348  	defer conn.Close()
   349  	obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
   350  	var out string
   351  	err = obj.Call("com.github.godbus.dbus.Tester.Test", 0, 2.10).Store(&out)
   352  	if err == nil {
   353  		t.Errorf("didn't get expected error")
   354  	}
   355  	tester.Close()
   356  }
   357  
   358  func TestHandlerIntrospect(t *testing.T) {
   359  	tester, err := newTester()
   360  	if err != nil {
   361  		t.Errorf("Unexpected error: %s", err)
   362  	}
   363  	conn, err := ConnectSessionBus()
   364  	if err != nil {
   365  		t.Errorf("Unexpected error: %s", err)
   366  	}
   367  	defer conn.Close()
   368  	obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
   369  	var out string
   370  	err = obj.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&out)
   371  	if err != nil {
   372  		t.Errorf("Unexpected error: %s", err)
   373  	}
   374  	expected := `<node>
   375      <interface name="org.freedesktop.DBus.Introspectable.Introspect">
   376          <method name="Introspect">
   377              <arg name="out" type="i" direction="out">
   378          </method>
   379      </interface>
   380      <interface name="com.github.godbus.dbus.Tester">
   381          <method name="Test">
   382              <arg name="in" type="i" direction="in">
   383              <arg name="out" type="i" direction="out">
   384          </method>
   385          <signal name="sig1">
   386              <arg name="out" type="i" direction="out">
   387          </signal>
   388      </interface>
   389  </node>`
   390  	if out != expected {
   391  		t.Errorf("didn't get expected return value, expected %s got %s", expected, out)
   392  	}
   393  	tester.Close()
   394  }
   395  
   396  func TestHandlerIntrospectPath(t *testing.T) {
   397  	tester, err := newTester()
   398  	if err != nil {
   399  		t.Errorf("Unexpected error: %s", err)
   400  	}
   401  	conn, err := ConnectSessionBus()
   402  	if err != nil {
   403  		t.Errorf("Unexpected error: %s", err)
   404  	}
   405  	defer conn.Close()
   406  	obj := conn.Object(tester.Name(), "/com/github/godbus")
   407  	var out string
   408  	err = obj.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&out)
   409  	if err != nil {
   410  		t.Errorf("Unexpected error: %s", err)
   411  	}
   412  	expected := `<node><node name="tester"></node></node>`
   413  	if out != expected {
   414  		t.Errorf("didn't get expected return value, expected %s got %s", expected, out)
   415  	}
   416  	tester.Close()
   417  }
   418  
   419  func TestHandlerSignal(t *testing.T) {
   420  	tester, err := newTester()
   421  	if err != nil {
   422  		t.Errorf("Unexpected error: %s", err)
   423  	}
   424  	conn, err := ConnectSessionBus()
   425  	if err != nil {
   426  		t.Errorf("Unexpected error: %s", err)
   427  	}
   428  	defer conn.Close()
   429  	if err = tester.AddSignal("com.github.godbus.dbus.Tester", "sig1"); err != nil {
   430  		t.Fatal(err)
   431  	}
   432  	if err = conn.Emit(
   433  		"/com/github/godbus/tester",
   434  		"com.github.godbus.dbus.Tester.sig1",
   435  		"foo",
   436  	); err != nil {
   437  		t.Fatal(err)
   438  	}
   439  	select {
   440  	case sig := <-tester.sigs:
   441  		if sig.Body[0] != "foo" {
   442  			t.Errorf("Unexpected signal got %s, expected %s", sig.Body[0], "foo")
   443  		}
   444  	case <-time.After(time.Second * 10): //overly generous timeout
   445  		t.Errorf("Didn't receive a signal after 10 seconds")
   446  	}
   447  	tester.Close()
   448  }
   449  
   450  type X struct {
   451  }
   452  
   453  func (x *X) Method1() *Error {
   454  	return nil
   455  }
   456  
   457  func TestRaceInExport(t *testing.T) {
   458  	const (
   459  		dbusPath      = "/org/example/godbus/test1"
   460  		dbusInterface = "org.example.godbus.test1"
   461  	)
   462  
   463  	bus, err := ConnectSessionBus()
   464  	if err != nil {
   465  		t.Fatal(err)
   466  	}
   467  	defer bus.Close()
   468  
   469  	var x X
   470  
   471  	var wg sync.WaitGroup
   472  	wg.Add(2)
   473  	go func() {
   474  		err = bus.Export(&x, dbusPath, dbusInterface)
   475  		if err != nil {
   476  			t.Fatal(err)
   477  		}
   478  		wg.Done()
   479  	}()
   480  
   481  	go func() {
   482  		obj := bus.Object(bus.Names()[0], dbusPath)
   483  		obj.Call(dbusInterface+".Method1", 0)
   484  		wg.Done()
   485  	}()
   486  	wg.Wait()
   487  }
   488  

View as plain text