v0.33.0 62a09f5

portal_inhibitor_test.go RawHistoryFolder

1package idle
2
3import (
4	"context"
5	"errors"
6	"testing"
7
8	"github.com/bnema/dumber/internal/infrastructure/idle/mocks"
9	"github.com/stretchr/testify/require"
10)
11
12const testHandle = "/org/freedesktop/portal/desktop/request/dumber/test"
13
14var errPortal = errors.New("portal error")
15
16// newTestInhibitor returns an inhibitor whose portal answered Version.
17func newTestInhibitor(t *testing.T) (*PortalInhibitor, *mocks.MockPortalClient) {
18	t.Helper()
19	portal := mocks.NewMockPortalClient(t)
20	portal.EXPECT().Version().Return(uint32(3), nil).Once()
21	return newPortalInhibitor(context.Background(), portal), portal
22}
23
24func TestNewPortalInhibitorClosesUnavailablePortal(t *testing.T) {
25	portal := mocks.NewMockPortalClient(t)
26	portal.EXPECT().Version().Return(uint32(0), errPortal).Once()
27	portal.EXPECT().Close().Return(nil).Once()
28
29	p := newPortalInhibitor(context.Background(), portal)
30	require.Nil(t, p.portal)
31
32	// Without a portal, the refcount still works and makes no portal call.
33	ctx := context.Background()
34	require.NoError(t, p.Inhibit(ctx, "video playing"))
35	require.True(t, p.IsInhibited())
36	require.NoError(t, p.Uninhibit(ctx))
37	require.False(t, p.IsInhibited())
38	require.NoError(t, p.Close())
39}
40
41func TestPortalInhibitorRefcountsOneRequest(t *testing.T) {
42	p, portal := newTestInhibitor(t)
43	ctx := context.Background()
44	portal.EXPECT().Inhibit("video playing").Return(testHandle, nil).Once()
45
46	require.NoError(t, p.Inhibit(ctx, "video playing"))
47	require.NoError(t, p.Inhibit(ctx, "second video"))
48	require.NoError(t, p.Uninhibit(ctx))
49	require.True(t, p.IsInhibited(), "released while one inhibit is still held")
50
51	portal.EXPECT().CloseRequest(testHandle).Return(nil).Once()
52	require.NoError(t, p.Uninhibit(ctx))
53	require.False(t, p.IsInhibited())
54
55	// Extra Uninhibit calls are no-ops.
56	require.NoError(t, p.Uninhibit(ctx))
57}
58
59func TestPortalInhibitorIgnoresCloseRequestError(t *testing.T) {
60	// A portal that already ended the request answers Close with an error.
61	p, portal := newTestInhibitor(t)
62	ctx := context.Background()
63	portal.EXPECT().Inhibit("video playing").Return(testHandle, nil).Once()
64	portal.EXPECT().CloseRequest(testHandle).Return(errPortal).Once()
65
66	require.NoError(t, p.Inhibit(ctx, "video playing"))
67	require.NoError(t, p.Uninhibit(ctx))
68
69	// The next inhibition starts a new request.
70	portal.EXPECT().Inhibit("next video").Return(testHandle, nil).Once()
71	require.NoError(t, p.Inhibit(ctx, "next video"))
72}
73
74func TestPortalInhibitorRollsBackFailedInhibit(t *testing.T) {
75	p, portal := newTestInhibitor(t)
76	ctx := context.Background()
77	portal.EXPECT().Inhibit("video playing").Return("", errPortal).Once()
78
79	require.ErrorIs(t, p.Inhibit(ctx, "video playing"), errPortal)
80	require.False(t, p.IsInhibited())
81
82	// Nothing to release: no CloseRequest expected.
83	require.NoError(t, p.Uninhibit(ctx))
84}
85
86func TestPortalInhibitorClose(t *testing.T) {
87	tests := []struct {
88		name     string
89		inhibit  bool
90		closeErr error
91	}{
92		{name: "releases active request", inhibit: true},
93		{name: "without active request"},
94		{name: "returns connection close error once", closeErr: errPortal},
95	}
96	for _, tt := range tests {
97		t.Run(tt.name, func(t *testing.T) {
98			p, portal := newTestInhibitor(t)
99			if tt.inhibit {
100				portal.EXPECT().Inhibit("video playing").Return(testHandle, nil).Once()
101				portal.EXPECT().CloseRequest(testHandle).Return(nil).Once()
102				require.NoError(t, p.Inhibit(context.Background(), "video playing"))
103			}
104			portal.EXPECT().Close().Return(tt.closeErr).Once()
105
106			require.ErrorIs(t, p.Close(), tt.closeErr)
107			require.False(t, p.IsInhibited())
108			// Close is idempotent.
109			require.NoError(t, p.Close())
110		})
111	}
112}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close