fix(floating): keep zero-inner publication atomic
- commit
- 7ccd74f58cc0448406a4e88fbd5622b022f9f0db
- author
- brice · · 2026-07-27 17:23 UTC
- parent
- dae353e
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +32 -0
M internal/usecase/daemon/transactional_resize.go +2 -0
| @@ -383,6 +383,8 @@ func (d *Daemon) applyVisibleFloatingLayoutForMember(sess *session, tb *tab, cur | |||
| 383 | 383 | p.mu.Lock() | |
| 384 | 384 | p.popupGeometry = geometry | |
| 385 | 385 | p.mu.Unlock() | |
| 386 | + | tb.mu.Unlock() | |
| 387 | + | return nil, true | |
| 386 | 388 | } | |
| 387 | 389 | tb.mu.Unlock() | |
| 388 | 390 | if current != nil && !current() { | |
| @@ -383,6 +383,8 @@ func (d *Daemon) applyVisibleFloatingLayoutForMember(sess *session, tb *tab, cur | |||||
| 383 | p.mu.Lock() | 383 | p.mu.Lock() | ||
| 384 | p.popupGeometry = geometry | 384 | p.popupGeometry = geometry | ||
| 385 | p.mu.Unlock() | 385 | p.mu.Unlock() | ||
| 386 | + | tb.mu.Unlock() | |||
| 387 | + | return nil, true | |||
| 386 | } | 388 | } | ||
| 387 | tb.mu.Unlock() | 389 | tb.mu.Unlock() | ||
| 388 | if current != nil && !current() { | 390 | if current != nil && !current() { | ||
M internal/usecase/daemon/transactional_resize_test.go +30 -0
| @@ -617,6 +617,36 @@ func TestApplyVisibleFloatingLayoutPublishesSameSizeGeometryWithoutPTYResize(t * | |||
| 617 | 617 | popup.mu.Unlock() | |
| 618 | 618 | } | |
| 619 | 619 | ||
| 620 | + | func TestZeroInnerFloatingDrawerPublicationDoesNotRecheckCurrent(t *testing.T) { | |
| 621 | + | cfg := domain.FloatingConfig{Width: 100, Height: 100} | |
| 622 | + | committed := calculateContentFloatingGeometry(domain.Size{Cols: 80, Rows: 22}, cfg) | |
| 623 | + | pty := &transactionalResizePTY{} | |
| 624 | + | popup := newPane("floating", pty, rectSize(committed.Inner)) | |
| 625 | + | popup.rect = committed.Inner | |
| 626 | + | popup.popupGeometry = committed | |
| 627 | + | tb := newTab(nil, domain.Size{Cols: 79, Rows: 2}) | |
| 628 | + | installTestFloating(tb, popup, true) | |
| 629 | + | d := newTestDaemon(t, nil, stubClock{}) | |
| 630 | + | d.ApplyConfig(domain.Config{Floating: cfg}) | |
| 631 | + | calls := 0 | |
| 632 | + | ||
| 633 | + | failed, ok := d.applyVisibleFloatingLayout(&session{tabs: []*tab{tb}}, tb, func() bool { | |
| 634 | + | calls++ | |
| 635 | + | return calls == 1 | |
| 636 | + | }) | |
| 637 | + | ||
| 638 | + | require.True(t, ok, "a publication accepted while current must not later report stale") | |
| 639 | + | require.Equal(t, 1, calls, "current must not be rechecked after publication") | |
| 640 | + | require.Empty(t, failed) | |
| 641 | + | require.Empty(t, pty.requested()) | |
| 642 | + | requested := calculateContentFloatingGeometry(tb.size, cfg) | |
| 643 | + | popup.mu.Lock() | |
| 644 | + | require.Equal(t, requested, popup.popupGeometry) | |
| 645 | + | require.Equal(t, committed.Inner, popup.rect) | |
| 646 | + | require.Equal(t, rectSize(committed.Inner), domain.Size{Cols: popup.screen.Frame.Width, Rows: popup.screen.Frame.Height}) | |
| 647 | + | popup.mu.Unlock() | |
| 648 | + | } | |
| 649 | + | ||
| 620 | 650 | func TestZeroInnerFloatingDrawerRejectsStaleRequestWithoutPhysicalPublication(t *testing.T) { | |
| 621 | 651 | cfg := domain.FloatingConfig{Width: 100, Height: 100} | |
| 622 | 652 | committed := calculateContentFloatingGeometry(domain.Size{Cols: 80, Rows: 22}, cfg) | |
| @@ -617,6 +617,36 @@ func TestApplyVisibleFloatingLayoutPublishesSameSizeGeometryWithoutPTYResize(t * | |||||
| 617 | popup.mu.Unlock() | 617 | popup.mu.Unlock() | ||
| 618 | } | 618 | } | ||
| 619 | 619 | ||||
| 620 | + | func TestZeroInnerFloatingDrawerPublicationDoesNotRecheckCurrent(t *testing.T) { | |||
| 621 | + | cfg := domain.FloatingConfig{Width: 100, Height: 100} | |||
| 622 | + | committed := calculateContentFloatingGeometry(domain.Size{Cols: 80, Rows: 22}, cfg) | |||
| 623 | + | pty := &transactionalResizePTY{} | |||
| 624 | + | popup := newPane("floating", pty, rectSize(committed.Inner)) | |||
| 625 | + | popup.rect = committed.Inner | |||
| 626 | + | popup.popupGeometry = committed | |||
| 627 | + | tb := newTab(nil, domain.Size{Cols: 79, Rows: 2}) | |||
| 628 | + | installTestFloating(tb, popup, true) | |||
| 629 | + | d := newTestDaemon(t, nil, stubClock{}) | |||
| 630 | + | d.ApplyConfig(domain.Config{Floating: cfg}) | |||
| 631 | + | calls := 0 | |||
| 632 | + | ||||
| 633 | + | failed, ok := d.applyVisibleFloatingLayout(&session{tabs: []*tab{tb}}, tb, func() bool { | |||
| 634 | + | calls++ | |||
| 635 | + | return calls == 1 | |||
| 636 | + | }) | |||
| 637 | + | ||||
| 638 | + | require.True(t, ok, "a publication accepted while current must not later report stale") | |||
| 639 | + | require.Equal(t, 1, calls, "current must not be rechecked after publication") | |||
| 640 | + | require.Empty(t, failed) | |||
| 641 | + | require.Empty(t, pty.requested()) | |||
| 642 | + | requested := calculateContentFloatingGeometry(tb.size, cfg) | |||
| 643 | + | popup.mu.Lock() | |||
| 644 | + | require.Equal(t, requested, popup.popupGeometry) | |||
| 645 | + | require.Equal(t, committed.Inner, popup.rect) | |||
| 646 | + | require.Equal(t, rectSize(committed.Inner), domain.Size{Cols: popup.screen.Frame.Width, Rows: popup.screen.Frame.Height}) | |||
| 647 | + | popup.mu.Unlock() | |||
| 648 | + | } | |||
| 649 | + | ||||
| 620 | func TestZeroInnerFloatingDrawerRejectsStaleRequestWithoutPhysicalPublication(t *testing.T) { | 650 | func TestZeroInnerFloatingDrawerRejectsStaleRequestWithoutPhysicalPublication(t *testing.T) { | ||
| 621 | cfg := domain.FloatingConfig{Width: 100, Height: 100} | 651 | cfg := domain.FloatingConfig{Width: 100, Height: 100} | ||
| 622 | committed := calculateContentFloatingGeometry(domain.Size{Cols: 80, Rows: 22}, cfg) | 652 | committed := calculateContentFloatingGeometry(domain.Size{Cols: 80, Rows: 22}, cfg) | ||