feat/data-dnd e05cb15

scene_animating_test.go RawHistoryFolder

1package core
2
3import (
4	"context"
5	"testing"
6	"time"
7
8	"github.com/bnema/neferwl/internal/ports"
9)
10
11// A scene published while a spring runs carries Animating, so its flip wakes
12// core for the next frame; the scene the spring settles on does not, and
13// neither does any scene of an idle output.
14func TestSceneAnimatingFollowsSprings(t *testing.T) {
15	c, ic := indicatorCore(t)
16	c.cfg.Animations.On = true
17	a := c.cur()
18	a.mon.AddWindow(1)
19	a.mon.AddWindow(2)
20	if s := indicatorScene(t, c); s.Animating {
21		t.Fatal("an idle output publishes an animating scene")
22	}
23
24	// A slide starts.
25	w := a.mon.Current()
26	w.view.off = 100
27	w.view.motion = newMotion(viewSpring(w.view.off, 0), ic.now, 1)
28	ic.now = ic.now.Add(10 * time.Millisecond)
29	first := stepScenes(t, c, a)["A"]
30	if !first.Animating {
31		t.Fatal("a scene with a running spring is not animating")
32	}
33	ic.now = ic.now.Add(10 * time.Millisecond)
34	if second := stepScenes(t, c, a)["A"]; !second.Animating || second.Seq == first.Seq {
35		t.Fatalf("running slide: animating=%v Seq %d (was %d)", second.Animating, second.Seq, first.Seq)
36	}
37
38	// The step that lands the spring publishes the last scene: nothing more
39	// is due, so it must not wake core again.
40	ic.now = ic.now.Add(10 * time.Second)
41	last := stepScenes(t, c, a)["A"]
42	if last.Animating {
43		t.Fatal("the scene a spring settles on is animating")
44	}
45	if a.springing() {
46		t.Fatal("bad setup: the spring still runs")
47	}
48	// Idle again: still false, and the Seq is held (no extra recomposition).
49	if err := c.publish(context.Background()); err != nil {
50		t.Fatal(err)
51	}
52	idle := (<-c.ch.Scenes)[0]
53	if idle.Animating || idle.Seq != last.Seq || !idle.SameAs(last) {
54		t.Fatalf("idle publish: animating=%v Seq %d (was %d)", idle.Animating, idle.Seq, last.Seq)
55	}
56}
57
58// A pulse cancelled because nothing can show it (fullscreen) publishes the
59// focused scene settled, with a fresh Seq: otherwise outputs keep the
60// animating scene and wake core on every flip.
61func TestCancelledPulseClearsAnimating(t *testing.T) {
62	c, ic := pulseCore(t)
63	c.cur().mon.Current().FocusID(1)
64	indicatorScene(t, c)
65	ic.now = ic.now.Add(pulseSettle)
66	if !c.pulseTick() {
67		t.Fatal("bad setup: no pulse")
68	}
69	running := indicatorScene(t, c)
70	if !running.Animating {
71		t.Fatal("bad setup: the pulsing scene is not animating")
72	}
73	c.cur().mon.ToggleFullscreen()
74	cancelled := indicatorScene(t, c)
75	if cancelled.Animating || c.animating() {
76		t.Fatalf("cancelled pulse: scene animating=%v core animating=%v", cancelled.Animating, c.animating())
77	}
78	if cancelled.Seq == running.Seq {
79		t.Fatalf("cancelled pulse kept the animating scene's Seq %d", running.Seq)
80	}
81	idle := indicatorScene(t, c)
82	if idle.Animating || idle.Seq != cancelled.Seq {
83		t.Fatalf("idle publish after cancel: animating=%v Seq %d (was %d)", idle.Animating, idle.Seq, cancelled.Seq)
84	}
85}
86
87// Animating is per output: a spring on one does not mark the other.
88func TestSceneAnimatingIsPerOutput(t *testing.T) {
89	c, ic := indicatorCore(t)
90	c.cfg.Animations.On = true
91	a := c.cur()
92	c.addScreen(ports.OutputInfo{Name: "B", Width: 300, Height: 200})
93	b := c.screens[1]
94	a.mon.AddWindow(1)
95	b.mon.AddWindow(2)
96	indicatorScene(t, c)
97	w := b.mon.Current()
98	w.view.off = 100
99	w.view.motion = newMotion(viewSpring(w.view.off, 0), ic.now, 1)
100	ic.now = ic.now.Add(10 * time.Millisecond)
101	got := stepScenes(t, c, b)
102	if !got["B"].Animating || got["A"].Animating {
103		t.Fatalf("animating A=%v B=%v, want B only", got["A"].Animating, got["B"].Animating)
104	}
105}
106
107// A spring that settles on the very scene its last running step drew (the
108// tail rounds to the final pixel) must still publish a scene with Animating
109// off, with a fresh Seq: SameAs ignores the flag, and the outputs would
110// otherwise keep the animating scene they show and wake core on every later
111// flip.
112func TestSettledSceneSameAsPreviousClearsAnimating(t *testing.T) {
113	// Commands (rehit) are not read: room for every step.
114	c, ic := indicatorCoreWith(t, make(chan ports.ClientCommand, 4096))
115	c.cfg.Animations.On = true
116	a := c.cur()
117	a.mon.AddWindow(1)
118	a.mon.AddWindow(2)
119	indicatorScene(t, c)
120
121	w := a.mon.Current()
122	w.view.off = 100
123	w.view.motion = newMotion(viewSpring(w.view.off, 0), ic.now, 1)
124	// Step in 1 ms until the spring settles: the snap settles it once it is
125	// within half a pixel of its target, where the last running scene can
126	// draw the same pixels as the settled one.
127	start := ic.now
128	var running, settled ports.Scene
129	for ms := 1; ; ms++ {
130		if ms > 2000 {
131			t.Fatal("the spring never settles")
132		}
133		ic.now = start.Add(time.Duration(ms) * time.Millisecond)
134		settled = stepScenes(t, c, a)["A"]
135		if !a.springing() {
136			break
137		}
138		running = settled
139	}
140	if !running.Animating {
141		t.Fatalf("bad setup: last running scene animating=%v", running.Animating)
142	}
143	if !settled.SameAs(running) {
144		t.Fatal("bad setup: the settled scene differs from the last running one")
145	}
146	if settled.Animating {
147		t.Fatal("the settled scene is animating")
148	}
149	if settled.Seq == running.Seq {
150		t.Fatalf("settled scene kept the animating scene's Seq %d", running.Seq)
151	}
152}

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 25

Tags 10

↑↓ moveenter openesc close