135 lines
3.5 KiB
Go
135 lines
3.5 KiB
Go
package system
|
||
|
||
import (
|
||
"context"
|
||
"os"
|
||
"path/filepath"
|
||
"reflect"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
func TestNewSampler(t *testing.T) {
|
||
s := NewSampler("/proc/stat", time.Second)
|
||
if s == nil {
|
||
t.Fatal("NewSampler returned nil")
|
||
}
|
||
if s.statPath != "/proc/stat" {
|
||
t.Errorf("statPath = %q, want /proc/stat", s.statPath)
|
||
}
|
||
if s.interval != time.Second {
|
||
t.Errorf("interval = %v, want 1s", s.interval)
|
||
}
|
||
}
|
||
|
||
func TestSamplerSnapshotsRealProcStat(t *testing.T) {
|
||
ctx, cancel := context.WithCancel(context.Background())
|
||
defer cancel()
|
||
|
||
s := NewSampler("/proc/stat", 10*time.Millisecond)
|
||
s.Start(ctx)
|
||
// Wait for at least two samples so there is a delta to compute.
|
||
time.Sleep(50 * time.Millisecond)
|
||
|
||
snap := s.Snapshot()
|
||
if snap == nil {
|
||
t.Fatal("Snapshot returned nil (expected at least one sample)")
|
||
}
|
||
if len(snap) == 0 {
|
||
t.Fatal("Snapshot returned empty (expected at least one core)")
|
||
}
|
||
for _, c := range snap {
|
||
if c.UsagePct < 0 || c.UsagePct > 100 {
|
||
t.Errorf("core %d: usage_pct = %f, want 0–100", c.Core, c.UsagePct)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestSamplerReturnsCopy(t *testing.T) {
|
||
ctx, cancel := context.WithCancel(context.Background())
|
||
defer cancel()
|
||
|
||
s := NewSampler("/proc/stat", 10*time.Millisecond)
|
||
s.Start(ctx)
|
||
time.Sleep(50 * time.Millisecond)
|
||
|
||
snap1 := s.Snapshot()
|
||
snap2 := s.Snapshot()
|
||
|
||
// Both should be non-nil and deep-equal.
|
||
if snap1 == nil || snap2 == nil {
|
||
t.Fatal("Snapshot returned nil")
|
||
}
|
||
if !reflect.DeepEqual(snap1, snap2) {
|
||
t.Error("two sequential snapshots should be deep-equal")
|
||
}
|
||
|
||
// Mutating the returned slice should not affect the sampler.
|
||
if len(snap1) > 0 {
|
||
snap1[0].UsagePct = 999
|
||
if snap2[0].UsagePct == 999 {
|
||
t.Error("mutating one snapshot affected the other — expected a copy")
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestSamplerStartOnce(t *testing.T) {
|
||
ctx, cancel := context.WithCancel(context.Background())
|
||
defer cancel()
|
||
|
||
s := NewSampler("/proc/stat", 10*time.Millisecond)
|
||
s.Start(ctx)
|
||
s.Start(ctx) // second call must not panic or create a second goroutine
|
||
time.Sleep(30 * time.Millisecond)
|
||
|
||
if snap := s.Snapshot(); snap == nil {
|
||
t.Fatal("Snapshot returned nil after Start")
|
||
}
|
||
}
|
||
|
||
func TestSamplerContextCancelStopsGoroutine(t *testing.T) {
|
||
ctx, cancel := context.WithCancel(context.Background())
|
||
s := NewSampler("/proc/stat", 10*time.Millisecond)
|
||
s.Start(ctx)
|
||
|
||
time.Sleep(30 * time.Millisecond)
|
||
if snap := s.Snapshot(); snap == nil {
|
||
t.Fatal("Snapshot returned nil before cancel")
|
||
}
|
||
|
||
cancel()
|
||
// Give the goroutine time to exit. If it doesn't, the test will hang.
|
||
time.Sleep(20 * time.Millisecond)
|
||
// Snapshot should still return the last cached value (no panic).
|
||
if snap := s.Snapshot(); snap == nil {
|
||
t.Fatal("Snapshot returned nil after cancel (cache should persist)")
|
||
}
|
||
}
|
||
|
||
func TestSamplerWithFakeStat(t *testing.T) {
|
||
dir := t.TempDir()
|
||
statPath := filepath.Join(dir, "stat")
|
||
content := []byte("cpu 100 20 30 400 10 5 3 2 0 0\ncpu0 50 10 15 200 5 3 1 1 0 0\ncpu1 50 10 15 200 5 2 2 1 0 0\n")
|
||
if err := os.WriteFile(statPath, content, 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
ctx, cancel := context.WithCancel(context.Background())
|
||
defer cancel()
|
||
|
||
s := NewSampler(statPath, 20*time.Millisecond)
|
||
s.Start(ctx)
|
||
time.Sleep(50 * time.Millisecond)
|
||
|
||
snap := s.Snapshot()
|
||
if snap == nil {
|
||
t.Fatal("Snapshot returned nil")
|
||
}
|
||
// With the stat file not changing between reads, deltas are zero → 0% usage.
|
||
for _, c := range snap {
|
||
if c.UsagePct != 0 {
|
||
t.Errorf("core %d: expected 0%% usage (static stat file), got %f", c.Core, c.UsagePct)
|
||
}
|
||
}
|
||
}
|