feat/data-dnd e05cb15

surfaces_test.go RawHistoryFolder

1package surfaces
2
3import (
4	"os"
5	"testing"
6
7	portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
8	"github.com/bnema/neferwl/internal/ports"
9)
10
11func shows(ids ...ports.WindowID) ports.Scene {
12	s := ports.Scene{OutputWidth: 100, OutputHeight: 100}
13	for _, id := range ids {
14		s.Windows = append(s.Windows, ports.SceneWindow{ID: id, Rect: ports.Rect{W: 10, H: 10}})
15	}
16	return s
17}
18
19// An empty content of a window the scene draws keeps the previous one with
20// its Acquire fence dropped; the content goes once a scene no longer lists
21// the window.
22func TestEmptyContentKeptWhileShown(t *testing.T) {
23	tb := New()
24	r, w, err := os.Pipe()
25	if err != nil {
26		t.Fatal(err)
27	}
28	defer r.Close()
29	defer w.Close()
30	full := ports.SurfaceContent{ID: 1, Seq: 4, Width: 10, Height: 10, DMABuf: &ports.DMABuf{ID: 7}, Acquire: r}
31	tb.Update(full, shows(1))
32	tb.Update(ports.SurfaceContent{ID: 1, Seq: 5}, shows(1))
33	kept, ok := tb.Map()[1]
34	if !ok || kept.DMABuf != full.DMABuf || kept.Seq != 4 {
35		t.Fatalf("kept %+v, want the previous content", kept)
36	}
37	if kept.Acquire != nil {
38		t.Fatal("the kept content keeps its Acquire fence")
39	}
40	if !tb.Kept(1) {
41		t.Fatal("Kept false for a content drawn after its empty one")
42	}
43	// A scene still listing it keeps it; one without it drops it.
44	tb.Prune(shows(1, 2))
45	if _, ok := tb.Map()[1]; !ok {
46		t.Fatal("dropped while still listed")
47	}
48	tb.Prune(shows(2))
49	if _, ok := tb.Map()[1]; ok || tb.Kept(1) {
50		t.Fatal("not dropped once unlisted")
51	}
52}
53
54// An empty content of a window the scene does not draw drops it at once.
55func TestEmptyContentOfUnshownWindowDropped(t *testing.T) {
56	tb := New()
57	tb.Update(ports.SurfaceContent{ID: 1, SHM: &ports.SHMBuffer{Pool: 1}}, shows(1))
58	tb.Update(ports.SurfaceContent{ID: 1}, shows(2))
59	if _, ok := tb.Map()[1]; ok || tb.Kept(1) {
60		t.Fatal("content of an unshown window kept")
61	}
62	// Hidden counts as not drawn.
63	tb.Update(ports.SurfaceContent{ID: 3, SHM: &ports.SHMBuffer{Pool: 3}}, shows())
64	hidden := ports.Scene{OutputWidth: 100, OutputHeight: 100, Windows: []ports.SceneWindow{{ID: 3, Hidden: true, Rect: ports.Rect{W: 10, H: 10}}}}
65	tb.Update(ports.SurfaceContent{ID: 3}, hidden)
66	if _, ok := tb.Map()[3]; ok {
67		t.Fatal("content of a hidden window kept")
68	}
69}
70
71// A new content replaces a kept one and the window is no longer kept (the
72// client mapped again).
73func TestNewContentReplacesKept(t *testing.T) {
74	tb := New()
75	tb.Update(ports.SurfaceContent{ID: 1, Seq: 1, SHM: &ports.SHMBuffer{Pool: 1}}, shows(1))
76	tb.Update(ports.SurfaceContent{ID: 1, Seq: 2}, shows(1))
77	tb.Update(ports.SurfaceContent{ID: 1, Seq: 3, SHM: &ports.SHMBuffer{Pool: 2}}, shows(1))
78	if c := tb.Map()[1]; c.Seq != 3 || c.SHM.Pool != 2 || tb.Kept(1) {
79		t.Fatalf("after a new content: %+v kept %t", c, tb.Kept(1))
80	}
81	// The map handed out is the table's own: the renderer sees updates.
82	m := tb.Map()
83	tb.Update(ports.SurfaceContent{ID: 2, Seq: 1, SHM: &ports.SHMBuffer{Pool: 9}}, shows(1, 2))
84	if _, ok := m[2]; !ok {
85		t.Fatal("Map is a copy")
86	}
87}
88
89// Update and Prune allocate nothing in steady state.
90func TestUpdateAllocations(t *testing.T) {
91	tb := New()
92	s := shows(1, 2)
93	c := ports.SurfaceContent{ID: 1, Seq: 1, SHM: &ports.SHMBuffer{Pool: 1}}
94	tb.Update(c, s)
95	if n := testing.AllocsPerRun(100, func() {
96		c.Seq++
97		tb.Update(c, s)
98		tb.Prune(s)
99	}); n != 0 {
100		t.Fatalf("%v allocations per update", n)
101	}
102}
103
104// The kept content's children lose their Acquire fences too, on a copy:
105// wayland's own slice (shared with its publication) is left alone.
106func TestKeptChildrenFencesDroppedOnACopy(t *testing.T) {
107	tb := New()
108	r, w, err := os.Pipe()
109	if err != nil {
110		t.Fatal(err)
111	}
112	defer r.Close()
113	defer w.Close()
114	children := []ports.Subsurface{{SurfaceContent: ports.SurfaceContent{DMABuf: &ports.DMABuf{ID: 2}, Acquire: r}}}
115	full := ports.SurfaceContent{ID: 1, Seq: 1, SHM: &ports.SHMBuffer{Pool: 1}, Children: children}
116	tb.Update(full, shows(1))
117	tb.Update(ports.SurfaceContent{ID: 1, Seq: 2}, shows(1))
118	kept := tb.Map()[1]
119	if len(kept.Children) != 1 || kept.Children[0].Acquire != nil || kept.Children[0].DMABuf != children[0].DMABuf {
120		t.Fatalf("kept children %+v", kept.Children)
121	}
122	if children[0].Acquire != r {
123		t.Fatal("wayland's slice was modified")
124	}
125}
126
127// Both backends report a window shown only when the scene draws it and its
128// content is not kept.
129func TestShowsSnapshotSkipsKept(t *testing.T) {
130	kept := func(id ports.WindowID) bool { return id == 2 }
131	var snap ShowsSnapshot
132	got := snap.FromSeen(shows(1, 2), map[ports.WindowID]uint64{1: 3, 2: 4, 3: 5}, kept)
133	if len(got) != 1 || got[1] != 3 {
134		t.Fatalf("shown from seen %v", got)
135	}
136	var fromContents ShowsSnapshot
137	contents := map[ports.WindowID]ports.SurfaceContent{1: {ID: 1, Seq: 3}, 2: {ID: 2, Seq: 4}, 3: {ID: 3, Seq: 5}}
138	got = fromContents.FromContents(shows(1, 2), contents, kept)
139	if len(got) != 1 || got[1] != 3 {
140		t.Fatalf("shown from contents %v", got)
141	}
142	if !Shown(shows(2), 2, nil) {
143		t.Fatal("nil kept counts nothing as kept")
144	}
145}
146
147// An unchanged snapshot is reused without allocating; a changed one never
148// mutates a map already handed out.
149func TestShowsSnapshotImmutable(t *testing.T) {
150	var snap ShowsSnapshot
151	scene := shows(1)
152	seen := map[ports.WindowID]uint64{1: 1, 2: 2}
153	first := snap.FromSeen(scene, seen, nil)
154	if allocs := testing.AllocsPerRun(100, func() { snap.FromSeen(scene, seen, nil) }); allocs != 0 {
155		t.Errorf("unchanged shows: %.1f allocs, want 0", allocs)
156	}
157	seen[1] = 3
158	second := snap.FromSeen(scene, seen, nil)
159	if first[1] != 1 || second[1] != 3 {
160		t.Fatalf("snapshot mutated: old %v new %v", first, second)
161	}
162}
163
164// Apply takes the batch into the table, advances seen, reports what needs a
165// frame or a report, then resets the batch before giving it back.
166func TestApply(t *testing.T) {
167	tb := New()
168	seen := map[ports.WindowID]uint64{1: 5}
169	b := &ports.ContentBatch{Items: []ports.SurfaceContent{
170		{ID: 1, Seq: 4, SHM: &ports.SHMBuffer{Pool: 1}}, // older than seen
171		{ID: 2, Seq: 1, SHM: &ports.SHMBuffer{Pool: 2}},
172	}}
173	ret := portsmocks.NewMockContentReturn(t)
174	ret.EXPECT().Return(b).Run(func(got *ports.ContentBatch) {
175		if len(got.Items) != 0 {
176			t.Errorf("batch returned before its reset: %d items", len(got.Items))
177		}
178	}).Once()
179	dirty, changed := tb.Apply(b, shows(2), seen, ret)
180	if !dirty || !changed {
181		t.Fatalf("dirty %v changed %v, want both", dirty, changed)
182	}
183	if seen[1] != 5 || seen[2] != 1 {
184		t.Fatalf("seen %v", seen)
185	}
186	if len(tb.Map()) != 2 {
187		t.Fatalf("table %v", tb.Map())
188	}
189
190	// A window no scene draws and no newer Seq: nothing to do.
191	b = &ports.ContentBatch{Items: []ports.SurfaceContent{{ID: 1, Seq: 5, SHM: &ports.SHMBuffer{Pool: 1}}}}
192	ret.EXPECT().Return(b).Once()
193	dirty, changed = tb.Apply(b, shows(2), seen, ret)
194	if dirty || changed {
195		t.Fatalf("dirty %v changed %v, want neither", dirty, changed)
196	}
197}

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 23

Tags 10

↑↓ moveenter openesc close