- cpu.go: collectCPUs(), parseCPUs(), parseCPUSection() - splitDMISections(): splits multi-section dmidecode output generically - parseFieldLines(): reusable key→value parser for DMI sections - parseCPUStatus(): Populated/Unpopulated → OK/WARNING/EMPTY/UNKNOWN - parseSocketIndex(): CPU0/Processor 1/Socket 2 → integer - cleanManufacturer(): strips (R), Corporation, Inc. suffixes - parseMHz(), parseInt(): field value parsers - Serial fallback: <board_serial>-CPU-<socket> when DMI serial absent - readMicrocode(): /sys/devices/system/cpu/cpu0/microcode/version - cpu_test.go: dual-socket, unpopulated skipped, status, socket, manufacturer, MHz Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
144 lines
3.7 KiB
Go
144 lines
3.7 KiB
Go
package collector
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestParseCPUs_dual_socket(t *testing.T) {
|
|
out := mustReadFile(t, "testdata/dmidecode_type4.txt")
|
|
cpus := parseCPUs(out, "CAR315KA0803B90")
|
|
|
|
if len(cpus) != 2 {
|
|
t.Fatalf("expected 2 CPUs, got %d", len(cpus))
|
|
}
|
|
|
|
cpu0 := cpus[0]
|
|
if cpu0.Socket == nil || *cpu0.Socket != 0 {
|
|
t.Errorf("cpu0 socket: got %v, want 0", cpu0.Socket)
|
|
}
|
|
if cpu0.Model == nil || *cpu0.Model != "Intel(R) Xeon(R) Gold 6530" {
|
|
t.Errorf("cpu0 model: got %v", cpu0.Model)
|
|
}
|
|
if cpu0.Manufacturer == nil || *cpu0.Manufacturer != "Intel" {
|
|
t.Errorf("cpu0 manufacturer: got %v, want Intel", cpu0.Manufacturer)
|
|
}
|
|
if cpu0.Cores == nil || *cpu0.Cores != 32 {
|
|
t.Errorf("cpu0 cores: got %v, want 32", cpu0.Cores)
|
|
}
|
|
if cpu0.Threads == nil || *cpu0.Threads != 64 {
|
|
t.Errorf("cpu0 threads: got %v, want 64", cpu0.Threads)
|
|
}
|
|
if cpu0.MaxFrequencyMHz == nil || *cpu0.MaxFrequencyMHz != 4000 {
|
|
t.Errorf("cpu0 max_frequency_mhz: got %v, want 4000", cpu0.MaxFrequencyMHz)
|
|
}
|
|
if cpu0.FrequencyMHz == nil || *cpu0.FrequencyMHz != 2100 {
|
|
t.Errorf("cpu0 frequency_mhz: got %v, want 2100", cpu0.FrequencyMHz)
|
|
}
|
|
if cpu0.Status == nil || *cpu0.Status != "OK" {
|
|
t.Errorf("cpu0 status: got %v, want OK", cpu0.Status)
|
|
}
|
|
// Intel Xeon serial not available → fallback
|
|
if cpu0.SerialNumber == nil || *cpu0.SerialNumber != "CAR315KA0803B90-CPU-0" {
|
|
t.Errorf("cpu0 serial fallback: got %v, want CAR315KA0803B90-CPU-0", cpu0.SerialNumber)
|
|
}
|
|
|
|
cpu1 := cpus[1]
|
|
if cpu1.Socket == nil || *cpu1.Socket != 1 {
|
|
t.Errorf("cpu1 socket: got %v, want 1", cpu1.Socket)
|
|
}
|
|
if cpu1.SerialNumber == nil || *cpu1.SerialNumber != "CAR315KA0803B90-CPU-1" {
|
|
t.Errorf("cpu1 serial fallback: got %v, want CAR315KA0803B90-CPU-1", cpu1.SerialNumber)
|
|
}
|
|
}
|
|
|
|
func TestParseCPUs_unpopulated_skipped(t *testing.T) {
|
|
out := mustReadFile(t, "testdata/dmidecode_type4_disabled.txt")
|
|
cpus := parseCPUs(out, "BOARD-001")
|
|
|
|
if len(cpus) != 1 {
|
|
t.Fatalf("expected 1 CPU (unpopulated skipped), got %d", len(cpus))
|
|
}
|
|
if cpus[0].Socket == nil || *cpus[0].Socket != 0 {
|
|
t.Errorf("expected socket 0, got %v", cpus[0].Socket)
|
|
}
|
|
}
|
|
|
|
func TestParseCPUStatus(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
want string
|
|
}{
|
|
{"Populated, Enabled", "OK"},
|
|
{"Populated, Disabled By User", "WARNING"},
|
|
{"Populated, Disabled By BIOS", "WARNING"},
|
|
{"Unpopulated", "EMPTY"},
|
|
{"Not Populated", "EMPTY"},
|
|
{"Unknown", "UNKNOWN"},
|
|
{"", "UNKNOWN"},
|
|
}
|
|
for _, tt := range tests {
|
|
got := parseCPUStatus(tt.input)
|
|
if got != tt.want {
|
|
t.Errorf("parseCPUStatus(%q) = %q, want %q", tt.input, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseSocketIndex(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
want int
|
|
ok bool
|
|
}{
|
|
{"CPU0", 0, true},
|
|
{"CPU1", 1, true},
|
|
{"Processor 1", 1, true},
|
|
{"Socket 2", 2, true},
|
|
{"", 0, false},
|
|
{"No digits here", 0, false},
|
|
}
|
|
for _, tt := range tests {
|
|
got, ok := parseSocketIndex(tt.input)
|
|
if ok != tt.ok || got != tt.want {
|
|
t.Errorf("parseSocketIndex(%q) = (%d, %v), want (%d, %v)", tt.input, got, ok, tt.want, tt.ok)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCleanManufacturer(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
want string
|
|
}{
|
|
{"Intel(R) Corporation", "Intel"},
|
|
{"AMD", "AMD"},
|
|
{"To Be Filled By O.E.M.", ""},
|
|
{" Intel(R) Corporation ", "Intel"},
|
|
}
|
|
for _, tt := range tests {
|
|
got := cleanManufacturer(tt.input)
|
|
if got != tt.want {
|
|
t.Errorf("cleanManufacturer(%q) = %q, want %q", tt.input, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseMHz(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
want int
|
|
}{
|
|
{"4000 MHz", 4000},
|
|
{"2100 MHz", 2100},
|
|
{"Unknown", 0},
|
|
{"", 0},
|
|
{"N/A", 0},
|
|
}
|
|
for _, tt := range tests {
|
|
got := parseMHz(tt.input)
|
|
if got != tt.want {
|
|
t.Errorf("parseMHz(%q) = %d, want %d", tt.input, got, tt.want)
|
|
}
|
|
}
|
|
}
|