Files
mesh-host/internal/profile/power_test.go
T
jochen 41b3392789
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Count the same meters the power module's sampler reads (hq ADR 0255)
An APU's integrated graphics and Intel's second (MMIO) view of a package made a machine
report a power meter the sampler would not read, so its bar could only say not
measured.
2026-10-08 14:44:55 +02:00

134 lines
4.9 KiB
Go

package profile
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
)
// Defends novox/hq ADR 0255: a machine says whether a battery powers it and whether anything on it
// measures the power it draws, read from the kernel, so the controller composes a battery's or a power
// draw's block only where there is one.
func write(t *testing.T, path, content string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(content+"\n"), 0o644); err != nil {
t.Fatal(err)
}
}
// sysfs points every power source at a directory of its own, empty unless a case fills it.
func sysfs(t *testing.T) string {
t.Helper()
dir := t.TempDir()
old := [4]string{PowerSupplySource, PowercapSource, SeatSource, NvidiaSource}
PowerSupplySource = filepath.Join(dir, "power_supply")
PowercapSource = filepath.Join(dir, "powercap")
SeatSource = filepath.Join(dir, "drm")
NvidiaSource = filepath.Join(dir, "nvidia")
t.Cleanup(func() { PowerSupplySource, PowercapSource, SeatSource, NvidiaSource = old[0], old[1], old[2], old[3] })
return dir
}
func TestALaptopHasABatteryAndAMeter(t *testing.T) {
dir := sysfs(t)
write(t, dir+"/power_supply/ACAD/type", "Mains")
write(t, dir+"/power_supply/BAT1/type", "Battery")
write(t, dir+"/power_supply/BAT1/scope", "System")
write(t, dir+"/power_supply/BAT1/present", "1")
write(t, dir+"/powercap/intel-rapl:0/name", "package-0")
write(t, dir+"/powercap/intel-rapl:0/energy_uj", "1")
b := battery{}.Detect(context.Background())
if !b.Present || b.Detail != "BAT1" {
t.Fatalf("battery: %+v", b)
}
m := powerMeter{}.Detect(context.Background())
if !m.Present || m.Detail != "battery BAT1, processor package-0" {
t.Fatalf("power meter: %+v", m)
}
}
func TestADevicesBatteryIsNotTheMachines(t *testing.T) {
// The case worth a test: a desktop with a wireless mouse reports a Battery, and a battery block
// composed for it would show the mouse's charge as the machine's.
dir := sysfs(t)
write(t, dir+"/power_supply/hidpp_battery_0/type", "Battery")
write(t, dir+"/power_supply/hidpp_battery_0/scope", "Device")
write(t, dir+"/power_supply/BAT0/type", "Battery")
write(t, dir+"/power_supply/BAT0/present", "0")
if b := (battery{}).Detect(context.Background()); b.Present {
t.Fatalf("a mouse's battery, or an empty bay, was taken for the machine's: %+v", b)
}
if m := (powerMeter{}).Detect(context.Background()); m.Present {
t.Fatalf("a device's battery was taken for a meter: %+v", m)
}
}
func TestADesktopMeasuresItsProcessorAndItsGraphics(t *testing.T) {
dir := sysfs(t)
write(t, dir+"/powercap/intel-rapl:0/name", "package-0")
write(t, dir+"/powercap/intel-rapl:0/energy_uj", "1")
// A sub-domain is part of the package, not a second meter.
write(t, dir+"/powercap/intel-rapl:0:0/name", "core")
write(t, dir+"/powercap/intel-rapl:0:0/energy_uj", "1")
write(t, dir+"/drm/card1/device/hwmon/hwmon4/power1_average", "8000000")
write(t, dir+"/drm/card1/device/mem_info_vram_vendor", "samsung")
write(t, dir+"/nvidia/0000:01:00.0/information", "x")
if b := (battery{}).Detect(context.Background()); b.Present {
t.Fatalf("a desktop has a battery: %+v", b)
}
m := powerMeter{}.Detect(context.Background())
if !m.Present || m.Detail != "processor package-0, graphics card1, graphics nvidia" {
t.Fatalf("power meter: %+v", m)
}
}
func TestAMachineWithNothingToReadHasNeither(t *testing.T) {
sysfs(t)
b, m := battery{}.Detect(context.Background()), powerMeter{}.Detect(context.Background())
if b.Present || m.Present || b.How == "" || m.How == "" || !strings.Contains(m.Detail, "nothing") {
t.Fatalf("%+v %+v", b, m)
}
}
func TestAPackageShownTwiceIsOneMeterAndIntegratedGraphicsAreNone(t *testing.T) {
dir := sysfs(t)
write(t, dir+"/powercap/intel-rapl:0/name", "package-0")
write(t, dir+"/powercap/intel-rapl:0/energy_uj", "1")
write(t, dir+"/powercap/intel-rapl-mmio:0/name", "package-0")
write(t, dir+"/powercap/intel-rapl-mmio:0/energy_uj", "1")
// An APU's graphics: power reported, no memory of its own.
write(t, dir+"/drm/card1/device/hwmon/hwmon5/power1_average", "19000000")
m := powerMeter{}.Detect(context.Background())
if !m.Present || m.Detail != "processor package-0" {
t.Fatalf("power meter: %+v", m)
}
}
func TestOnlyIntegratedGraphicsMeasureNothing(t *testing.T) {
dir := sysfs(t)
write(t, dir+"/drm/card0/device/hwmon/hwmon1/power1_input", "5000000")
if m := (powerMeter{}).Detect(context.Background()); m.Present {
t.Fatalf("integrated graphics were taken for a meter: %+v", m)
}
}
func TestABatteryOfUnknownScopeIsTheMachines(t *testing.T) {
// Many laptops' firmware says nothing of scope; the kernel reports Unknown. Only Device is a
// peripheral's.
dir := sysfs(t)
write(t, dir+"/power_supply/BAT0/type", "Battery")
write(t, dir+"/power_supply/BAT0/scope", "Unknown")
if b := (battery{}).Detect(context.Background()); !b.Present || b.Detail != "BAT0" {
t.Fatalf("battery: %+v", b)
}
}