@@ -245,9 +245,162 @@ func Test_Creating_new_connection_with_custom_connection(t *testing.T) {
245245 })
246246}
247247
248+ func Test_Subscribing_to_signals (t * testing.T ) {
249+ t .Parallel ()
250+
251+ ctx := context .Background ()
252+
253+ t .Run ("subscribes_to" , func (t * testing.T ) {
254+ t .Parallel ()
255+
256+ t .Run ("login1_interface" , func (t * testing.T ) {
257+ t .Parallel ()
258+
259+ addMatchCalled := false
260+
261+ connectionWithInterfaceCheck := & mockConnection {
262+ AddMatchSignalContextF : func (ctx context.Context , options ... dbus.MatchOption ) error {
263+ addMatchCalled = true
264+ if len (options ) < 2 {
265+ t .Fatalf ("Expected at least 2 match options (interface and member)" )
266+ }
267+ return nil
268+ },
269+ }
270+
271+ conn , err := login1 .NewWithConnection (connectionWithInterfaceCheck )
272+ if err != nil {
273+ t .Fatalf ("Unexpected error creating connection: %v" , err )
274+ }
275+
276+ if _ , err := conn .SubscribeWithContext (ctx , "SessionNew" ); err != nil {
277+ t .Fatalf ("Unexpected error subscribing to signals: %v" , err )
278+ }
279+
280+ if ! addMatchCalled {
281+ t .Fatalf ("Expected AddMatchSignalContext to be called" )
282+ }
283+ })
284+
285+ t .Run ("for_all_given_members" , func (t * testing.T ) {
286+ t .Parallel ()
287+
288+ callCount := 0
289+
290+ connectionWithMemberCheck := & mockConnection {
291+ AddMatchSignalContextF : func (ctx context.Context , options ... dbus.MatchOption ) error {
292+ callCount ++
293+ return nil
294+ },
295+ }
296+
297+ conn , err := login1 .NewWithConnection (connectionWithMemberCheck )
298+ if err != nil {
299+ t .Fatalf ("Unexpected error creating connection: %v" , err )
300+ }
301+
302+ expectedMembers := []string {"SessionNew" , "SessionRemoved" , "UserNew" }
303+ if _ , err := conn .SubscribeWithContext (ctx , expectedMembers ... ); err != nil {
304+ t .Fatalf ("Unexpected error subscribing to signals: %v" , err )
305+ }
306+
307+ if callCount != len (expectedMembers ) {
308+ t .Fatalf ("Expected AddMatchSignalContext to be called %d times, got %d" , len (expectedMembers ), callCount )
309+ }
310+ })
311+ })
312+
313+ t .Run ("passes_received_signals_to_channel" , func (t * testing.T ) {
314+ t .Parallel ()
315+
316+ signalChannelProvided := false
317+
318+ connectionWithSignalCheck := & mockConnection {
319+ SignalF : func (ch chan <- * dbus.Signal ) {
320+ signalChannelProvided = ch != nil
321+ // Send a test signal to verify the channel works
322+ go func () {
323+ ch <- & dbus.Signal {
324+ Sender : "org.freedesktop.login1" ,
325+ Path : "/org/freedesktop/login1" ,
326+ Name : "org.freedesktop.login1.Manager.SessionNew" ,
327+ Body : []any {"session1" , dbus .ObjectPath ("/org/freedesktop/login1/session/session1" )},
328+ }
329+ }()
330+ },
331+ }
332+
333+ conn , err := login1 .NewWithConnection (connectionWithSignalCheck )
334+ if err != nil {
335+ t .Fatalf ("Unexpected error creating connection: %v" , err )
336+ }
337+
338+ ch , err := conn .SubscribeWithContext (ctx , "SessionNew" )
339+ if err != nil {
340+ t .Fatalf ("Unexpected error subscribing to signals: %v" , err )
341+ }
342+
343+ if ch == nil {
344+ t .Fatalf ("Expected signal channel to be returned" )
345+ }
346+
347+ if ! signalChannelProvided {
348+ t .Fatalf ("Expected signal channel to be passed to connection" )
349+ }
350+
351+ // Verify we can receive signals through the channel
352+ ctx , cancel := context .WithTimeout (ctx , time .Second * 3 )
353+ defer cancel ()
354+
355+ select {
356+ case sig := <- ch :
357+ if sig == nil {
358+ t .Fatalf ("Received nil signal" )
359+ }
360+ if sig .Name != "org.freedesktop.login1.Manager.SessionNew" {
361+ t .Fatalf ("Expected signal name %q, got %q" , "org.freedesktop.login1.Manager.SessionNew" , sig .Name )
362+ }
363+ case <- ctx .Done ():
364+ t .Fatalf ("Timeout waiting for signal" )
365+ }
366+ })
367+
368+ t .Run ("returns_error_when_adding_match_signal_fails" , func (t * testing.T ) {
369+ t .Parallel ()
370+
371+ expectedError := fmt .Errorf ("failed to add match" )
372+
373+ connectionWithError := & mockConnection {
374+ AddMatchSignalContextF : func (ctx context.Context , options ... dbus.MatchOption ) error {
375+ return expectedError
376+ },
377+ }
378+
379+ conn , err := login1 .NewWithConnection (connectionWithError )
380+ if err != nil {
381+ t .Fatalf ("Unexpected error creating connection: %v" , err )
382+ }
383+
384+ _ , err = conn .SubscribeWithContext (ctx , "SessionNew" )
385+ if err == nil {
386+ t .Fatalf ("Expected error when adding match signal fails" )
387+ }
388+ })
389+ }
390+
248391// mockConnection is a test helper for mocking dbus.Conn.
249392type mockConnection struct {
250- ObjectF func (string , dbus.ObjectPath ) dbus.BusObject
393+ ObjectF func (string , dbus.ObjectPath ) dbus.BusObject
394+ AddMatchSignalContextF func (context.Context , ... dbus.MatchOption ) error
395+ SignalF func (chan <- * dbus.Signal )
396+ }
397+
398+ // AddMatchSignalContext ...
399+ func (m * mockConnection ) AddMatchSignalContext (ctx context.Context , options ... dbus.MatchOption ) error {
400+ if m .AddMatchSignalContextF != nil {
401+ return m .AddMatchSignalContextF (ctx , options ... )
402+ }
403+ return nil
251404}
252405
253406// Auth ...
@@ -261,7 +414,11 @@ func (m *mockConnection) Hello() error {
261414}
262415
263416// Signal ...
264- func (m * mockConnection ) Signal (ch chan <- * dbus.Signal ) {}
417+ func (m * mockConnection ) Signal (ch chan <- * dbus.Signal ) {
418+ if m .SignalF != nil {
419+ m .SignalF (ch )
420+ }
421+ }
265422
266423// Object ...
267424func (m * mockConnection ) Object (dest string , path dbus.ObjectPath ) dbus.BusObject {
@@ -277,11 +434,6 @@ func (m *mockConnection) Close() error {
277434 return nil
278435}
279436
280- // BusObject ...
281- func (m * mockConnection ) BusObject () dbus.BusObject {
282- return nil
283- }
284-
285437// Connected ...
286438func (m * mockConnection ) Connected () bool {
287439 return true
0 commit comments