Files
nadir-agent/internal/modules/system/cpustat_test.go
T
urania d4364a6cb7
build-and-release / release (push) Successful in 2m39s
feat(system): enhance system architecture
2026-06-25 14:44:47 +02:00

135 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 0100", 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)
}
}
}