v0.9.0 e5e4ded

terminal_lazy_test.go RawHistoryFolder

1package uiterm
2
3import (
4	"bytes"
5	"context"
6	"testing"
7
8	"github.com/bnema/vev/internal/domain"
9	"github.com/bnema/vev/internal/ports"
10)
11
12// TestTerminalSnapshotReadsCommittedRevision pins the lazy conversion: a
13// Snapshot read after later uncommitted writes still returns the committed
14// screen of the latest revision, and each revision converts its own state.
15func TestTerminalSnapshotReadsCommittedRevision(t *testing.T) {
16	terminal := newTestTerminal(t, 4, 1)
17	for _, tc := range []struct {
18		write    string
19		flush    bool
20		picker   bool
21		revision uint64
22		first    string
23	}{
24		{write: "A", flush: true, revision: 1, first: "A"},
25		{write: "\rB", revision: 1, first: "A"},
26		{flush: true, revision: 2, first: "B"},
27		{picker: true, revision: 3, first: "B"},
28		{write: "\rC", revision: 3, first: "B"},
29		{flush: true, revision: 4, first: "C"},
30	} {
31		if tc.write != "" {
32			if _, err := terminal.Write([]byte(tc.write)); err != nil {
33				t.Fatal(err)
34			}
35		}
36		if tc.flush {
37			if err := terminal.Flush(); err != nil {
38				t.Fatal(err)
39			}
40		}
41		if tc.picker {
42			// A context-only publication reuses the committed frame.
43			if err := terminal.PublishContext(ports.UIContext{AttachmentHandle: "attachment", Status: ports.UIStatusPicker}); err != nil {
44				t.Fatal(err)
45			}
46		}
47		snapshot, err := terminal.Snapshot()
48		if err != nil {
49			t.Fatal(err)
50		}
51		if snapshot.Revision != tc.revision || snapshot.Cells[0].Text != tc.first {
52			t.Fatalf("after %+v: revision %d first %q, want %d %q", tc, snapshot.Revision, snapshot.Cells[0].Text, tc.revision, tc.first)
53		}
54		if tc.revision >= 3 && snapshot.Context.Status != ports.UIStatusPicker {
55			t.Fatalf("after %+v: context %+v lost the picker publication", tc, snapshot.Context)
56		}
57	}
58}
59
60// TestTerminalBoundaryMatchesSnapshotWithoutConverting pins the per-frame
61// bookkeeping read: Boundary reports the same revision and context as
62// Snapshot, follows the same availability rules, and never builds cells.
63func TestTerminalBoundaryMatchesSnapshotWithoutConverting(t *testing.T) {
64	terminal := newTestTerminal(t, 4, 1)
65	if _, err := terminal.Boundary(); err == nil {
66		t.Fatal("boundary before the first publication must be unavailable")
67	}
68	terminal.BeginOutput(ports.UIContext{AttachmentHandle: "attachment", Generation: 3, Status: ports.UIStatusAttached})
69	if _, err := terminal.Write([]byte("A")); err != nil {
70		t.Fatal(err)
71	}
72	terminal.EndOutput(true)
73
74	boundary, err := terminal.Boundary()
75	if err != nil {
76		t.Fatal(err)
77	}
78	terminal.mu.Lock()
79	converted := terminal.latest.Revision
80	terminal.mu.Unlock()
81	if converted != 0 {
82		t.Fatalf("Boundary converted revision %d", converted)
83	}
84	snapshot, err := terminal.Snapshot()
85	if err != nil {
86		t.Fatal(err)
87	}
88	if boundary.Revision != snapshot.Revision || boundary.Context != snapshot.Context {
89		t.Fatalf("boundary %+v, snapshot revision %d context %+v", boundary, snapshot.Revision, snapshot.Context)
90	}
91
92	terminal.InvalidateTerminalObservation()
93	if _, err := terminal.Boundary(); err == nil {
94		t.Fatal("boundary of an invalidated observation must be unavailable")
95	}
96}
97
98// BenchmarkTerminalMirrorFlush measures one mirrored flush that nobody reads,
99// the steady state of an interactive client with UI control enabled.
100func BenchmarkTerminalMirrorFlush(b *testing.B) {
101	terminal, err := NewMirror(context.Background(), domain.Geometry{Size: domain.Size{Cols: 200, Rows: 50}}, "attachment")
102	if err != nil {
103		b.Fatal(err)
104	}
105	defer terminal.Close()
106	line := append(bytes.Repeat([]byte("x"), 199), '\n')
107	b.ReportAllocs()
108	for b.Loop() {
109		terminal.ObserveTerminalWrite(line)
110		terminal.ObserveTerminalFlush()
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 12

Tags 11

↑↓ moveenter openesc close