| /* |
| Copyright 2015 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package dbus |
| |
| import ( |
| "fmt" |
| "os" |
| "testing" |
| |
| godbus "github.com/godbus/dbus" |
| ) |
| |
| const ( |
| DBusNameFlagDoNotQueue uint32 = 1 << (iota + 1) |
| ) |
| |
| const ( |
| DBusRequestNameReplyPrimaryOwner uint32 = iota + 1 |
| DBusRequestNameReplyAlreadyOwner |
| ) |
| |
| const ( |
| DBusReleaseNameReplyReleased uint32 = iota + 1 |
| DBusReleaseNameReplyNotOwner |
| ) |
| |
| func doDBusTest(t *testing.T, dbus Interface, real bool) { |
| bus, err := dbus.SystemBus() |
| if err != nil { |
| if !real { |
| t.Errorf("dbus.SystemBus() failed with fake Interface") |
| } |
| t.Skipf("D-Bus is not running: %v", err) |
| } |
| busObj := bus.BusObject() |
| |
| id := "" |
| err = busObj.Call("org.freedesktop.DBus.GetId", 0).Store(&id) |
| if err != nil { |
| t.Errorf("expected success, got %v", err) |
| } |
| if len(id) == 0 { |
| t.Errorf("expected non-empty Id, got \"\"") |
| } |
| |
| // Switch to the session bus for the rest, since the system bus is more |
| // locked down (and thus harder to trick into emitting signals). |
| |
| bus, err = dbus.SessionBus() |
| if err != nil { |
| if !real { |
| t.Errorf("dbus.SystemBus() failed with fake Interface") |
| } |
| t.Skipf("D-Bus session bus is not available: %v", err) |
| } |
| busObj = bus.BusObject() |
| |
| name := fmt.Sprintf("io.kubernetes.dbus_test_%d", os.Getpid()) |
| owner := "" |
| err = busObj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&owner) |
| if err == nil { |
| t.Errorf("expected '%s' to be un-owned, but found owner %s", name, owner) |
| } |
| dbuserr, ok := err.(godbus.Error) |
| if !ok { |
| t.Errorf("expected godbus.Error, but got %#v", err) |
| } |
| if dbuserr.Name != "org.freedesktop.DBus.Error.NameHasNoOwner" { |
| t.Errorf("expected NameHasNoOwner error but got %v", err) |
| } |
| |
| sigchan := make(chan *godbus.Signal, 10) |
| bus.Signal(sigchan) |
| |
| rule := fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", name) |
| err = busObj.Call("org.freedesktop.DBus.AddMatch", 0, rule).Store() |
| if err != nil { |
| t.Errorf("expected success, got %v", err) |
| } |
| |
| var ret uint32 |
| err = busObj.Call("org.freedesktop.DBus.RequestName", 0, name, DBusNameFlagDoNotQueue).Store(&ret) |
| if err != nil { |
| t.Errorf("expected success, got %v", err) |
| } |
| if ret != DBusRequestNameReplyPrimaryOwner { |
| t.Errorf("expected %v, got %v", DBusRequestNameReplyPrimaryOwner, ret) |
| } |
| |
| err = busObj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&owner) |
| if err != nil { |
| t.Errorf("expected success, got %v", err) |
| } |
| |
| var changedSignal, acquiredSignal, lostSignal *godbus.Signal |
| |
| sig1 := <-sigchan |
| sig2 := <-sigchan |
| // We get two signals, but the order isn't guaranteed |
| if sig1.Name == "org.freedesktop.DBus.NameOwnerChanged" { |
| changedSignal = sig1 |
| acquiredSignal = sig2 |
| } else { |
| acquiredSignal = sig1 |
| changedSignal = sig2 |
| } |
| |
| if acquiredSignal.Sender != "org.freedesktop.DBus" || acquiredSignal.Name != "org.freedesktop.DBus.NameAcquired" { |
| t.Errorf("expected NameAcquired signal, got %v", acquiredSignal) |
| } |
| acquiredName := acquiredSignal.Body[0].(string) |
| if acquiredName != name { |
| t.Errorf("unexpected NameAcquired arguments: %v", acquiredSignal) |
| } |
| |
| if changedSignal.Sender != "org.freedesktop.DBus" || changedSignal.Name != "org.freedesktop.DBus.NameOwnerChanged" { |
| t.Errorf("expected NameOwnerChanged signal, got %v", changedSignal) |
| } |
| |
| changedName := changedSignal.Body[0].(string) |
| oldOwner := changedSignal.Body[1].(string) |
| newOwner := changedSignal.Body[2].(string) |
| if changedName != name || oldOwner != "" || newOwner != owner { |
| t.Errorf("unexpected NameOwnerChanged arguments: %v", changedSignal) |
| } |
| |
| err = busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&ret) |
| if err != nil { |
| t.Errorf("expected success, got %v", err) |
| } |
| if ret != DBusReleaseNameReplyReleased { |
| t.Errorf("expected %v, got %v", DBusReleaseNameReplyReleased, ret) |
| } |
| |
| sig1 = <-sigchan |
| sig2 = <-sigchan |
| if sig1.Name == "org.freedesktop.DBus.NameOwnerChanged" { |
| changedSignal = sig1 |
| lostSignal = sig2 |
| } else { |
| lostSignal = sig1 |
| changedSignal = sig2 |
| } |
| |
| if lostSignal.Sender != "org.freedesktop.DBus" || lostSignal.Name != "org.freedesktop.DBus.NameLost" { |
| t.Errorf("expected NameLost signal, got %v", lostSignal) |
| } |
| lostName := lostSignal.Body[0].(string) |
| if lostName != name { |
| t.Errorf("unexpected NameLost arguments: %v", lostSignal) |
| } |
| |
| if changedSignal.Sender != "org.freedesktop.DBus" || changedSignal.Name != "org.freedesktop.DBus.NameOwnerChanged" { |
| t.Errorf("expected NameOwnerChanged signal, got %v", changedSignal) |
| } |
| |
| changedName = changedSignal.Body[0].(string) |
| oldOwner = changedSignal.Body[1].(string) |
| newOwner = changedSignal.Body[2].(string) |
| if changedName != name || oldOwner != owner || newOwner != "" { |
| t.Errorf("unexpected NameOwnerChanged arguments: %v", changedSignal) |
| } |
| |
| if len(sigchan) != 0 { |
| t.Errorf("unexpected extra signals (%d)", len(sigchan)) |
| } |
| |
| // Unregister sigchan |
| bus.Signal(sigchan) |
| } |
| |
| func TestRealDBus(t *testing.T) { |
| dbus := New() |
| doDBusTest(t, dbus, true) |
| } |
| |
| func TestFakeDBus(t *testing.T) { |
| uniqueName := ":1.1" |
| ownedName := "" |
| |
| fakeSystem := NewFakeConnection() |
| fakeSystem.SetBusObject( |
| func(method string, args ...interface{}) ([]interface{}, error) { |
| if method == "org.freedesktop.DBus.GetId" { |
| return []interface{}{"foo"}, nil |
| } |
| return nil, fmt.Errorf("unexpected method call '%s'", method) |
| }, |
| ) |
| |
| fakeSession := NewFakeConnection() |
| fakeSession.SetBusObject( |
| func(method string, args ...interface{}) ([]interface{}, error) { |
| if method == "org.freedesktop.DBus.GetNameOwner" { |
| checkName := args[0].(string) |
| if checkName != ownedName { |
| return nil, godbus.Error{Name: "org.freedesktop.DBus.Error.NameHasNoOwner", Body: nil} |
| } |
| return []interface{}{uniqueName}, nil |
| } else if method == "org.freedesktop.DBus.RequestName" { |
| reqName := args[0].(string) |
| _ = args[1].(uint32) |
| if ownedName != "" { |
| return []interface{}{DBusRequestNameReplyAlreadyOwner}, nil |
| } |
| ownedName = reqName |
| fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameAcquired", reqName) |
| fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", reqName, "", uniqueName) |
| return []interface{}{DBusRequestNameReplyPrimaryOwner}, nil |
| } else if method == "org.freedesktop.DBus.ReleaseName" { |
| reqName := args[0].(string) |
| if reqName != ownedName { |
| return []interface{}{DBusReleaseNameReplyNotOwner}, nil |
| } |
| ownedName = "" |
| fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", reqName, uniqueName, "") |
| fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameLost", reqName) |
| return []interface{}{DBusReleaseNameReplyReleased}, nil |
| } else if method == "org.freedesktop.DBus.AddMatch" { |
| return nil, nil |
| } else { |
| return nil, fmt.Errorf("unexpected method call '%s'", method) |
| } |
| }, |
| ) |
| |
| dbus := NewFake(fakeSystem, fakeSession) |
| doDBusTest(t, dbus, false) |
| } |