forked from wrenn/wrenn
Co-authored-by: Tasnim Kabir Sadik <tksadik@omukk.dev> Reviewed-on: wrenn/wrenn#55 Co-authored-by: pptx704 <rafeed@omukk.dev> Co-committed-by: pptx704 <rafeed@omukk.dev>
112 lines
3.5 KiB
Go
112 lines
3.5 KiB
Go
package sandbox
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.omukk.dev/wrenn/wrenn/internal/envdclient"
|
|
)
|
|
|
|
func TestIsBusy(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
cfg Config
|
|
act envdclient.Activity
|
|
want bool
|
|
}{
|
|
// Default thresholds (zero cfg → defaults: cpu 5%, net 16K, disk 32K).
|
|
{"idle", Config{}, envdclient.Activity{CPUUsedPct: 0.5, NetBps: 100, DiskBps: 200}, false},
|
|
{"cpu just below", Config{}, envdclient.Activity{CPUUsedPct: 4.99}, false},
|
|
{"cpu at threshold", Config{}, envdclient.Activity{CPUUsedPct: 5.0}, true},
|
|
{"cpu above", Config{}, envdclient.Activity{CPUUsedPct: 80.0}, true},
|
|
{"net just below", Config{}, envdclient.Activity{NetBps: 16*1024 - 1}, false},
|
|
{"net at floor", Config{}, envdclient.Activity{NetBps: 16 * 1024}, true},
|
|
{"disk just below", Config{}, envdclient.Activity{DiskBps: 32*1024 - 1}, false},
|
|
{"disk at floor", Config{}, envdclient.Activity{DiskBps: 32 * 1024}, true},
|
|
{"download: low cpu, high net", Config{}, envdclient.Activity{CPUUsedPct: 1.0, NetBps: 5 * 1024 * 1024}, true},
|
|
|
|
// Explicit overrides take precedence over defaults.
|
|
{
|
|
"custom cpu threshold met",
|
|
Config{CPUBusyPct: 20.0},
|
|
envdclient.Activity{CPUUsedPct: 25.0},
|
|
true,
|
|
},
|
|
{
|
|
"custom cpu threshold not met",
|
|
Config{CPUBusyPct: 20.0},
|
|
envdclient.Activity{CPUUsedPct: 10.0},
|
|
false,
|
|
},
|
|
{
|
|
"custom net floor not met",
|
|
Config{NetFloorBps: 1024 * 1024},
|
|
envdclient.Activity{NetBps: 16 * 1024},
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m := &Manager{cfg: tt.cfg}
|
|
if got := m.isBusy(&tt.act); got != tt.want {
|
|
t.Errorf("isBusy(%+v) = %v, want %v", tt.act, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestApplyBusySample(t *testing.T) {
|
|
// Debounce requires busyDebounceSamples consecutive busy samples before the
|
|
// first bump. Verify the streak math and bump timing.
|
|
if busyDebounceSamples != 2 {
|
|
t.Skip("test written for busyDebounceSamples=2")
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
startStreak int
|
|
busy bool
|
|
wantStreak int
|
|
wantBump bool
|
|
}{
|
|
{"first busy, no bump yet", 0, true, 1, false},
|
|
{"second consecutive busy, bump", 1, true, 2, true},
|
|
{"sustained busy keeps bumping, streak held", 2, true, 2, true},
|
|
{"single noise spike from idle, no bump", 0, false, 0, false},
|
|
{"idle resets a building streak", 1, false, 0, false},
|
|
{"idle resets a saturated streak", 2, false, 0, false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
gotStreak, gotBump := applyBusySample(tt.startStreak, tt.busy)
|
|
if gotStreak != tt.wantStreak || gotBump != tt.wantBump {
|
|
t.Errorf("applyBusySample(%d, %v) = (%d, %v), want (%d, %v)",
|
|
tt.startStreak, tt.busy, gotStreak, gotBump, tt.wantStreak, tt.wantBump)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestApplyBusySample_NoiseScenario walks a realistic sample sequence: brief
|
|
// noise never crosses the debounce, but sustained work does and then a return
|
|
// to idle resets — proving an isolated spike cannot keep a sandbox alive.
|
|
func TestApplyBusySample_NoiseScenario(t *testing.T) {
|
|
if busyDebounceSamples != 2 {
|
|
t.Skip("test written for busyDebounceSamples=2")
|
|
}
|
|
|
|
samples := []bool{true, false, false, true, true, true, false}
|
|
wantBumps := []bool{false, false, false, false, true, true, false}
|
|
|
|
streak := 0
|
|
for i, busy := range samples {
|
|
var bump bool
|
|
streak, bump = applyBusySample(streak, busy)
|
|
if bump != wantBumps[i] {
|
|
t.Errorf("sample %d (busy=%v): bump = %v, want %v (streak=%d)",
|
|
i, busy, bump, wantBumps[i], streak)
|
|
}
|
|
}
|
|
}
|