main 3a820d6

queue_test.go RawHistoryFolder

1package presented
2
3import (
4	"reflect"
5	"testing"
6
7	portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
8	"github.com/bnema/neferwl/internal/ports"
9	"github.com/stretchr/testify/mock"
10	"github.com/stretchr/testify/require"
11)
12
13func ptr(m map[ports.WindowID]uint64) uintptr { return reflect.ValueOf(m).Pointer() }
14
15// An unchanged publication reuses its immutable snapshots; a change clones,
16// and earlier reports keep their maps.
17func TestQueueSnapshotsCloneOnlyOnChange(t *testing.T) {
18	var q Queue
19	seen := map[ports.WindowID]uint64{1: 4, 5: 7}
20	reads := map[ports.WindowID]uint64{5: 2}
21	q.Push(ports.OutputPresented{Output: "A", Seen: seen, ChildReads: reads})
22	first := q.Unsent()[0]
23	require.Equal(t, seen, first.Seen)
24	require.Equal(t, reads, first.ChildReads)
25	seen[1] = 99 // the caller's scratch changes: the report does not
26	require.Equal(t, uint64(4), first.Seen[1])
27
28	seen[1] = 4
29	q.Push(ports.OutputPresented{Output: "A", Seen: seen, ChildReads: reads})
30	again := q.Unsent()[0]
31	require.Equal(t, ptr(first.Seen), ptr(again.Seen), "unchanged seen is shared")
32	require.Equal(t, ptr(first.ChildReads), ptr(again.ChildReads), "unchanged reads are shared")
33	allocs := testing.AllocsPerRun(100, func() { q.Push(ports.OutputPresented{Output: "A", Seen: seen, ChildReads: reads}) })
34	require.Zero(t, allocs, "unchanged publication allocates nothing")
35
36	reads[5] = 3
37	q.Push(ports.OutputPresented{Output: "A", Seen: seen, ChildReads: reads})
38	changed := q.Unsent()[0]
39	require.Equal(t, map[ports.WindowID]uint64{5: 3}, changed.ChildReads)
40	require.Equal(t, map[ports.WindowID]uint64{5: 2}, first.ChildReads, "sent snapshot stays immutable")
41	require.Equal(t, ptr(first.Seen), ptr(changed.Seen), "seen still unchanged")
42}
43
44// Flip-less reports fold into the newest one and keep its flip; flips queue.
45func TestQueueKeepsEveryFlip(t *testing.T) {
46	var q Queue
47	seen := map[ports.WindowID]uint64{1: 1}
48	q.Push(ports.OutputPresented{Flip: &ports.FlipInfo{Seq: 1}, Seen: seen})
49	q.Push(ports.OutputPresented{Seen: seen})
50	q.Push(ports.OutputPresented{Flip: &ports.FlipInfo{Seq: 2}, Seen: seen})
51	q.Push(ports.OutputPresented{Seen: map[ports.WindowID]uint64{1: 3}})
52	u := q.Unsent()
53	require.Len(t, u, 2)
54	require.Equal(t, uint64(1), u[0].Flip.Seq)
55	require.Equal(t, uint64(2), u[1].Flip.Seq, "a flip-less report keeps the queued flip")
56	require.Equal(t, uint64(3), u[1].Seen[1])
57}
58
59// Merge folds a flip into the next: counted as merged and its shown windows
60// carried, without mutating a shared Shows snapshot.
61func TestMergeCarriesShowsWithoutMutatingSharedSnapshot(t *testing.T) {
62	shared := map[ports.WindowID]uint64{2: 5}
63	old := &ports.FlipInfo{Seq: 1, Merged: 2, Shows: map[ports.WindowID]uint64{1: 4}}
64	next := &ports.FlipInfo{Seq: 2, Shows: shared}
65	Merge(old, next)
66	require.Equal(t, 3, next.Merged)
67	require.Equal(t, map[ports.WindowID]uint64{1: 4, 2: 5}, next.Shows)
68	require.Equal(t, map[ports.WindowID]uint64{2: 5}, shared, "shared snapshot untouched")
69}
70
71// The queue never merges flips: every flip of a turn is pushed to the sink.
72func TestQueueDoesNotBoundFlips(t *testing.T) {
73	var q Queue
74	for i := range 40 {
75		q.Push(ports.OutputPresented{Flip: &ports.FlipInfo{Seq: uint64(i + 1)}})
76	}
77	u := q.Unsent()
78	require.Len(t, u, 40)
79	require.Zero(t, u[0].Flip.Merged)
80	require.Equal(t, uint64(1), u[0].Flip.Seq)
81}
82
83func TestQueueFlushPushesInOrderAndEmpties(t *testing.T) {
84	var q Queue
85	q.Push(ports.OutputPresented{Flip: &ports.FlipInfo{Seq: 1}})
86	q.Push(ports.OutputPresented{Flip: &ports.FlipInfo{Seq: 2}})
87	var got []ports.OutputPresented
88	sink := portsmocks.NewMockPresentationSink(t)
89	sink.EXPECT().Push(mock.Anything).RunAndReturn(func(r ports.OutputPresented) { got = append(got, r) })
90	q.Flush(sink)
91	require.Zero(t, q.Len())
92	require.Len(t, got, 2)
93	require.Equal(t, uint64(1), got[0].Flip.Seq)
94	require.Equal(t, uint64(2), got[1].Flip.Seq)
95	q.Flush(sink) // empty: nothing more is pushed
96	require.Len(t, got, 2)
97
98	q.Push(ports.OutputPresented{Flip: &ports.FlipInfo{Seq: 3}})
99	q.Reset()
100	require.Zero(t, q.Len())
101}

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