...

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

Documentation: github.com/godbus/dbus/v5

     1  package dbus
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  )
     8  
     9  type objectGoContextServer struct {
    10  	t     *testing.T
    11  	sleep time.Duration
    12  }
    13  
    14  func (o objectGoContextServer) Sleep() *Error {
    15  	o.t.Log("Got object call and sleeping for ", o.sleep)
    16  	time.Sleep(o.sleep)
    17  	o.t.Log("Completed sleeping for ", o.sleep)
    18  	return nil
    19  }
    20  
    21  func TestObjectGoWithContextTimeout(t *testing.T) {
    22  	bus, err := ConnectSessionBus()
    23  	if err != nil {
    24  		t.Fatalf("Unexpected error connecting to session bus: %s", err)
    25  	}
    26  	defer bus.Close()
    27  
    28  	name := bus.Names()[0]
    29  	bus.Export(objectGoContextServer{t, time.Second}, "/org/dannin/DBus/Test", "org.dannin.DBus.Test")
    30  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    31  	defer cancel()
    32  	select {
    33  	case call := <-bus.Object(name, "/org/dannin/DBus/Test").GoWithContext(ctx, "org.dannin.DBus.Test.Sleep", 0, nil).Done:
    34  		if call.Err != ctx.Err() {
    35  			t.Fatal("Expected ", ctx.Err(), " but got ", call.Err)
    36  		}
    37  	case <-time.After(2 * time.Second):
    38  		t.Fatal("Expected call to not respond in time")
    39  	}
    40  }
    41  
    42  func TestObjectGoWithContext(t *testing.T) {
    43  	bus, err := ConnectSessionBus()
    44  	if err != nil {
    45  		t.Fatalf("Unexpected error connecting to session bus: %s", err)
    46  	}
    47  	defer bus.Close()
    48  
    49  	name := bus.Names()[0]
    50  	bus.Export(objectGoContextServer{t, time.Millisecond}, "/org/dannin/DBus/Test", "org.dannin.DBus.Test")
    51  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    52  	defer cancel()
    53  	select {
    54  	case call := <-bus.Object(name, "/org/dannin/DBus/Test").GoWithContext(ctx, "org.dannin.DBus.Test.Sleep", 0, nil).Done:
    55  		if call.Err != ctx.Err() {
    56  			t.Fatal("Expected ", ctx.Err(), " but got ", call.Err)
    57  		}
    58  	case <-time.After(time.Second):
    59  		t.Fatal("Expected call to respond in 1 Millisecond")
    60  	}
    61  }
    62  
    63  type nopServer struct{}
    64  
    65  func (_ nopServer) Nop() *Error {
    66  	return nil
    67  }
    68  
    69  func TestObjectSignalHandling(t *testing.T) {
    70  	bus, err := ConnectSessionBus()
    71  	if err != nil {
    72  		t.Fatalf("Unexpected error connecting to session bus: %s", err)
    73  	}
    74  	defer bus.Close()
    75  
    76  	name := bus.Names()[0]
    77  	path := ObjectPath("/org/godbus/DBus/TestSignals")
    78  	otherPath := ObjectPath("/org/other/godbus/DBus/TestSignals")
    79  	iface := "org.godbus.DBus.TestSignals"
    80  	otherIface := "org.godbus.DBus.OtherTestSignals"
    81  	err = bus.Export(nopServer{}, path, iface)
    82  	if err != nil {
    83  		t.Fatalf("Unexpected error registering nop server: %v", err)
    84  	}
    85  
    86  	obj := bus.Object(name, path)
    87  	if err := bus.AddMatchSignal(
    88  		WithMatchInterface(iface),
    89  		WithMatchMember("Heartbeat"),
    90  		WithMatchObjectPath(path),
    91  	); err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	ch := make(chan *Signal, 5)
    96  	bus.Signal(ch)
    97  
    98  	go func() {
    99  		defer func() {
   100  			if err := recover(); err != nil {
   101  				t.Errorf("Caught panic in emitter goroutine: %v", err)
   102  			}
   103  		}()
   104  
   105  		emit := func(path ObjectPath, name string, values ...interface{}) {
   106  			t.Helper()
   107  			if err := bus.Emit(path, name, values...); err != nil {
   108  				t.Error("Emit:", err)
   109  			}
   110  		}
   111  
   112  		// desired signals
   113  		emit(path, iface+".Heartbeat", uint32(1))
   114  		emit(path, iface+".Heartbeat", uint32(2))
   115  		// undesired signals
   116  		emit(otherPath, iface+".Heartbeat", uint32(3))
   117  		emit(otherPath, otherIface+".Heartbeat", uint32(4))
   118  		emit(path, iface+".Updated", false)
   119  		// sentinel
   120  		emit(path, iface+".Heartbeat", uint32(5))
   121  
   122  		time.Sleep(100 * time.Millisecond)
   123  		emit(path, iface+".Heartbeat", uint32(6))
   124  	}()
   125  
   126  	checkSignal := func(ch chan *Signal, value uint32) {
   127  		t.Helper()
   128  
   129  		const timeout = 50 * time.Millisecond
   130  		var sig *Signal
   131  
   132  		select {
   133  		case sig = <-ch:
   134  			// do nothing
   135  		case <-time.After(timeout):
   136  			t.Fatalf("Failed to fetch signal in specified timeout %s", timeout)
   137  		}
   138  
   139  		if sig.Path != path {
   140  			t.Errorf("signal.Path mismatch: %s != %s", path, sig.Path)
   141  		}
   142  
   143  		name := iface + ".Heartbeat"
   144  		if sig.Name != name {
   145  			t.Errorf("signal.Name mismatch: %s != %s", name, sig.Name)
   146  		}
   147  
   148  		if len(sig.Body) != 1 {
   149  			t.Errorf("Invalid signal body length: %d", len(sig.Body))
   150  			return
   151  		}
   152  
   153  		if sig.Body[0] != interface{}(value) {
   154  			t.Errorf("signal value mismatch: %d != %d", value, sig.Body[0])
   155  		}
   156  	}
   157  
   158  	checkSignal(ch, 1)
   159  	checkSignal(ch, 2)
   160  	checkSignal(ch, 5)
   161  
   162  	obj.RemoveMatchSignal(iface, "Heartbeat", WithMatchObjectPath(obj.Path()))
   163  	select {
   164  	case sig := <-ch:
   165  		t.Errorf("Got signal after removing subscription: %v", sig)
   166  	case <-time.After(200 * time.Millisecond):
   167  	}
   168  }
   169  

View as plain text