portal_inhibitor_test.go RawHistoryFolder
1package idle
2
3import (
4 "bufio"
5 "bytes"
6 "context"
7 "os/exec"
8 "strings"
9 "sync"
10 "testing"
11
12 "github.com/godbus/dbus/v5"
13)
14
15func TestWatchForResponseCleanupSurvivesConnFieldCleared(t *testing.T) {
16 // clearConnOnDoneContext clears inhibitor.conn after watchForResponse has
17 // installed its cleanup defer; the test fails if cleanup dereferences the
18 // mutable field instead of the D-Bus connection snapshot.
19 conn := connectPrivateSessionBus(t)
20 defer conn.Close()
21
22 inhibitor := &PortalInhibitor{conn: conn}
23 ctx := &clearConnOnDoneContext{
24 Context: context.Background(),
25 done: closedDoneChannel(),
26 clear: func() {
27 inhibitor.mu.Lock()
28 inhibitor.conn = nil
29 inhibitor.mu.Unlock()
30 },
31 }
32
33 inhibitor.watchForResponse(ctx, "/org/freedesktop/portal/desktop/request/dumber/test")
34
35 inhibitor.mu.Lock()
36 connCleared := inhibitor.conn == nil
37 inhibitor.mu.Unlock()
38 if !connCleared {
39 t.Fatal("watchForResponse returned before observing context cancellation")
40 }
41}
42
43func connectPrivateSessionBus(t *testing.T) *dbus.Conn {
44 t.Helper()
45
46 address := startPrivateSessionBus(t)
47 conn, err := dbus.Connect(address)
48 if err != nil {
49 t.Fatalf("connect to private D-Bus session bus: %v", err)
50 }
51 return conn
52}
53
54func startPrivateSessionBus(t *testing.T) string {
55 t.Helper()
56
57 if _, err := exec.LookPath("dbus-daemon"); err != nil {
58 t.Skipf("dbus-daemon unavailable: %v", err)
59 }
60
61 var stderr bytes.Buffer
62 cmd := exec.Command("dbus-daemon", "--session", "--nofork", "--print-address=1")
63 cmd.Stderr = &stderr
64
65 stdout, err := cmd.StdoutPipe()
66 if err != nil {
67 t.Fatalf("open dbus-daemon stdout: %v", err)
68 }
69 err = cmd.Start()
70 if err != nil {
71 t.Fatalf("start dbus-daemon: %v", err)
72 }
73
74 t.Cleanup(func() {
75 if cmd.Process != nil {
76 _ = cmd.Process.Kill()
77 }
78 _ = cmd.Wait()
79 })
80
81 address, err := bufio.NewReader(stdout).ReadString('\n')
82 if err != nil {
83 t.Fatalf("read dbus-daemon address: %v; stderr: %s", err, stderr.String())
84 }
85 return strings.TrimSpace(address)
86}
87
88type clearConnOnDoneContext struct {
89 context.Context
90 done <-chan struct{}
91 once sync.Once
92 clear func()
93}
94
95func (c *clearConnOnDoneContext) Done() <-chan struct{} {
96 c.once.Do(c.clear)
97 return c.done
98}
99
100func closedDoneChannel() <-chan struct{} {
101 done := make(chan struct{})
102 close(done)
103 return done
104}