Skip to content

Commit

Permalink
login1: Add NewWithConnection method
Browse files Browse the repository at this point in the history
This method allows passing existing D-Bus connection, which allows to
re-use connection between clients and to mock D-Bus connection for
testing purposes.

Signed-off-by: Mateusz Gozdek <mgozdek@microsoft.com>
  • Loading branch information
invidian committed Jan 10, 2022
1 parent c3a73b1 commit 9a42b6b
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 9 deletions.
56 changes: 47 additions & 9 deletions login1/dbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,33 @@ const (

// Conn is a connection to systemds dbus endpoint.
type Conn struct {
conn *dbus.Conn
object dbus.BusObject
conn Connection
connManager connectionManager
object Caller
}

// Objector describes functionality required from a given D-Bus connection.
type Connection interface {
Object(string, dbus.ObjectPath) dbus.BusObject
Signal(ch chan<- *dbus.Signal)
// TODO: This should be replaced with AddMatchSignal.
// See https://github.com/coreos/go-systemd/issues/388 for details.
BusObject() dbus.BusObject
}

// ConnectionManager explicitly wraps dependencies on established D-Bus connection.
type connectionManager interface {
Hello() error
Auth(authMethods []dbus.Auth) error
Close() error

Connection
}

// Caller describes required functionality from D-Bus object.
type Caller interface {
// TODO: This method should eventually be removed, as it provides no context support.
Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call
}

// New establishes a connection to the system bus and authenticates.
Expand All @@ -47,20 +72,32 @@ func New() (*Conn, error) {
return c, nil
}

// NewWithConnection creates new login1 client using given D-Bus connection.
func NewWithConnection(connection Connection) (*Conn, error) {
if connection == nil {
return nil, fmt.Errorf("no connection given")
}

return &Conn{
conn: connection,
object: connection.Object(dbusDest, dbusPath),
}, nil
}

// Close closes the dbus connection
func (c *Conn) Close() {
if c == nil {
return
}

if c.conn != nil {
c.conn.Close()
if c.conn != nil && c.connManager != nil {
c.connManager.Close()
}
}

func (c *Conn) initConnection() error {
var err error
c.conn, err = dbus.SystemBusPrivate()
c.connManager, err = dbus.SystemBusPrivate()
if err != nil {
return err
}
Expand All @@ -70,18 +107,19 @@ func (c *Conn) initConnection() error {
// libc)
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}

err = c.conn.Auth(methods)
err = c.connManager.Auth(methods)
if err != nil {
c.conn.Close()
c.connManager.Close()
return err
}

err = c.conn.Hello()
err = c.connManager.Hello()
if err != nil {
c.conn.Close()
c.connManager.Close()
return err
}

c.conn = c.connManager
c.object = c.conn.Object("org.freedesktop.login1", dbus.ObjectPath(dbusPath))

return nil
Expand Down
90 changes: 90 additions & 0 deletions login1/dbus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"regexp"
"testing"

"github.com/godbus/dbus/v5"

"github.com/coreos/go-systemd/v22/login1"
)

Expand Down Expand Up @@ -88,3 +90,91 @@ func TestListUsers(t *testing.T) {
}
}
}

func Test_Creating_new_connection_with_custom_connection(t *testing.T) {
t.Parallel()

t.Run("connects_to_global_login1_path_and_interface", func(t *testing.T) {
t.Parallel()

objectConstructorCalled := false

connectionWithContextCheck := &mockConnection{
ObjectF: func(dest string, path dbus.ObjectPath) dbus.BusObject {
objectConstructorCalled = true

expectedDest := "org.freedesktop.login1"

if dest != expectedDest {
t.Fatalf("Expected D-Bus destination %q, got %q", expectedDest, dest)
}

expectedPath := dbus.ObjectPath("/org/freedesktop/login1")

if path != expectedPath {
t.Fatalf("Expected D-Bus path %q, got %q", expectedPath, path)
}

return nil
},
}

if _, err := login1.NewWithConnection(connectionWithContextCheck); err != nil {
t.Fatalf("Unexpected error creating connection: %v", err)
}

if !objectConstructorCalled {
t.Fatalf("Expected object constructor to be called")
}
})

t.Run("returns_error_when_no_custom_connection_is_given", func(t *testing.T) {
t.Parallel()

testConn, err := login1.NewWithConnection(nil)
if err == nil {
t.Fatalf("Expected error creating connection with no connector")
}

if testConn != nil {
t.Fatalf("Expected connection to be nil when New returns error")
}
})
}

// mockConnection is a test helper for mocking dbus.Conn.
type mockConnection struct {
ObjectF func(string, dbus.ObjectPath) dbus.BusObject
}

// Auth ...
func (m *mockConnection) Auth(authMethods []dbus.Auth) error {
return nil
}

// Hello ...
func (m *mockConnection) Hello() error {
return nil
}

// Signal ...
func (m *mockConnection) Signal(ch chan<- *dbus.Signal) {}

// Object ...
func (m *mockConnection) Object(dest string, path dbus.ObjectPath) dbus.BusObject {
if m.ObjectF == nil {
return nil
}

return m.ObjectF(dest, path)
}

// Close ...
func (m *mockConnection) Close() error {
return nil
}

// BusObject ...
func (m *mockConnection) BusObject() dbus.BusObject {
return nil
}

0 comments on commit 9a42b6b

Please sign in to comment.