detached 8747466

test(core): sync no-scene and command checks on a flip barrier instead of timeouts

commit
8747466f45a8234c8d40aa6786cf76da24089d0d
author
brice · · 2026-10-05 08:28 UTC
parent
1c70add
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +31 -27
M internal/core/animations_off_test.go +15 -5
@@ -17,10 +17,22 @@ func (r *swipeRig) stillMoves(t *testing.T, n int, dx, dy float64) {
1717 r.at += 8 * time.Millisecond
1818 r.input <- ports.SwipeUpdate{DX: dx, DY: dy, Time: r.at}
1919 }
20+ if s, ok := r.published(); ok {
21+ t.Fatalf("a swipe update published a scene (%d windows): the view follows the fingers", len(s))
22+ }
23+}
24+
25+// published reports the scene set core published for the events sent so
26+// far, if any. A flip that names no output is a barrier: core handles its
27+// events in order and publishes before taking the next one, so once the
28+// send returns every earlier event is handled.
29+func (r *swipeRig) published() ([]ports.Scene, bool) {
30+ send(r.frames, ports.OutputFrame{Output: "sync"})
2031 select {
2132 case s := <-r.scenes:
22- t.Fatalf("a swipe update published a scene (%d windows): the view follows the fingers", len(s))
23- case <-time.After(50 * time.Millisecond):
33+ return s, true
34+ default:
35+ return nil, false
2436 }
2537 }
2638
@@ -104,10 +116,8 @@ func (r *swipeRig) noFrameScene(t *testing.T, outputs ...string) {
104116 r.frames <- ports.OutputFrame{Output: o}
105117 }
106118 }
107- select {
108- case s := <-r.scenes:
119+ if s, ok := r.published(); ok {
109120 t.Fatalf("a flip published a scene (%d windows): a spring still runs", len(s))
110- case <-time.After(50 * time.Millisecond):
111121 }
112122 }
113123
@@ -17,10 +17,22 @@ func (r *swipeRig) stillMoves(t *testing.T, n int, dx, dy float64) {
17 r.at += 8 * time.Millisecond17 r.at += 8 * time.Millisecond
18 r.input <- ports.SwipeUpdate{DX: dx, DY: dy, Time: r.at}18 r.input <- ports.SwipeUpdate{DX: dx, DY: dy, Time: r.at}
19 }19 }
20+ if s, ok := r.published(); ok {
21+ t.Fatalf("a swipe update published a scene (%d windows): the view follows the fingers", len(s))
22+ }
23+}
24+
25+// published reports the scene set core published for the events sent so
26+// far, if any. A flip that names no output is a barrier: core handles its
27+// events in order and publishes before taking the next one, so once the
28+// send returns every earlier event is handled.
29+func (r *swipeRig) published() ([]ports.Scene, bool) {
30+ send(r.frames, ports.OutputFrame{Output: "sync"})
20 select {31 select {
21 case s := <-r.scenes:32 case s := <-r.scenes:
22- t.Fatalf("a swipe update published a scene (%d windows): the view follows the fingers", len(s))33+ return s, true
23- case <-time.After(50 * time.Millisecond):34+ default:
35+ return nil, false
24 }36 }
25 }37 }
26 38
@@ -104,10 +116,8 @@ func (r *swipeRig) noFrameScene(t *testing.T, outputs ...string) {
104 r.frames <- ports.OutputFrame{Output: o}116 r.frames <- ports.OutputFrame{Output: o}
105 }117 }
106 }118 }
107- select {119+ if s, ok := r.published(); ok {
108- case s := <-r.scenes:
109 t.Fatalf("a flip published a scene (%d windows): a spring still runs", len(s))120 t.Fatalf("a flip published a scene (%d windows): a spring still runs", len(s))
110- case <-time.After(50 * time.Millisecond):
111 }121 }
112 }122 }
113 123
M internal/core/swipe_test.go +16 -22
@@ -436,16 +436,8 @@ func TestSlideMovesPointerFocus(t *testing.T) {
436436 r.settle(t)
437437 var focus ports.PointerFocus
438438 var motion ports.PointerMotionTo
439- // Core sends the pointer update right after the scene: wait for it.
440- for {
441- var v ports.ClientCommand
442- select {
443- case v = <-r.commands:
444- case <-time.After(50 * time.Millisecond):
445- }
446- if v == nil {
447- break
448- }
439+ // Core sends the pointer update right after the scene.
440+ for _, v := range r.sent() {
449441 switch v := v.(type) {
450442 case ports.PointerFocus:
451443 focus = v
@@ -561,15 +553,7 @@ func TestDiscreteSwipeSkippedWhenPointerChangesOutput(t *testing.T) {
561553 scene(t, r.scenes)
562554 r.drain()
563555 r.input <- ports.SwipeEnd{Time: r.at}
564- for {
565- var v ports.ClientCommand
566- select {
567- case v = <-r.commands:
568- case <-time.After(50 * time.Millisecond):
569- }
570- if v == nil {
571- break
572- }
556+ for _, v := range r.sent() {
573557 if f, ok := v.(ports.FocusWindow); ok && f.ID == 3 {
574558 t.Fatal("swipe from DP-1 moved the focus on DP-2")
575559 }
@@ -578,11 +562,21 @@ func TestDiscreteSwipeSkippedWhenPointerChangesOutput(t *testing.T) {
578562
579563 // drain waits for core to handle what was sent, then empties commands.
580564 func (r *swipeRig) drain() {
565+ for range r.sent() {
566+ }
567+}
568+
569+// sent waits for core to handle what was sent and returns the commands it
570+// sent meanwhile (the sync flip is a barrier, see published).
571+func (r *swipeRig) sent() []ports.ClientCommand {
572+ send(r.frames, ports.OutputFrame{Output: "sync"})
573+ var out []ports.ClientCommand
581574 for {
582575 select {
583- case <-r.commands:
584- case <-time.After(50 * time.Millisecond):
585- return
576+ case v := <-r.commands:
577+ out = append(out, v)
578+ default:
579+ return out
586580 }
587581 }
588582 }
@@ -436,16 +436,8 @@ func TestSlideMovesPointerFocus(t *testing.T) {
436 r.settle(t)436 r.settle(t)
437 var focus ports.PointerFocus437 var focus ports.PointerFocus
438 var motion ports.PointerMotionTo438 var motion ports.PointerMotionTo
439- // Core sends the pointer update right after the scene: wait for it.439+ // Core sends the pointer update right after the scene.
440- for {440+ for _, v := range r.sent() {
441- var v ports.ClientCommand
442- select {
443- case v = <-r.commands:
444- case <-time.After(50 * time.Millisecond):
445- }
446- if v == nil {
447- break
448- }
449 switch v := v.(type) {441 switch v := v.(type) {
450 case ports.PointerFocus:442 case ports.PointerFocus:
451 focus = v443 focus = v
@@ -561,15 +553,7 @@ func TestDiscreteSwipeSkippedWhenPointerChangesOutput(t *testing.T) {
561 scene(t, r.scenes)553 scene(t, r.scenes)
562 r.drain()554 r.drain()
563 r.input <- ports.SwipeEnd{Time: r.at}555 r.input <- ports.SwipeEnd{Time: r.at}
564- for {556+ for _, v := range r.sent() {
565- var v ports.ClientCommand
566- select {
567- case v = <-r.commands:
568- case <-time.After(50 * time.Millisecond):
569- }
570- if v == nil {
571- break
572- }
573 if f, ok := v.(ports.FocusWindow); ok && f.ID == 3 {557 if f, ok := v.(ports.FocusWindow); ok && f.ID == 3 {
574 t.Fatal("swipe from DP-1 moved the focus on DP-2")558 t.Fatal("swipe from DP-1 moved the focus on DP-2")
575 }559 }
@@ -578,11 +562,21 @@ func TestDiscreteSwipeSkippedWhenPointerChangesOutput(t *testing.T) {
578 562
579 // drain waits for core to handle what was sent, then empties commands.563 // drain waits for core to handle what was sent, then empties commands.
580 func (r *swipeRig) drain() {564 func (r *swipeRig) drain() {
565+ for range r.sent() {
566+ }
567+}
568+
569+// sent waits for core to handle what was sent and returns the commands it
570+// sent meanwhile (the sync flip is a barrier, see published).
571+func (r *swipeRig) sent() []ports.ClientCommand {
572+ send(r.frames, ports.OutputFrame{Output: "sync"})
573+ var out []ports.ClientCommand
581 for {574 for {
582 select {575 select {
583- case <-r.commands:576+ case v := <-r.commands:
584- case <-time.After(50 * time.Millisecond):577+ out = append(out, v)
585- return578+ default:
579+ return out
586 }580 }
587 }581 }
588 }582 }

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