Files
mesh-catalog/modules/asus-zephyrus-g14/cmd/zephyrus/asus_test.go
T
jochen b098b64b22 Phase 3: asus-zephyrus-g14 and memory-pressure, with Go tools and long-running code
The laptop model's hardware module and a memory-pressure module for any
machine (hq research 027/03, 026/05, to-be 42 phase 3). The predecessor's
polling auto-profile and mem-guard user scripts become each module's own
Go code launched by the node runtime (ADR 0198): a profile switcher woken
by the kernel's power-supply uevents, and a guard that warns on RAM, swap
or PSI before systemd-oomd acts, on the desktop over the account's bus and
always as an event. supergfxctl and triggerhappy are kept as found
(research 027 Q1).
2026-10-04 15:42:56 +02:00

147 lines
4.7 KiB
Go

package main
import (
"context"
"strings"
"testing"
)
// What asusctl 6.4 and supergfxctl 5.2 said on the laptop on 2026-10-04.
const fanCurveQuiet = `
Fan curves for Quiet
[
(
fan: CPU,
pwm: (2, 0, 10, 20, 35, 55, 80, 100),
temp: (35, 45, 50, 55, 60, 65, 70, 80),
enabled: true,
),
(
fan: GPU,
pwm: (0, 0, 10, 20, 35, 65, 90, 115),
temp: (35, 45, 50, 55, 60, 65, 70, 80),
enabled: false,
),
]
`
const asusdRon = `(
charge_control_end_threshold: 80,
base_charge_control_end_threshold: 0,
disable_nvidia_powerd_on_battery: true,
ac_command: "supergfxctl -m Hybrid",
bat_command: "supergfxctl -m Integrated",
platform_profile_linked_epp: true,
platform_profile_on_battery: Quiet,
change_platform_profile_on_battery: true,
platform_profile_on_ac: Balanced,
change_platform_profile_on_ac: true,
ac_profile_tunings: {
Quiet: (
enabled: false,
group: {},
),
},
)`
func TestAsusctlsAnswersAreRead(t *testing.T) {
p, err := ParseProfileGet(profileGetBalanced)
if err != nil || p.Active != "Balanced" || p.OnAC != "Balanced" || p.Battery != "Quiet" {
t.Fatalf("%+v %v", p, err)
}
if _, err := ParseProfileGet("something else"); err == nil {
t.Fatal("an answer with no profile was read as one")
}
if l, err := ParseLeds("Current keyboard led brightness: High\n"); err != nil || l != "high" {
t.Fatalf("%q %v", l, err)
}
curves := ParseFanCurves(fanCurveQuiet)
if len(curves) != 2 || curves[0].Fan != "CPU" || curves[0].PWM[7] != 100 || curves[0].Temp[0] != 35 || curves[1].Enabled {
t.Fatalf("%+v", curves)
}
if got := ParseSupported("[Integrated, Hybrid, AsusMuxDgpu]\n"); strings.Join(got, ",") != "Integrated,Hybrid,AsusMuxDgpu" {
t.Fatalf("%v", got)
}
}
func TestAsusdsFileIsReadForWhatDecidesTheModulesCodeAndNothingNested(t *testing.T) {
c := ParseAsusdRon(asusdRon)
if *c.ChargeLimit != 80 || c.ProfileOnAC != "Balanced" || c.ProfileOnBattery != "Quiet" ||
c.ACCommand != "supergfxctl -m Hybrid" || c.BatteryCommand != "supergfxctl -m Integrated" ||
!*c.ChangesProfileOnAC || !*c.DisablesPowerdOnBatt {
t.Fatalf("%+v", c)
}
}
func TestAMissingVendorClientIsNamedWithWhyItIsMissing(t *testing.T) {
f := newFake(t)
f.fails["supergfxctl"] = notFound
_, err := f.machine().GPU(context.Background())
if err == nil || !strings.Contains(err.Error(), "research 027") {
t.Fatalf("%v", err)
}
f.fails["asusctl"] = notFound
_, err = f.machine().Profile(context.Background())
if err == nil || !strings.Contains(err.Error(), "package resource installs it") {
t.Fatalf("%v", err)
}
}
func TestAGPUModeIsSetOnlyWhenTheMachineSupportsItAndAsusdsSwitchingIsSaid(t *testing.T) {
f := newFake(t)
f.answers["supergfxctl -g"] = "Hybrid\n"
f.answers["supergfxctl -s"] = "[Integrated, Hybrid, AsusMuxDgpu]\n"
f.file("/etc/asusd/asusd.ron", asusdRon)
m := f.machine()
if _, err := GPUModeTool(context.Background(), m, map[string]any{"mode": "Vfio"}); err == nil {
t.Fatal("an unsupported mode was accepted")
}
out, err := GPUModeTool(context.Background(), m, map[string]any{"mode": "integrated"})
if err != nil {
t.Fatal(err)
}
if !f.called("supergfxctl -m Integrated") {
t.Fatalf("calls %v", f.calls)
}
if _, said := out.(map[string]any)["asusd_switches_it"]; !said {
t.Fatalf("asusd's own switching of the mode was not said: %+v", out)
}
}
func TestTheChargeLimitIsBoundedAndSetThroughAsusd(t *testing.T) {
f := newFake(t)
f.answers["asusctl battery info"] = "Current battery charge limit: 60%\n"
m := f.machine()
for _, bad := range []any{float64(10), float64(101), "x", 55.5} {
if _, err := ChargeLimitTool(context.Background(), m, map[string]any{"limit": bad}); err == nil {
t.Errorf("limit %v was accepted", bad)
}
}
out, err := ChargeLimitTool(context.Background(), m, map[string]any{"limit": float64(60)})
if err != nil || !f.called("asusctl battery limit 60") || out.(map[string]any)["asusd_limit_percent"] != 60 {
t.Fatalf("%+v %v %v", out, err, f.calls)
}
}
func TestAProfileSetByToolIsHeldAndAnUnknownOneRefused(t *testing.T) {
f := newFake(t)
f.onMains()
f.answers["asusctl profile get"] = profileGetBalanced
m := f.machine()
sw := NewSwitcher(m, nil)
if _, err := ProfileTool(context.Background(), m, sw, map[string]any{"profile": "Turbo"}); err == nil {
t.Fatal("an unknown profile was accepted")
}
out, err := ProfileTool(context.Background(), m, sw, map[string]any{"profile": "performance", "hold_minutes": float64(30)})
if err != nil || !f.called("asusctl profile set Performance") {
t.Fatalf("%v %v", err, f.calls)
}
if _, held := out.(map[string]any)["held_until"]; !held {
t.Fatalf("not held: %+v", out)
}
if r := sw.Report(); r.Held != "Performance" {
t.Fatalf("%+v", r)
}
}