v0.4.0 76bcd84

freeze_test.go RawHistoryFolder

1package wayland
2
3import (
4	"context"
5	"testing"
6
7	"github.com/stretchr/testify/require"
8
9	"github.com/bnema/nefercap/internal/adapters/wayland/testserver"
10	"github.com/bnema/nefercap/internal/ports"
11)
12
13// The test compositor paints the frame sequence into every pixel, so a
14// frozen capture keeps the sequence of the Freeze while live ones advance.
15func TestFreezeServesTheFrozenScreen(t *testing.T) {
16	ctx := context.Background()
17	s, srv := newSource(t, testserver.Config{Outputs: bigOutput()})
18	out := firstOutput(t, s)
19	frozen, err := s.Freeze(ctx, []uint32{out.ID})
20	require.NoError(t, err)
21	f := frozen[out.ID]
22	require.NoError(t, f.Validate())
23	require.Equal(t, [3]int{64, 48, 64 * 4}, [3]int{f.Width, f.Height, f.Stride})
24	captures := srv.Stats().Captures
25
26	whole, err := s.Capture(ctx, ports.Target{OutputID: out.ID})
27	require.NoError(t, err)
28	want := testserver.Pixel(5, 3, 1)
29	require.Equal(t, want[:], whole.Row(3)[5*4:6*4])
30
31	region, err := s.Capture(ctx, ports.Target{OutputID: out.ID, Region: ports.Region{X: 10, Y: 5, Width: 20, Height: 8}})
32	require.NoError(t, err)
33	require.Equal(t, [2]int{20, 8}, [2]int{region.Width, region.Height})
34	want = testserver.Pixel(10, 5, 1)
35	require.Equal(t, want[:], region.Row(0)[:4])
36	require.Equal(t, captures, srv.Stats().Captures, "frozen captures read no new frame")
37
38	s.Thaw()
39	live, err := s.Capture(ctx, ports.Target{OutputID: out.ID})
40	require.NoError(t, err)
41	want = testserver.Pixel(5, 3, 2)
42	require.Equal(t, want[:], live.Row(3)[5*4:6*4], "Thaw captures the live screen again")
43}
44
45// Freeze ends the live session and frees the shared buffers: the compositor
46// stops streaming and only the frozen copy stays in memory.
47func TestFreezeReleasesLiveResources(t *testing.T) {
48	s, srv := newSource(t, testserver.Config{Outputs: bigOutput()})
49	_, err := s.Freeze(context.Background(), []uint32{firstOutput(t, s).ID})
50	require.NoError(t, err)
51	require.NoError(t, s.roundtrip())
52	st := srv.Stats()
53	require.Equal(t, st.Sessions, st.DestroyedSessions)
54	require.Equal(t, st.Buffers, st.DestroyedBuffers)
55	require.Nil(t, s.bufs[0].data)
56}
57
58// A rotated output cannot be frozen: it stays live instead of failing the
59// selector, and its capture reports the transform as before.
60func TestFreezeLeavesRotatedOutputsLive(t *testing.T) {
61	s, _ := newSource(t, testserver.Config{Transform: 1})
62	out := firstOutput(t, s)
63	frozen, err := s.Freeze(context.Background(), []uint32{out.ID})
64	require.NoError(t, err)
65	require.Empty(t, frozen)
66	_, err = s.Capture(context.Background(), ports.Target{OutputID: out.ID})
67	require.ErrorIs(t, err, ports.ErrUnsupportedTransform)
68}
69
70// Thaw keeps the copies of the listed outputs; the others capture live.
71func TestThawKeepsListedOutputs(t *testing.T) {
72	ctx := context.Background()
73	s, _ := newSource(t, testserver.Config{Outputs: []testserver.OutputSpec{
74		{Name: "A-1", Width: 8, Height: 4, Scale: 1}, {Name: "B-2", Width: 8, Height: 4, Scale: 1},
75	}})
76	outs, err := s.Outputs(ctx)
77	require.NoError(t, err)
78	_, err = s.Freeze(ctx, []uint32{outs[0].ID, outs[1].ID}) // frames 1 and 2
79	require.NoError(t, err)
80	s.Thaw(outs[0].ID)
81	kept, err := s.Capture(ctx, ports.Target{OutputID: outs[0].ID})
82	require.NoError(t, err)
83	want := testserver.Pixel(0, 0, 1)
84	require.Equal(t, want[:], kept.Row(0)[:4])
85	live, err := s.Capture(ctx, ports.Target{OutputID: outs[1].ID})
86	require.NoError(t, err)
87	want = testserver.Pixel(0, 0, 3)
88	require.Equal(t, want[:], live.Row(0)[:4])
89}
90
91func TestCloseDropsFrozenFrames(t *testing.T) {
92	s, _ := newSource(t, testserver.Config{})
93	out := firstOutput(t, s)
94	_, err := s.Freeze(context.Background(), []uint32{out.ID})
95	require.NoError(t, err)
96	require.NoError(t, s.Close())
97	require.Nil(t, s.frozen)
98	_, err = s.Capture(context.Background(), ports.Target{OutputID: out.ID})
99	require.ErrorIs(t, err, ports.ErrClosed)
100}
101
102func TestFreezeWorkspaceIsItsRegionOfTheFrozenOutput(t *testing.T) {
103	cfg := wsConfig(testserver.WorkspaceSpec{ID: "7f3a-2", Name: "b", Output: "TEST-1", Active: true})
104	cfg.NeferwlSource = true
105	s, srv := newSource(t, cfg)
106	srv.SetFrame("b", [4]int32{2, 1, 4, 2})
107	ws := listWorkspaces(t, s)
108	require.Len(t, ws, 1)
109	out := firstOutput(t, s)
110	_, err := s.Freeze(context.Background(), []uint32{out.ID})
111	require.NoError(t, err)
112	f, err := s.Capture(context.Background(), ports.Target{OutputID: out.ID, WorkspaceID: ws[0].ID})
113	require.NoError(t, err)
114	require.Equal(t, [2]int{4, 2}, [2]int{f.Width, f.Height})
115	want := testserver.Pixel(2, 1, 1)
116	require.Equal(t, want[:], f.Row(0)[:4])
117	require.Equal(t, "output", srv.Stats().LastSource, "a frozen workspace is cut from the output frame")
118
119	_, err = s.Capture(context.Background(), ports.Target{OutputID: out.ID, WorkspaceID: 99})
120	require.ErrorIs(t, err, ports.ErrWorkspaceUnavailable)
121}
122
123// The copy shows the workspace that was displayed: a hidden one is
124// unavailable, as in a live capture without NeferWL's extension.
125func TestFreezeHiddenWorkspaceIsUnavailable(t *testing.T) {
126	s, _ := newSource(t, wsConfig(
127		testserver.WorkspaceSpec{ID: "1", Name: "a", Output: "TEST-1", Active: true},
128		testserver.WorkspaceSpec{ID: "2", Name: "b", Output: "TEST-1"},
129	))
130	ws := listWorkspaces(t, s)
131	require.Len(t, ws, 2)
132	out := firstOutput(t, s)
133	_, err := s.Freeze(context.Background(), []uint32{out.ID})
134	require.NoError(t, err)
135	for _, w := range ws {
136		_, err := s.Capture(context.Background(), ports.Target{OutputID: out.ID, WorkspaceID: w.ID})
137		if w.Active {
138			require.NoError(t, err)
139		} else {
140			require.ErrorIs(t, err, ports.ErrWorkspaceUnavailable)
141		}
142	}
143}
144
145func TestFreezeUnknownOutput(t *testing.T) {
146	s, _ := newSource(t, testserver.Config{})
147	_, err := s.Freeze(context.Background(), []uint32{12345})
148	require.ErrorIs(t, err, ports.ErrOutputNotFound)
149
150	out := firstOutput(t, s)
151	_, err = s.Freeze(context.Background(), []uint32{out.ID})
152	require.NoError(t, err)
153	_, err = s.Capture(context.Background(), ports.Target{OutputID: 12345})
154	require.ErrorIs(t, err, ports.ErrOutputNotFound)
155}
156
157func TestOwnedCopyDropsStridePadding(t *testing.T) {
158	f := ports.Frame{Pixels: make([]byte, 2*16), Width: 3, Height: 2, Stride: 16, Format: ports.XRGB8888}
159	for i := range f.Pixels {
160		f.Pixels[i] = byte(i)
161	}
162	c := ownedCopy(f)
163	require.NoError(t, c.Validate())
164	require.Equal(t, 12, c.Stride)
165	require.Equal(t, f.Row(1), c.Row(1))
166	f.Pixels[16] = 0xee
167	require.NotEqual(t, f.Row(1), c.Row(1), "the copy owns its storage")
168}

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 2

Tags 9

↑↓ moveenter openesc close