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 Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· 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) { | |||
| 17 | 17 | r.at += 8 * time.Millisecond | |
| 18 | 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 | 31 | select { | |
| 21 | 32 | 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 | |
| 24 | 36 | } | |
| 25 | 37 | } | |
| 26 | 38 | ||
| @@ -104,10 +116,8 @@ func (r *swipeRig) noFrameScene(t *testing.T, outputs ...string) { | |||
| 104 | 116 | r.frames <- ports.OutputFrame{Output: o} | |
| 105 | 117 | } | |
| 106 | 118 | } | |
| 107 | - | select { | |
| 108 | - | case s := <-r.scenes: | |
| 119 | + | if s, ok := r.published(); ok { | |
| 109 | 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 | ||
| @@ -17,10 +17,22 @@ func (r *swipeRig) stillMoves(t *testing.T, n int, dx, dy float64) { | |||||
| 17 | r.at += 8 * time.Millisecond | 17 | 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) { | |||
| 436 | 436 | r.settle(t) | |
| 437 | 437 | var focus ports.PointerFocus | |
| 438 | 438 | 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() { | |
| 449 | 441 | switch v := v.(type) { | |
| 450 | 442 | case ports.PointerFocus: | |
| 451 | 443 | focus = v | |
| @@ -561,15 +553,7 @@ func TestDiscreteSwipeSkippedWhenPointerChangesOutput(t *testing.T) { | |||
| 561 | 553 | scene(t, r.scenes) | |
| 562 | 554 | r.drain() | |
| 563 | 555 | 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() { | |
| 573 | 557 | if f, ok := v.(ports.FocusWindow); ok && f.ID == 3 { | |
| 574 | 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 | 563 | // drain waits for core to handle what was sent, then empties commands. | |
| 580 | 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 | 574 | for { | |
| 582 | 575 | 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 | |
| 586 | 580 | } | |
| 587 | 581 | } | |
| 588 | 582 | } | |
| @@ -436,16 +436,8 @@ func TestSlideMovesPointerFocus(t *testing.T) { | |||||
| 436 | r.settle(t) | 436 | r.settle(t) | ||
| 437 | var focus ports.PointerFocus | 437 | var focus ports.PointerFocus | ||
| 438 | var motion ports.PointerMotionTo | 438 | 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 = v | 443 | 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 | - | return | 578 | + | default: |
| 579 | + | return out | |||
| 586 | } | 580 | } | ||
| 587 | } | 581 | } | ||
| 588 | } | 582 | } | ||