detached 8aba936

fix(daemon): keep benchmark workloads representative

commit
8aba936c86b12b74795f15d5a2332d5828899d8b
author
brice · · 2026-07-11 05:37 UTC
committer
brice · · 2026-07-11 06:15 UTC
parent
b590931
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +26 -14
M internal/usecase/daemon/performance_benchmark_test.go +26 -8
@@ -36,6 +36,16 @@ func TestPerformanceFixtureCounters(t *testing.T) {
3636 require.True(t, fixture.resized())
3737 }
3838
39+func TestPerformanceFixtureResizeAlternatesRealDimensions(t *testing.T) {
40+ fixture := newPerformanceFixture(t, performanceConfig{size: domain.Size{Cols: 120, Rows: 40}})
41+
42+ for _, want := range []domain.Size{{Cols: 100, Rows: 30}, {Cols: 120, Rows: 40}} {
43+ fixture.resize()
44+ require.Equal(t, want, fixture.ac.size)
45+ require.True(t, fixture.resized())
46+ }
47+}
48+
3949 func TestPerformanceFixtureLargeHistoryTopology(t *testing.T) {
4050 fixture := newPerformanceFixture(t, performanceConfig{size: domain.Size{Cols: 120, Rows: 40}, tabs: 4, panes: 4, historyRows: 10_000})
4151
@@ -95,6 +105,9 @@ func benchmarkDaemonLargeHistory(b *testing.B, workload string, run func(*perfor
95105 if workload == "live-paint" && metrics.outputFrames != uint64(b.N) {
96106 b.Fatalf("live paint emitted %d frames for %d operations", metrics.outputFrames, b.N)
97107 }
108+ if workload == "snapshot-capture" && metrics.snapshotWrites != uint64(b.N) {
109+ b.Fatalf("snapshot capture wrote %d snapshots for %d operations", metrics.snapshotWrites, b.N)
110+ }
98111 benchmarkReportMetrics(b, metrics, b.N, 10_000)
99112 })
100113 }
@@ -109,6 +122,7 @@ func benchmarkReportMetrics(b *testing.B, metrics performanceMetrics, operations
109122 b.ReportMetric(float64(metrics.outputFrames)/perOperation, "outputframes/op")
110123 b.ReportMetric(float64(metrics.outputBytes)/perOperation, "outputbytes/op")
111124 b.ReportMetric(float64(metrics.outputPayloadBytes)/perOperation, "framepayloadbytes/op")
125+ b.ReportMetric(float64(metrics.snapshotWrites)/perOperation, "snapshotwrites/op")
112126 b.ReportMetric(float64(metrics.snapshotBytes)/perOperation, "snapshotbytes/op")
113127 b.ReportMetric(float64(historyRows), "historyrows/pane")
114128 }
@@ -155,6 +169,8 @@ type performanceFixture struct {
155169 activePane *pane
156170 liveWrites [][]byte
157171 paints int
172+ resizeSizes [2]domain.Size
173+ resizes int
158174 resizedSize domain.Size
159175 }
160176
@@ -182,13 +198,14 @@ func newPerformanceFixture(t testing.TB, config performanceConfig) *performanceF
182198 sess.snapEligible.Store(true)
183199
184200 fixture := &performanceFixture{
185- t: t,
186- d: d,
187- sess: sess,
188- ac: ac,
189- output: output,
190- snaps: &countingSnapshotStore{},
191- liveWrites: [][]byte{[]byte("\x1b[1;1HA\x1b[2;2HA"), []byte("\x1b[1;1HB\x1b[2;2HB")},
201+ t: t,
202+ d: d,
203+ sess: sess,
204+ ac: ac,
205+ output: output,
206+ snaps: &countingSnapshotStore{},
207+ liveWrites: [][]byte{[]byte("\x1b[1;1HA\x1b[2;2HA"), []byte("\x1b[1;1HB\x1b[2;2HB")},
208+ resizeSizes: [2]domain.Size{{Cols: 100, Rows: 30}, config.size},
192209 }
193210 WithSnapshotStore(fixture.snaps)(d)
194211 for tabIndex, tb := range sess.tabs {
@@ -279,7 +296,8 @@ func (f *performanceFixture) searchMatches() int {
279296 }
280297
281298 func (f *performanceFixture) resize() {
282- f.resizedSize = domain.Size{Cols: 100, Rows: 30}
299+ f.resizedSize = f.resizeSizes[f.resizes%len(f.resizeSizes)]
300+ f.resizes++
283301 f.d.resize(f.sess, f.ac, f.resizedSize)
284302 }
285303
@@ -36,6 +36,16 @@ func TestPerformanceFixtureCounters(t *testing.T) {
36 require.True(t, fixture.resized())36 require.True(t, fixture.resized())
37 }37 }
38 38
39+func TestPerformanceFixtureResizeAlternatesRealDimensions(t *testing.T) {
40+ fixture := newPerformanceFixture(t, performanceConfig{size: domain.Size{Cols: 120, Rows: 40}})
41+
42+ for _, want := range []domain.Size{{Cols: 100, Rows: 30}, {Cols: 120, Rows: 40}} {
43+ fixture.resize()
44+ require.Equal(t, want, fixture.ac.size)
45+ require.True(t, fixture.resized())
46+ }
47+}
48+
39 func TestPerformanceFixtureLargeHistoryTopology(t *testing.T) {49 func TestPerformanceFixtureLargeHistoryTopology(t *testing.T) {
40 fixture := newPerformanceFixture(t, performanceConfig{size: domain.Size{Cols: 120, Rows: 40}, tabs: 4, panes: 4, historyRows: 10_000})50 fixture := newPerformanceFixture(t, performanceConfig{size: domain.Size{Cols: 120, Rows: 40}, tabs: 4, panes: 4, historyRows: 10_000})
41 51
@@ -95,6 +105,9 @@ func benchmarkDaemonLargeHistory(b *testing.B, workload string, run func(*perfor
95 if workload == "live-paint" && metrics.outputFrames != uint64(b.N) {105 if workload == "live-paint" && metrics.outputFrames != uint64(b.N) {
96 b.Fatalf("live paint emitted %d frames for %d operations", metrics.outputFrames, b.N)106 b.Fatalf("live paint emitted %d frames for %d operations", metrics.outputFrames, b.N)
97 }107 }
108+ if workload == "snapshot-capture" && metrics.snapshotWrites != uint64(b.N) {
109+ b.Fatalf("snapshot capture wrote %d snapshots for %d operations", metrics.snapshotWrites, b.N)
110+ }
98 benchmarkReportMetrics(b, metrics, b.N, 10_000)111 benchmarkReportMetrics(b, metrics, b.N, 10_000)
99 })112 })
100 }113 }
@@ -109,6 +122,7 @@ func benchmarkReportMetrics(b *testing.B, metrics performanceMetrics, operations
109 b.ReportMetric(float64(metrics.outputFrames)/perOperation, "outputframes/op")122 b.ReportMetric(float64(metrics.outputFrames)/perOperation, "outputframes/op")
110 b.ReportMetric(float64(metrics.outputBytes)/perOperation, "outputbytes/op")123 b.ReportMetric(float64(metrics.outputBytes)/perOperation, "outputbytes/op")
111 b.ReportMetric(float64(metrics.outputPayloadBytes)/perOperation, "framepayloadbytes/op")124 b.ReportMetric(float64(metrics.outputPayloadBytes)/perOperation, "framepayloadbytes/op")
125+ b.ReportMetric(float64(metrics.snapshotWrites)/perOperation, "snapshotwrites/op")
112 b.ReportMetric(float64(metrics.snapshotBytes)/perOperation, "snapshotbytes/op")126 b.ReportMetric(float64(metrics.snapshotBytes)/perOperation, "snapshotbytes/op")
113 b.ReportMetric(float64(historyRows), "historyrows/pane")127 b.ReportMetric(float64(historyRows), "historyrows/pane")
114 }128 }
@@ -155,6 +169,8 @@ type performanceFixture struct {
155 activePane *pane169 activePane *pane
156 liveWrites [][]byte170 liveWrites [][]byte
157 paints int171 paints int
172+ resizeSizes [2]domain.Size
173+ resizes int
158 resizedSize domain.Size174 resizedSize domain.Size
159 }175 }
160 176
@@ -182,13 +198,14 @@ func newPerformanceFixture(t testing.TB, config performanceConfig) *performanceF
182 sess.snapEligible.Store(true)198 sess.snapEligible.Store(true)
183 199
184 fixture := &performanceFixture{200 fixture := &performanceFixture{
185- t: t,201+ t: t,
186- d: d,202+ d: d,
187- sess: sess,203+ sess: sess,
188- ac: ac,204+ ac: ac,
189- output: output,205+ output: output,
190- snaps: &countingSnapshotStore{},206+ snaps: &countingSnapshotStore{},
191- liveWrites: [][]byte{[]byte("\x1b[1;1HA\x1b[2;2HA"), []byte("\x1b[1;1HB\x1b[2;2HB")},207+ liveWrites: [][]byte{[]byte("\x1b[1;1HA\x1b[2;2HA"), []byte("\x1b[1;1HB\x1b[2;2HB")},
208+ resizeSizes: [2]domain.Size{{Cols: 100, Rows: 30}, config.size},
192 }209 }
193 WithSnapshotStore(fixture.snaps)(d)210 WithSnapshotStore(fixture.snaps)(d)
194 for tabIndex, tb := range sess.tabs {211 for tabIndex, tb := range sess.tabs {
@@ -279,7 +296,8 @@ func (f *performanceFixture) searchMatches() int {
279 }296 }
280 297
281 func (f *performanceFixture) resize() {298 func (f *performanceFixture) resize() {
282- f.resizedSize = domain.Size{Cols: 100, Rows: 30}299+ f.resizedSize = f.resizeSizes[f.resizes%len(f.resizeSizes)]
300+ f.resizes++
283 f.d.resize(f.sess, f.ac, f.resizedSize)301 f.d.resize(f.sess, f.ac, f.resizedSize)
284 }302 }
285 303
M internal/usecase/daemon/render_test.go +0 -6
@@ -107,12 +107,6 @@ func TestPaletteBackdropProductionRenderAndDismissal(t *testing.T) {
107107
108108 // --- test doubles -----------------------------------------------------------
109109
110-type discardTransport struct{}
111-
112-func (discardTransport) Send(ports.Frame) error { return nil }
113-func (discardTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
114-func (discardTransport) Close() error { return nil }
115-
116110 // stubClock returns timers whose channel never fires, so a scheduler under it
117111 // blocks in its debounce loop until the session context is cancelled. Used by
118112
@@ -107,12 +107,6 @@ func TestPaletteBackdropProductionRenderAndDismissal(t *testing.T) {
107 107
108 // --- test doubles -----------------------------------------------------------108 // --- test doubles -----------------------------------------------------------
109 109
110-type discardTransport struct{}
111-
112-func (discardTransport) Send(ports.Frame) error { return nil }
113-func (discardTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
114-func (discardTransport) Close() error { return nil }
115-
116 // stubClock returns timers whose channel never fires, so a scheduler under it110 // stubClock returns timers whose channel never fires, so a scheduler under it
117 // blocks in its debounce loop until the session context is cancelled. Used by111 // blocks in its debounce loop until the session context is cancelled. Used by
118 112

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close