blob: 14e749991ca518103430c65dcaaac9278d6b6bf6 [file] [log] [blame]
/*
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)
}