v0.1.0 f503a24

headless_surface_test.go RawHistoryFolder

1package neferclient_test
2
3import (
4	"context"
5	"testing"
6	"time"
7
8	"github.com/stretchr/testify/mock"
9	"github.com/stretchr/testify/require"
10
11	"github.com/bnema/neferclient"
12	neferclientmocks "github.com/bnema/neferclient/mocks"
13)
14
15func connectHeadless(t *testing.T) *neferclient.Conn {
16	t.Helper()
17	socket := startHeadless(t)
18	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
19	defer cancel()
20	for {
21		c, err := neferclient.Connect(ctx, socket)
22		if err == nil {
23			t.Cleanup(func() { _ = c.Close() })
24			for len(c.Outputs()) == 0 { // the virtual output may arrive late
25				select {
26				case <-c.Wake():
27					require.NoError(t, c.Dispatch(nil))
28				case <-ctx.Done():
29					t.Fatal("no output announced")
30				}
31			}
32			return c
33		}
34		if ctx.Err() != nil {
35			t.Fatal(err)
36		}
37		time.Sleep(5 * time.Millisecond)
38	}
39}
40
41func TestHeadlessLayerSurface(t *testing.T) {
42	c := connectHeadless(t)
43	s, err := c.NewLayerSurface(neferclient.LayerConfig{
44		Level: neferclient.LayerTop, Anchors: neferclient.AnchorTop | neferclient.AnchorLeft,
45		Width: 200, Height: 50,
46	})
47	require.NoError(t, err)
48
49	var configured, feedback bool
50	var cw, ch int32
51	h := neferclientmocks.NewMockHandler(t)
52	h.EXPECT().Configure(s.ID(), mock.Anything, mock.Anything).Run(func(_ neferclient.SurfaceID, w, hh int32) {
53		configured, cw, ch = true, w, hh
54	}).Return().Maybe()
55	h.EXPECT().FeedbackDone(s.ID()).Run(func(neferclient.SurfaceID) { feedback = true }).Return().Maybe()
56	h.EXPECT().Scale(s.ID(), mock.Anything).Return().Maybe()
57	dispatchUntil(t, c, h, func() bool { return configured && feedback })
58
59	require.Equal(t, int32(200), cw)
60	require.Equal(t, int32(50), ch)
61	fb := s.Feedback()
62	require.NotNil(t, fb)
63	require.NotZero(t, fb.MainDevice)
64	require.NotEmpty(t, fb.Formats)
65	t.Logf("main device %#x, %d formats", fb.MainDevice, len(fb.Formats))
66	pw, ph, err := s.PhysicalSize()
67	require.NoError(t, err)
68	require.Positive(t, pw)
69	require.Positive(t, ph)
70	require.NoError(t, s.SetInputRegion([]neferclient.Rect{{X: 0, Y: 0, Width: 10, Height: 10}}))
71	require.NoError(t, s.Close())
72	require.NoError(t, c.Roundtrip())
73}
74
75func TestHeadlessLockUnlock(t *testing.T) {
76	c := connectHeadless(t)
77	l, err := c.Lock()
78	require.NoError(t, err)
79	require.Error(t, l.Unlock(), "unlock before locked must be refused")
80
81	var locked bool
82	var surfaces []*neferclient.Surface
83	for _, o := range c.Outputs() {
84		s, err := l.NewSurface(o.Global)
85		require.NoError(t, err)
86		surfaces = append(surfaces, s)
87	}
88	h := neferclientmocks.NewMockHandler(t)
89	h.EXPECT().Locked().Run(func() { locked = true }).Return().Maybe()
90	h.EXPECT().Configure(mock.Anything, mock.Anything, mock.Anything).Return().Maybe()
91	h.EXPECT().FeedbackDone(mock.Anything).Return().Maybe()
92	h.EXPECT().Scale(mock.Anything, mock.Anything).Return().Maybe()
93	dispatchUntil(t, c, h, func() bool { return locked })
94	require.True(t, l.Locked())
95	for _, s := range surfaces {
96		w, hh, _ := s.Size()
97		t.Logf("lock surface %d: %dx%d", s.ID(), w, hh)
98	}
99	require.Error(t, l.Close(), "a locked session is not closed, only unlocked")
100	require.NoError(t, l.Unlock())
101	require.False(t, l.Locked())
102	for _, s := range surfaces {
103		require.NoError(t, s.Close())
104	}
105	require.NoError(t, c.Roundtrip())
106}
107
108// TestHeadlessSeat binds the seat against a real compositor and drives it
109// through a surface: the seat bind, capability handling and cursor request
110// must not fail, whatever devices the headless backend offers.
111func TestHeadlessSeat(t *testing.T) {
112	c := connectHeadless(t)
113	s, err := c.NewLayerSurface(neferclient.LayerConfig{
114		Level: neferclient.LayerTop, Anchors: neferclient.AnchorTop | neferclient.AnchorLeft,
115		Width: 100, Height: 40, Keyboard: neferclient.KeyboardOnDemand,
116	})
117	require.NoError(t, err)
118	seat := c.Seat()
119	require.NotNil(t, seat)
120	seat.SetSecret(neferclient.NewSecretBuffer(16))
121	seat.SetSecret(nil)
122
123	var configured bool
124	h := neferclientmocks.NewMockHandler(t)
125	h.EXPECT().Configure(s.ID(), mock.Anything, mock.Anything).Run(func(neferclient.SurfaceID, int32, int32) { configured = true }).Return().Maybe()
126	h.EXPECT().FeedbackDone(mock.Anything).Return().Maybe()
127	h.EXPECT().Scale(mock.Anything, mock.Anything).Return().Maybe()
128	h.EXPECT().Pointer(mock.Anything).Return().Maybe()
129	h.EXPECT().Key(mock.Anything).Return().Maybe()
130	h.EXPECT().KeyboardFocus(mock.Anything, mock.Anything).Return().Maybe()
131	h.EXPECT().SecretChanged(mock.Anything).Return().Maybe()
132	dispatchUntil(t, c, h, func() bool { return configured })
133	require.NoError(t, c.Roundtrip())
134	require.NoError(t, c.Dispatch(h))
135	require.NoError(t, seat.SetCursor(neferclient.CursorPointer))
136	require.NoError(t, s.Close())
137}

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 1

Tags 4

↑↓ moveenter openesc close