mirror of
https://github.com/henrygd/beszel.git
synced 2026-08-16 07:17:46 +02:00
Adds fan RPM monitoring as a peer to the existing temperature collection, addressing #1918. --------- Co-authored-by: henrygd <hank@henrygd.me>
88 lines
2.8 KiB
Go
88 lines
2.8 KiB
Go
//go:build testing
|
|
|
|
package agent
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// writeFile creates path with parents and writes contents.
|
|
func writeFile(t *testing.T, path, contents string) {
|
|
t.Helper()
|
|
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
|
|
require.NoError(t, os.WriteFile(path, []byte(contents), 0o644))
|
|
}
|
|
|
|
// TestReadHwmonFans verifies the /sys/class/hwmon walker:
|
|
// - picks up fan*_input from every chip,
|
|
// - keys entries by chip name + sensor label (or fan idx if no label),
|
|
// - retains 0 RPM for stopped fans,
|
|
// - tolerates chips with no fan files at all.
|
|
func TestReadHwmonFans(t *testing.T) {
|
|
root := t.TempDir()
|
|
|
|
// hwmon0: Raspberry Pi 5 active cooler — one fan, no label.
|
|
writeFile(t, filepath.Join(root, "hwmon0", "name"), "pwmfan\n")
|
|
writeFile(t, filepath.Join(root, "hwmon0", "fan1_input"), "6500\n")
|
|
|
|
// hwmon1: a thermal-only chip, no fan files. Must not error.
|
|
writeFile(t, filepath.Join(root, "hwmon1", "name"), "cpu_thermal\n")
|
|
writeFile(t, filepath.Join(root, "hwmon1", "temp1_input"), "55000\n")
|
|
|
|
// hwmon2: two fans — one stopped (0 RPM) and one labeled "chassis".
|
|
writeFile(t, filepath.Join(root, "hwmon2", "name"), "nct6798\n")
|
|
writeFile(t, filepath.Join(root, "hwmon2", "fan1_input"), "0\n")
|
|
writeFile(t, filepath.Join(root, "hwmon2", "fan2_input"), "1200\n")
|
|
writeFile(t, filepath.Join(root, "hwmon2", "fan2_label"), "chassis\n")
|
|
|
|
fans, err := readHwmonFans(root)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, map[string]uint16{
|
|
"pwmfan_fan1": 6500,
|
|
"nct6798_fan1": 0,
|
|
"nct6798_chassis": 1200,
|
|
}, fans)
|
|
}
|
|
|
|
// TestReadHwmonFansMissingRoot returns an error rather than panicking when the
|
|
// hwmon root doesn't exist (e.g. running on a kernel without hwmon support).
|
|
func TestReadHwmonFansMissingRoot(t *testing.T) {
|
|
_, err := readHwmonFans(filepath.Join(t.TempDir(), "does-not-exist"))
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
// TestReadHwmonFansEmpty returns an empty map (not nil error) when the root
|
|
// exists but contains no chips at all.
|
|
func TestReadHwmonFansEmpty(t *testing.T) {
|
|
root := t.TempDir()
|
|
fans, err := readHwmonFans(root)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, fans)
|
|
}
|
|
|
|
func TestFanDiscoveryCache(t *testing.T) {
|
|
root := t.TempDir()
|
|
input := filepath.Join(root, "hwmon0", "fan1_input")
|
|
writeFile(t, filepath.Join(root, "hwmon0", "name"), "chip\n")
|
|
writeFile(t, input, "1000\n")
|
|
|
|
getSensors := newFanSensorCache(root)
|
|
sensors, err := getSensors()
|
|
require.NoError(t, err)
|
|
fans := readFanSensors(sensors)
|
|
assert.Equal(t, uint16(1000), fans["chip_fan1"])
|
|
|
|
writeFile(t, input, "1200\n")
|
|
writeFile(t, filepath.Join(root, "hwmon0", "fan1_label"), "case\n")
|
|
sensors, err = getSensors()
|
|
require.NoError(t, err)
|
|
fans = readFanSensors(sensors)
|
|
assert.Equal(t, map[string]uint16{"chip_fan1": 1200}, fans)
|
|
}
|