test(daemon): specify render pipeline damage ownership
- commit
- 98194742aea9feb76146ee2bc402bf5f54125b60
- author
- brice · · 2026-07-12 04:48 UTC
- parent
- bda2cc4
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +6 -3
M internal/usecase/daemon/render_test.go +6 -3
| @@ -371,7 +371,10 @@ func TestPTYReaderRepublishesSynchronizedCompletionAfterAttachmentLifecycle(t *t | |||
| 371 | 371 | }) | |
| 372 | 372 | } | |
| 373 | 373 | ||
| 374 | - | func TestClearNonRenderablePaneDamage(t *testing.T) { | |
| 374 | + | // S2 keeps damage pending until the owning render capture consumes it. In | |
| 375 | + | // particular, a pane becoming invisible must not let a PTY reader erase data | |
| 376 | + | // that a later attachment or picker preview needs to render. | |
| 377 | + | func TestNonRenderablePaneDamageRemainsPendingForCapture(t *testing.T) { | |
| 375 | 378 | newFixture := func(t *testing.T) (*Daemon, *session, *tab, *pane, chan ports.Frame) { | |
| 376 | 379 | t.Helper() | |
| 377 | 380 | p, release := newBlockingPTY(t) | |
| @@ -381,7 +384,7 @@ func TestClearNonRenderablePaneDamage(t *testing.T) { | |||
| 381 | 384 | return d, sess, tb, tb.focusedPane(), sends | |
| 382 | 385 | } | |
| 383 | 386 | ||
| 384 | - | t.Run("clears headless, inactive, collapsed, and hidden panes without output", func(t *testing.T) { | |
| 387 | + | t.Run("headless, inactive, collapsed, and hidden panes retain damage without output", func(t *testing.T) { | |
| 385 | 388 | for _, tt := range []struct { | |
| 386 | 389 | name string | |
| 387 | 390 | setup func(*Daemon, *session, *tab, *pane) | |
| @@ -408,7 +411,7 @@ func TestClearNonRenderablePaneDamage(t *testing.T) { | |||
| 408 | 411 | tt.setup(d, sess, tb, p) | |
| 409 | 412 | p.screen.Write([]byte("damage")) | |
| 410 | 413 | d.clearNonRenderablePaneDamage(sess, tb, p) | |
| 411 | - | require.Empty(t, p.screen.Damage()) | |
| 414 | + | require.NotEmpty(t, p.screen.Damage(), "only render capture may consume VT damage") | |
| 412 | 415 | select { | |
| 413 | 416 | case frame := <-sends: | |
| 414 | 417 | t.Fatalf("non-renderable output must not compose or send: %#v", frame) | |
| @@ -371,7 +371,10 @@ func TestPTYReaderRepublishesSynchronizedCompletionAfterAttachmentLifecycle(t *t | |||||
| 371 | }) | 371 | }) | ||
| 372 | } | 372 | } | ||
| 373 | 373 | ||||
| 374 | - | func TestClearNonRenderablePaneDamage(t *testing.T) { | 374 | + | // S2 keeps damage pending until the owning render capture consumes it. In |
| 375 | + | // particular, a pane becoming invisible must not let a PTY reader erase data | |||
| 376 | + | // that a later attachment or picker preview needs to render. | |||
| 377 | + | func TestNonRenderablePaneDamageRemainsPendingForCapture(t *testing.T) { | |||
| 375 | newFixture := func(t *testing.T) (*Daemon, *session, *tab, *pane, chan ports.Frame) { | 378 | newFixture := func(t *testing.T) (*Daemon, *session, *tab, *pane, chan ports.Frame) { | ||
| 376 | t.Helper() | 379 | t.Helper() | ||
| 377 | p, release := newBlockingPTY(t) | 380 | p, release := newBlockingPTY(t) | ||
| @@ -381,7 +384,7 @@ func TestClearNonRenderablePaneDamage(t *testing.T) { | |||||
| 381 | return d, sess, tb, tb.focusedPane(), sends | 384 | return d, sess, tb, tb.focusedPane(), sends | ||
| 382 | } | 385 | } | ||
| 383 | 386 | ||||
| 384 | - | t.Run("clears headless, inactive, collapsed, and hidden panes without output", func(t *testing.T) { | 387 | + | t.Run("headless, inactive, collapsed, and hidden panes retain damage without output", func(t *testing.T) { |
| 385 | for _, tt := range []struct { | 388 | for _, tt := range []struct { | ||
| 386 | name string | 389 | name string | ||
| 387 | setup func(*Daemon, *session, *tab, *pane) | 390 | setup func(*Daemon, *session, *tab, *pane) | ||
| @@ -408,7 +411,7 @@ func TestClearNonRenderablePaneDamage(t *testing.T) { | |||||
| 408 | tt.setup(d, sess, tb, p) | 411 | tt.setup(d, sess, tb, p) | ||
| 409 | p.screen.Write([]byte("damage")) | 412 | p.screen.Write([]byte("damage")) | ||
| 410 | d.clearNonRenderablePaneDamage(sess, tb, p) | 413 | d.clearNonRenderablePaneDamage(sess, tb, p) | ||
| 411 | - | require.Empty(t, p.screen.Damage()) | 414 | + | require.NotEmpty(t, p.screen.Damage(), "only render capture may consume VT damage") |
| 412 | select { | 415 | select { | ||
| 413 | case frame := <-sends: | 416 | case frame := <-sends: | ||
| 414 | t.Fatalf("non-renderable output must not compose or send: %#v", frame) | 417 | t.Fatalf("non-renderable output must not compose or send: %#v", frame) | ||