1package daemon
2
3import (
4 "context"
5 "strings"
6 "testing"
7 "time"
8
9 vt "github.com/bnema/vev-vt"
10 "github.com/bnema/vev/internal/domain"
11 portsmocks "github.com/bnema/vev/internal/ports/mocks"
12 "github.com/stretchr/testify/require"
13)
14
15func TestHistoryPolicyReloadIncludesHiddenFloatingPanes(t *testing.T) {
16 d := newTestDaemon(t, nil, stubClock{})
17 sess := addControlSession(d, "history", "tab", "pane")
18 tb := sess.tabs[0]
19 primary := tb.focusedPane()
20 floating := newPane("floating", nil, domain.Size{Cols: 80, Rows: 24})
21 installTestFloating(tb, floating, false)
22 panes := []*pane{primary, floating}
23 views := make([]vt.HistoryView, len(panes))
24 for i, p := range panes {
25 for _, text := range []string{"a", "b", "c"} {
26 appendHistoryRow(t, p.history, testRow(text))
27 }
28 views[i] = p.history.View()
29 }
30 cfg := domain.Defaults()
31 cfg.Scrollback = domain.ScrollbackConfig{Megabytes: 1, Lines: 2}
32 d.ApplyConfig(cfg)
33 for i, p := range panes {
34 require.Equal(t, 2, p.history.Len())
35 require.Equal(t, uint64(1_000_000), p.history.ByteCap())
36 require.Equal(t, "b", cellsString(p.history.View().Row(0)))
37 require.Equal(t, 3, views[i].Len(), "reload must preserve captured documents")
38 }
39 cfg.Scrollback.Megabytes = 0
40 d.ApplyConfig(cfg)
41 for _, p := range panes {
42 require.Zero(t, p.history.Len())
43 require.Zero(t, p.history.Cap())
44 }
45 cfg.Scrollback = domain.ScrollbackConfig{Megabytes: 1}
46 d.ApplyConfig(cfg)
47 created := newPaneWithStableIDAndTitle("new", "stable-new", nil, domain.Size{Cols: 80, Rows: 24}, "shell", d.currentHistoryConfig())
48 require.Zero(t, created.history.Cap())
49 require.Equal(t, uint64(1_000_000), created.history.ByteCap())
50}
51
52func TestHistoryCompressionSkipsBusyPanesAndRemainsBounded(t *testing.T) {
53 d := newTestDaemon(t, nil, stubClock{})
54 sess := addControlSession(d, "history", "tab", "pane")
55 p := sess.tabs[0].focusedPane()
56 installTestHistory(p, vt.HistoryConfig{MaxRows: 64, MaxBytes: 1 << 20, ChunkRows: 16})
57 for range 48 {
58 appendHistoryRow(t, p.history, testRow(strings.Repeat("a", 80)))
59 }
60 before := p.history.View()
61 p.mu.Lock()
62 d.compressIdleHistories() // TryLock must not wait for the reader's lock.
63 p.mu.Unlock()
64 require.Zero(t, p.history.CompressionStats().ColdPages)
65 for range 4 {
66 d.compressIdleHistories()
67 }
68 require.Equal(t, 2, p.history.CompressionStats().ColdPages)
69 require.Equal(t, 'a', before.Cell(0, 0).Rune)
70 require.Equal(t, 48, p.history.Len())
71}
72
73func TestHistoryMaintenanceStopsItsTimerOnCancellation(t *testing.T) {
74 clock := portsmocks.NewMockClock(t)
75 timer := portsmocks.NewMockTimer(t)
76 clock.EXPECT().NewTimer(historyMaintenanceInterval).Return(timer).Once()
77 timer.EXPECT().C().Return(make(<-chan time.Time)).Once()
78 timer.EXPECT().Stop().Return(true).Once()
79 ctx, cancel := context.WithCancel(t.Context())
80 cancel()
81 d := &Daemon{clock: clock}
82 d.historyMaintenance(ctx)
83}