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