Skip to content

Commit 04c09ee

Browse files
committed
login1: Add NewWithConnection method
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 <[email protected]>
1 parent cbb9e21 commit 04c09ee

File tree

2 files changed

+137
-9
lines changed

2 files changed

+137
-9
lines changed

login1/dbus.go

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,33 @@ const (
3232

3333
// Conn is a connection to systemds dbus endpoint.
3434
type Conn struct {
35-
conn *dbus.Conn
36-
object dbus.BusObject
35+
conn Connection
36+
connManager connectionManager
37+
object Caller
38+
}
39+
40+
// Objector describes functionality required from a given D-Bus connection.
41+
type Connection interface {
42+
Object(string, dbus.ObjectPath) dbus.BusObject
43+
Signal(ch chan<- *dbus.Signal)
44+
// TODO: This should be replaced with AddMatchSignal.
45+
// See https://github.com/coreos/go-systemd/issues/388 for details.
46+
BusObject() dbus.BusObject
47+
}
48+
49+
// ConnectionManager explicitly wraps dependencies on established D-Bus connection.
50+
type connectionManager interface {
51+
Hello() error
52+
Auth(authMethods []dbus.Auth) error
53+
Close() error
54+
55+
Connection
56+
}
57+
58+
// Caller describes required functionality from D-Bus object.
59+
type Caller interface {
60+
// TODO: This method should eventually be removed, as it provides no context support.
61+
Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call
3762
}
3863

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

75+
// NewWithConnection creates new login1 client using given D-Bus connection.
76+
func NewWithConnection(connection Connection) (*Conn, error) {
77+
if connection == nil {
78+
return nil, fmt.Errorf("no connection given")
79+
}
80+
81+
return &Conn{
82+
conn: connection,
83+
object: connection.Object(dbusDest, dbusPath),
84+
}, nil
85+
}
86+
5087
// Close closes the dbus connection
5188
func (c *Conn) Close() {
5289
if c == nil {
5390
return
5491
}
5592

56-
if c.conn != nil {
57-
c.conn.Close()
93+
if c.conn != nil && c.connManager != nil {
94+
c.connManager.Close()
5895
}
5996
}
6097

6198
func (c *Conn) initConnection() error {
6299
var err error
63-
c.conn, err = dbus.SystemBusPrivate()
100+
c.connManager, err = dbus.SystemBusPrivate()
64101
if err != nil {
65102
return err
66103
}
@@ -70,18 +107,19 @@ func (c *Conn) initConnection() error {
70107
// libc)
71108
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
72109

73-
err = c.conn.Auth(methods)
110+
err = c.connManager.Auth(methods)
74111
if err != nil {
75-
c.conn.Close()
112+
c.connManager.Close()
76113
return err
77114
}
78115

79-
err = c.conn.Hello()
116+
err = c.connManager.Hello()
80117
if err != nil {
81-
c.conn.Close()
118+
c.connManager.Close()
82119
return err
83120
}
84121

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

87125
return nil

login1/dbus_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"regexp"
2121
"testing"
2222

23+
"github.com/godbus/dbus/v5"
24+
2325
"github.com/coreos/go-systemd/v22/login1"
2426
)
2527

@@ -88,3 +90,91 @@ func TestListUsers(t *testing.T) {
8890
}
8991
}
9092
}
93+
94+
func Test_Creating_new_connection_with_custom_connection(t *testing.T) {
95+
t.Parallel()
96+
97+
t.Run("connects_to_global_login1_path_and_interface", func(t *testing.T) {
98+
t.Parallel()
99+
100+
objectConstructorCalled := false
101+
102+
connectionWithContextCheck := &mockConnection{
103+
ObjectF: func(dest string, path dbus.ObjectPath) dbus.BusObject {
104+
objectConstructorCalled = true
105+
106+
expectedDest := "org.freedesktop.login1"
107+
108+
if dest != expectedDest {
109+
t.Fatalf("Expected D-Bus destination %q, got %q", expectedDest, dest)
110+
}
111+
112+
expectedPath := dbus.ObjectPath("/org/freedesktop/login1")
113+
114+
if path != expectedPath {
115+
t.Fatalf("Expected D-Bus path %q, got %q", expectedPath, path)
116+
}
117+
118+
return nil
119+
},
120+
}
121+
122+
if _, err := login1.NewWithConnection(connectionWithContextCheck); err != nil {
123+
t.Fatalf("Unexpected error creating connection: %v", err)
124+
}
125+
126+
if !objectConstructorCalled {
127+
t.Fatalf("Expected object constructor to be called")
128+
}
129+
})
130+
131+
t.Run("returns_error_when_no_custom_connection_is_given", func(t *testing.T) {
132+
t.Parallel()
133+
134+
testConn, err := login1.NewWithConnection(nil)
135+
if err == nil {
136+
t.Fatalf("Expected error creating connection with no connector")
137+
}
138+
139+
if testConn != nil {
140+
t.Fatalf("Expected connection to be nil when New returns error")
141+
}
142+
})
143+
}
144+
145+
// mockConnection is a test helper for mocking dbus.Conn.
146+
type mockConnection struct {
147+
ObjectF func(string, dbus.ObjectPath) dbus.BusObject
148+
}
149+
150+
// Auth ...
151+
func (m *mockConnection) Auth(authMethods []dbus.Auth) error {
152+
return nil
153+
}
154+
155+
// Hello ...
156+
func (m *mockConnection) Hello() error {
157+
return nil
158+
}
159+
160+
// Signal ...
161+
func (m *mockConnection) Signal(ch chan<- *dbus.Signal) {}
162+
163+
// Object ...
164+
func (m *mockConnection) Object(dest string, path dbus.ObjectPath) dbus.BusObject {
165+
if m.ObjectF == nil {
166+
return nil
167+
}
168+
169+
return m.ObjectF(dest, path)
170+
}
171+
172+
// Close ...
173+
func (m *mockConnection) Close() error {
174+
return nil
175+
}
176+
177+
// BusObject ...
178+
func (m *mockConnection) BusObject() dbus.BusObject {
179+
return nil
180+
}

0 commit comments

Comments
 (0)