test(copy): stabilize allocation budget gate
- commit
- cdb77a1ca6ef06825373274f27b79fa19c6f9a45
- author
- brice · · 2026-07-12 22:03 UTC
- parent
- e53346f
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +34 -2
A internal/usecase/daemon/copy_enter_allocation_budget_race_test.go +8 -0
| @@ -0,0 +1,8 @@ | |||
| 1 | + | //go:build race | |
| 2 | + | ||
| 3 | + | package daemon | |
| 4 | + | ||
| 5 | + | // Race instrumentation allocations are not part of the parent benchmark's | |
| 6 | + | // allocation baseline. TestCopyEnterAllocationBudget still exercises copy | |
| 7 | + | // entry and validates its result under -race. | |
| 8 | + | const copyEnterAllocationBudgetEnabled = false | |
| @@ -0,0 +1,8 @@ | |||||
| 1 | + | //go:build race | |||
| 2 | + | ||||
| 3 | + | package daemon | |||
| 4 | + | ||||
| 5 | + | // Race instrumentation allocations are not part of the parent benchmark's | |||
| 6 | + | // allocation baseline. TestCopyEnterAllocationBudget still exercises copy | |||
| 7 | + | // entry and validates its result under -race. | |||
| 8 | + | const copyEnterAllocationBudgetEnabled = false | |||
A internal/usecase/daemon/copy_enter_allocation_budget_test.go +7 -0
| @@ -0,0 +1,7 @@ | |||
| 1 | + | //go:build !race | |
| 2 | + | ||
| 3 | + | package daemon | |
| 4 | + | ||
| 5 | + | // copyEnterAllocationBudgetEnabled keeps the allocation gate aligned with the | |
| 6 | + | // non-race parent benchmark, whose allocation baseline defines the budget. | |
| 7 | + | const copyEnterAllocationBudgetEnabled = true | |
| @@ -0,0 +1,7 @@ | |||||
| 1 | + | //go:build !race | |||
| 2 | + | ||||
| 3 | + | package daemon | |||
| 4 | + | ||||
| 5 | + | // copyEnterAllocationBudgetEnabled keeps the allocation gate aligned with the | |||
| 6 | + | // non-race parent benchmark, whose allocation baseline defines the budget. | |||
| 7 | + | const copyEnterAllocationBudgetEnabled = true | |||
M internal/usecase/daemon/performance_benchmark_test.go +19 -2
| @@ -539,10 +539,21 @@ func TestCopyEnterAllocationBudget(t *testing.T) { | |||
| 539 | 539 | } { | |
| 540 | 540 | t.Run(tt.name, func(t *testing.T) { | |
| 541 | 541 | fixture := newPerformanceFixture(t, performanceConfig{size: domain.Size{Cols: 120, Rows: 40}, tabs: tt.tabs, panes: tt.panes, historyRows: 10_000}) | |
| 542 | - | allocs := testing.AllocsPerRun(20, func() { | |
| 542 | + | run := func() { | |
| 543 | 543 | fixture.d.enterCopyMode(fixture.sess, fixture.ac) | |
| 544 | 544 | fixture.ac.ackOutputState(fixture.ac.output.next) | |
| 545 | - | }) | |
| 545 | + | } | |
| 546 | + | ||
| 547 | + | // Always exercise and validate copy entry. The race detector adds | |
| 548 | + | // instrumentation allocations which are not reported by the parent | |
| 549 | + | // benchmark, so its allocation count cannot represent this budget. | |
| 550 | + | run() | |
| 551 | + | require.True(t, fixture.copyModeActive(), "copy entry must install a history-backed mode") | |
| 552 | + | if !copyEnterAllocationBudgetEnabled { | |
| 553 | + | return | |
| 554 | + | } | |
| 555 | + | ||
| 556 | + | allocs := testing.AllocsPerRun(20, run) | |
| 546 | 557 | require.LessOrEqual(t, allocs, float64(tt.max), "copy enter must stay within 10%% of the parent allocation baseline") | |
| 547 | 558 | }) | |
| 548 | 559 | } | |
| @@ -869,6 +880,12 @@ func (f *performanceFixture) searchMatches() int { | |||
| 869 | 880 | return len(f.ac.overlays.copyMode.Searches) | |
| 870 | 881 | } | |
| 871 | 882 | ||
| 883 | + | func (f *performanceFixture) copyModeActive() bool { | |
| 884 | + | f.ac.overlays.copyMu.Lock() | |
| 885 | + | defer f.ac.overlays.copyMu.Unlock() | |
| 886 | + | return f.ac.overlays.copyMode != nil && f.ac.overlays.copySnapshot != nil && f.ac.overlays.copySnapshot.Len() >= 10_000 | |
| 887 | + | } | |
| 888 | + | ||
| 872 | 889 | func (f *performanceFixture) resize() { | |
| 873 | 890 | f.resizeTo(f.resizeSizes[f.resizes%len(f.resizeSizes)]) | |
| 874 | 891 | } | |
| @@ -539,10 +539,21 @@ func TestCopyEnterAllocationBudget(t *testing.T) { | |||||
| 539 | } { | 539 | } { | ||
| 540 | t.Run(tt.name, func(t *testing.T) { | 540 | t.Run(tt.name, func(t *testing.T) { | ||
| 541 | fixture := newPerformanceFixture(t, performanceConfig{size: domain.Size{Cols: 120, Rows: 40}, tabs: tt.tabs, panes: tt.panes, historyRows: 10_000}) | 541 | fixture := newPerformanceFixture(t, performanceConfig{size: domain.Size{Cols: 120, Rows: 40}, tabs: tt.tabs, panes: tt.panes, historyRows: 10_000}) | ||
| 542 | - | allocs := testing.AllocsPerRun(20, func() { | 542 | + | run := func() { |
| 543 | fixture.d.enterCopyMode(fixture.sess, fixture.ac) | 543 | fixture.d.enterCopyMode(fixture.sess, fixture.ac) | ||
| 544 | fixture.ac.ackOutputState(fixture.ac.output.next) | 544 | fixture.ac.ackOutputState(fixture.ac.output.next) | ||
| 545 | - | }) | 545 | + | } |
| 546 | + | ||||
| 547 | + | // Always exercise and validate copy entry. The race detector adds | |||
| 548 | + | // instrumentation allocations which are not reported by the parent | |||
| 549 | + | // benchmark, so its allocation count cannot represent this budget. | |||
| 550 | + | run() | |||
| 551 | + | require.True(t, fixture.copyModeActive(), "copy entry must install a history-backed mode") | |||
| 552 | + | if !copyEnterAllocationBudgetEnabled { | |||
| 553 | + | return | |||
| 554 | + | } | |||
| 555 | + | ||||
| 556 | + | allocs := testing.AllocsPerRun(20, run) | |||
| 546 | require.LessOrEqual(t, allocs, float64(tt.max), "copy enter must stay within 10%% of the parent allocation baseline") | 557 | require.LessOrEqual(t, allocs, float64(tt.max), "copy enter must stay within 10%% of the parent allocation baseline") | ||
| 547 | }) | 558 | }) | ||
| 548 | } | 559 | } | ||
| @@ -869,6 +880,12 @@ func (f *performanceFixture) searchMatches() int { | |||||
| 869 | return len(f.ac.overlays.copyMode.Searches) | 880 | return len(f.ac.overlays.copyMode.Searches) | ||
| 870 | } | 881 | } | ||
| 871 | 882 | ||||
| 883 | + | func (f *performanceFixture) copyModeActive() bool { | |||
| 884 | + | f.ac.overlays.copyMu.Lock() | |||
| 885 | + | defer f.ac.overlays.copyMu.Unlock() | |||
| 886 | + | return f.ac.overlays.copyMode != nil && f.ac.overlays.copySnapshot != nil && f.ac.overlays.copySnapshot.Len() >= 10_000 | |||
| 887 | + | } | |||
| 888 | + | ||||
| 872 | func (f *performanceFixture) resize() { | 889 | func (f *performanceFixture) resize() { | ||
| 873 | f.resizeTo(f.resizeSizes[f.resizes%len(f.resizeSizes)]) | 890 | f.resizeTo(f.resizeSizes[f.resizes%len(f.resizeSizes)]) | ||
| 874 | } | 891 | } | ||