fix(daemon): synchronize pane start logging
- commit
- 1469f94a0971b36db2d463b17da938ae8d1150b9
- author
- brice · · 2026-07-10 05:51 UTC
- parent
- 59ce3a4
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +59 -1
M internal/usecase/daemon/floating_test.go +55 -0
| @@ -523,6 +523,61 @@ func TestFloatingLaunchFailureCapturesSessionNameBeforeConcurrentRename(t *testi | |||
| 523 | 523 | require.True(t, strings.Contains(logs.String(), "session=captured"), logs.String()) | |
| 524 | 524 | } | |
| 525 | 525 | ||
| 526 | + | func TestFloatingInstallLogsSessionNameSafelyDuringRename(t *testing.T) { | |
| 527 | + | floatingPTY, _ := newBlockingPTY(t) | |
| 528 | + | factory := portsmocks.NewMockPTYFactory(t) | |
| 529 | + | opened := make(chan struct{}) | |
| 530 | + | allowOpen := make(chan struct{}) | |
| 531 | + | factory.EXPECT().Open(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn( | |
| 532 | + | func(string, []string, []string, string, domain.Size) (ports.PTY, error) { | |
| 533 | + | close(opened) | |
| 534 | + | <-allowOpen | |
| 535 | + | return floatingPTY, nil | |
| 536 | + | }).Once() | |
| 537 | + | var logs bytes.Buffer | |
| 538 | + | handled := make(chan struct{}) | |
| 539 | + | d := newTestDaemon(t, factory, stubClock{}) | |
| 540 | + | d.log = slog.New(floatingLogHandler{Handler: slog.NewTextHandler(&logs, nil), handled: handled}) | |
| 541 | + | tb := newFloatingTestTab(t) | |
| 542 | + | sess := &session{name: "captured", tabs: []*tab{tb}, ctx: t.Context()} | |
| 543 | + | ||
| 544 | + | d.startFloating(sess, tb, false) | |
| 545 | + | <-opened | |
| 546 | + | renamed := make(chan struct{}) | |
| 547 | + | stopRename := make(chan struct{}) | |
| 548 | + | renameDone := make(chan struct{}) | |
| 549 | + | go func() { | |
| 550 | + | defer close(renameDone) | |
| 551 | + | sess.mu.Lock() | |
| 552 | + | sess.name = "renamed" | |
| 553 | + | sess.mu.Unlock() | |
| 554 | + | close(renamed) | |
| 555 | + | for { | |
| 556 | + | select { | |
| 557 | + | case <-stopRename: | |
| 558 | + | return | |
| 559 | + | default: | |
| 560 | + | } | |
| 561 | + | sess.mu.Lock() | |
| 562 | + | sess.name = "renamed" | |
| 563 | + | sess.mu.Unlock() | |
| 564 | + | } | |
| 565 | + | }() | |
| 566 | + | <-renamed | |
| 567 | + | close(allowOpen) | |
| 568 | + | select { | |
| 569 | + | case <-handled: | |
| 570 | + | case <-time.After(time.Second): | |
| 571 | + | t.Fatal("installed floating pane was not logged") | |
| 572 | + | } | |
| 573 | + | close(stopRename) | |
| 574 | + | <-renameDone | |
| 575 | + | require.Contains(t, logs.String(), "session=renamed", logs.String()) | |
| 576 | + | ||
| 577 | + | d.teardownFloating(tb) | |
| 578 | + | d.sessWg.Wait() | |
| 579 | + | } | |
| 580 | + | ||
| 526 | 581 | func TestFloatingToggleUsesVisibleTarget(t *testing.T) { | |
| 527 | 582 | d := newTestDaemon(t, nil, stubClock{}) | |
| 528 | 583 | tb := newFloatingTestTab(t) | |
| @@ -523,6 +523,61 @@ func TestFloatingLaunchFailureCapturesSessionNameBeforeConcurrentRename(t *testi | |||||
| 523 | require.True(t, strings.Contains(logs.String(), "session=captured"), logs.String()) | 523 | require.True(t, strings.Contains(logs.String(), "session=captured"), logs.String()) | ||
| 524 | } | 524 | } | ||
| 525 | 525 | ||||
| 526 | + | func TestFloatingInstallLogsSessionNameSafelyDuringRename(t *testing.T) { | |||
| 527 | + | floatingPTY, _ := newBlockingPTY(t) | |||
| 528 | + | factory := portsmocks.NewMockPTYFactory(t) | |||
| 529 | + | opened := make(chan struct{}) | |||
| 530 | + | allowOpen := make(chan struct{}) | |||
| 531 | + | factory.EXPECT().Open(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn( | |||
| 532 | + | func(string, []string, []string, string, domain.Size) (ports.PTY, error) { | |||
| 533 | + | close(opened) | |||
| 534 | + | <-allowOpen | |||
| 535 | + | return floatingPTY, nil | |||
| 536 | + | }).Once() | |||
| 537 | + | var logs bytes.Buffer | |||
| 538 | + | handled := make(chan struct{}) | |||
| 539 | + | d := newTestDaemon(t, factory, stubClock{}) | |||
| 540 | + | d.log = slog.New(floatingLogHandler{Handler: slog.NewTextHandler(&logs, nil), handled: handled}) | |||
| 541 | + | tb := newFloatingTestTab(t) | |||
| 542 | + | sess := &session{name: "captured", tabs: []*tab{tb}, ctx: t.Context()} | |||
| 543 | + | ||||
| 544 | + | d.startFloating(sess, tb, false) | |||
| 545 | + | <-opened | |||
| 546 | + | renamed := make(chan struct{}) | |||
| 547 | + | stopRename := make(chan struct{}) | |||
| 548 | + | renameDone := make(chan struct{}) | |||
| 549 | + | go func() { | |||
| 550 | + | defer close(renameDone) | |||
| 551 | + | sess.mu.Lock() | |||
| 552 | + | sess.name = "renamed" | |||
| 553 | + | sess.mu.Unlock() | |||
| 554 | + | close(renamed) | |||
| 555 | + | for { | |||
| 556 | + | select { | |||
| 557 | + | case <-stopRename: | |||
| 558 | + | return | |||
| 559 | + | default: | |||
| 560 | + | } | |||
| 561 | + | sess.mu.Lock() | |||
| 562 | + | sess.name = "renamed" | |||
| 563 | + | sess.mu.Unlock() | |||
| 564 | + | } | |||
| 565 | + | }() | |||
| 566 | + | <-renamed | |||
| 567 | + | close(allowOpen) | |||
| 568 | + | select { | |||
| 569 | + | case <-handled: | |||
| 570 | + | case <-time.After(time.Second): | |||
| 571 | + | t.Fatal("installed floating pane was not logged") | |||
| 572 | + | } | |||
| 573 | + | close(stopRename) | |||
| 574 | + | <-renameDone | |||
| 575 | + | require.Contains(t, logs.String(), "session=renamed", logs.String()) | |||
| 576 | + | ||||
| 577 | + | d.teardownFloating(tb) | |||
| 578 | + | d.sessWg.Wait() | |||
| 579 | + | } | |||
| 580 | + | ||||
| 526 | func TestFloatingToggleUsesVisibleTarget(t *testing.T) { | 581 | func TestFloatingToggleUsesVisibleTarget(t *testing.T) { | ||
| 527 | d := newTestDaemon(t, nil, stubClock{}) | 582 | d := newTestDaemon(t, nil, stubClock{}) | ||
| 528 | tb := newFloatingTestTab(t) | 583 | tb := newFloatingTestTab(t) | ||
M internal/usecase/daemon/session.go +4 -1
| @@ -409,7 +409,10 @@ func (d *Daemon) startTabGoroutines(sess *session, tb *tab) { | |||
| 409 | 409 | ||
| 410 | 410 | func (d *Daemon) startPaneGoroutines(sess *session, tb *tab, p *pane) { | |
| 411 | 411 | if p != nil { | |
| 412 | - | d.log.Info("pane created", "session", sess.name, "pane", p.id) | |
| 412 | + | sess.mu.Lock() | |
| 413 | + | name := sess.name | |
| 414 | + | sess.mu.Unlock() | |
| 415 | + | d.log.Info("pane created", "session", name, "pane", p.id) | |
| 413 | 416 | } | |
| 414 | 417 | d.sessWg.Add(2) | |
| 415 | 418 | go d.ptyReader(sess, tb, p) | |
| @@ -409,7 +409,10 @@ func (d *Daemon) startTabGoroutines(sess *session, tb *tab) { | |||||
| 409 | 409 | ||||
| 410 | func (d *Daemon) startPaneGoroutines(sess *session, tb *tab, p *pane) { | 410 | func (d *Daemon) startPaneGoroutines(sess *session, tb *tab, p *pane) { | ||
| 411 | if p != nil { | 411 | if p != nil { | ||
| 412 | - | d.log.Info("pane created", "session", sess.name, "pane", p.id) | 412 | + | sess.mu.Lock() |
| 413 | + | name := sess.name | |||
| 414 | + | sess.mu.Unlock() | |||
| 415 | + | d.log.Info("pane created", "session", name, "pane", p.id) | |||
| 413 | } | 416 | } | ||
| 414 | d.sessWg.Add(2) | 417 | d.sessWg.Add(2) | ||
| 415 | go d.ptyReader(sess, tb, p) | 418 | go d.ptyReader(sess, tb, p) | ||