|
|
|
|
| package agent
|
|
|
| import (
|
| "os"
|
| "path/filepath"
|
| "testing"
|
|
|
| "github.com/henrygd/beszel/agent/utils"
|
| "github.com/henrygd/beszel/internal/entities/system"
|
| "github.com/stretchr/testify/assert"
|
| "github.com/stretchr/testify/require"
|
| )
|
|
|
| func TestNormalizeHexID(t *testing.T) {
|
| tests := []struct {
|
| in string
|
| want string
|
| }{
|
| {"0x1002", "1002"},
|
| {"C2", "c2"},
|
| {" 15BF ", "15bf"},
|
| {"0x15bf", "15bf"},
|
| {"", ""},
|
| }
|
| for _, tt := range tests {
|
| subName := tt.in
|
| if subName == "" {
|
| subName = "empty_string"
|
| }
|
| t.Run(subName, func(t *testing.T) {
|
| got := normalizeHexID(tt.in)
|
| assert.Equal(t, tt.want, got)
|
| })
|
| }
|
| }
|
|
|
| func TestCacheKeyForAmdgpu(t *testing.T) {
|
| tests := []struct {
|
| deviceID string
|
| revisionID string
|
| want string
|
| }{
|
| {"1114", "c2", "1114:c2"},
|
| {"15bf", "", "15bf"},
|
| {"1506", "c1", "1506:c1"},
|
| }
|
| for _, tt := range tests {
|
| got := cacheKeyForAmdgpu(tt.deviceID, tt.revisionID)
|
| assert.Equal(t, tt.want, got)
|
| }
|
| }
|
|
|
| func TestReadSysfsFloat(t *testing.T) {
|
| dir := t.TempDir()
|
|
|
| validPath := filepath.Join(dir, "val")
|
| require.NoError(t, os.WriteFile(validPath, []byte(" 42.5 \n"), 0o644))
|
| got, err := readSysfsFloat(validPath)
|
| require.NoError(t, err)
|
| assert.Equal(t, 42.5, got)
|
|
|
|
|
| sciPath := filepath.Join(dir, "sci")
|
| require.NoError(t, os.WriteFile(sciPath, []byte("1e2"), 0o644))
|
| got, err = readSysfsFloat(sciPath)
|
| require.NoError(t, err)
|
| assert.Equal(t, 100.0, got)
|
|
|
|
|
| _, err = readSysfsFloat(filepath.Join(dir, "missing"))
|
| require.Error(t, err)
|
|
|
|
|
| badPath := filepath.Join(dir, "bad")
|
| require.NoError(t, os.WriteFile(badPath, []byte("not a number"), 0o644))
|
| _, err = readSysfsFloat(badPath)
|
| require.Error(t, err)
|
| }
|
|
|
| func TestIsAmdGpu(t *testing.T) {
|
| dir := t.TempDir()
|
| deviceDir := filepath.Join(dir, "device")
|
| require.NoError(t, os.MkdirAll(deviceDir, 0o755))
|
|
|
|
|
| require.NoError(t, os.WriteFile(filepath.Join(deviceDir, "vendor"), []byte("0x1002\n"), 0o644))
|
| assert.True(t, isAmdGpu(dir), "vendor 0x1002 should be AMD")
|
|
|
|
|
| require.NoError(t, os.WriteFile(filepath.Join(deviceDir, "vendor"), []byte("0x10de\n"), 0o644))
|
| assert.False(t, isAmdGpu(dir), "vendor 0x10de should not be AMD")
|
|
|
|
|
| require.NoError(t, os.Remove(filepath.Join(deviceDir, "vendor")))
|
| assert.False(t, isAmdGpu(dir), "missing vendor file should be false")
|
| }
|
|
|
| func TestAmdgpuNameCacheRoundTrip(t *testing.T) {
|
|
|
| deviceID, revisionID := "cachedev99", "00"
|
| cacheAmdgpuName(deviceID, revisionID, "AMD Test GPU 99 Graphics", true)
|
|
|
| name, found, done := getCachedAmdgpuName(deviceID, revisionID)
|
| assert.True(t, found)
|
| assert.True(t, done)
|
| assert.Equal(t, "AMD Test GPU 99", name)
|
|
|
|
|
| name2, found2, _ := getCachedAmdgpuName(deviceID, "")
|
| assert.True(t, found2)
|
| assert.Equal(t, "AMD Test GPU 99", name2)
|
|
|
|
|
| cacheMissingAmdgpuName("missedev99", "ab")
|
| _, found3, done3 := getCachedAmdgpuName("missedev99", "ab")
|
| assert.False(t, found3)
|
| assert.True(t, done3, "done should be true so caller skips file lookup")
|
| }
|
|
|
| func TestUpdateAmdGpuDataWithFakeSysfs(t *testing.T) {
|
| tests := []struct {
|
| name string
|
| writeGTT bool
|
| wantMemoryUsed float64
|
| wantMemoryTotal float64
|
| }{
|
| {
|
| name: "sums vram and gtt when gtt is present",
|
| writeGTT: true,
|
| wantMemoryUsed: utils.BytesToMegabytes(1073741824 + 536870912),
|
| wantMemoryTotal: utils.BytesToMegabytes(2147483648 + 4294967296),
|
| },
|
| {
|
| name: "falls back to vram when gtt is missing",
|
| writeGTT: false,
|
| wantMemoryUsed: utils.BytesToMegabytes(1073741824),
|
| wantMemoryTotal: utils.BytesToMegabytes(2147483648),
|
| },
|
| }
|
|
|
| for _, tt := range tests {
|
| t.Run(tt.name, func(t *testing.T) {
|
| dir := t.TempDir()
|
| cardPath := filepath.Join(dir, "card0")
|
| devicePath := filepath.Join(cardPath, "device")
|
| hwmonPath := filepath.Join(devicePath, "hwmon", "hwmon0")
|
| require.NoError(t, os.MkdirAll(hwmonPath, 0o755))
|
|
|
| write := func(name, content string) {
|
| require.NoError(t, os.WriteFile(filepath.Join(devicePath, name), []byte(content), 0o644))
|
| }
|
| write("vendor", "0x1002")
|
| write("device", "0x1506")
|
| write("revision", "0xc1")
|
| write("gpu_busy_percent", "25")
|
| write("mem_info_vram_used", "1073741824")
|
| write("mem_info_vram_total", "2147483648")
|
| if tt.writeGTT {
|
| write("mem_info_gtt_used", "536870912")
|
| write("mem_info_gtt_total", "4294967296")
|
| }
|
| require.NoError(t, os.WriteFile(filepath.Join(hwmonPath, "temp1_input"), []byte("45000"), 0o644))
|
| require.NoError(t, os.WriteFile(filepath.Join(hwmonPath, "power1_input"), []byte("20000000"), 0o644))
|
|
|
|
|
| cacheAmdgpuName("1506", "c1", "AMD Radeon 610M Graphics", true)
|
|
|
| gm := &GPUManager{GpuDataMap: make(map[string]*system.GPUData)}
|
| ok := gm.updateAmdGpuData(cardPath)
|
| require.True(t, ok)
|
|
|
| gpu, ok := gm.GpuDataMap["card0"]
|
| require.True(t, ok)
|
| assert.Equal(t, "AMD Radeon 610M", gpu.Name)
|
| assert.Equal(t, 25.0, gpu.Usage)
|
| assert.Equal(t, tt.wantMemoryUsed, gpu.MemoryUsed)
|
| assert.Equal(t, tt.wantMemoryTotal, gpu.MemoryTotal)
|
| assert.Equal(t, 45.0, gpu.Temperature)
|
| assert.Equal(t, 20.0, gpu.Power)
|
| assert.Equal(t, 1.0, gpu.Count)
|
| })
|
| }
|
| }
|
|
|
| func TestLookupAmdgpuNameInFile(t *testing.T) {
|
| idsPath := filepath.Join("test-data", "amdgpu.ids")
|
|
|
| tests := []struct {
|
| name string
|
| deviceID string
|
| revisionID string
|
| wantName string
|
| wantExact bool
|
| wantFound bool
|
| }{
|
| {
|
| name: "exact device and revision match",
|
| deviceID: "1114",
|
| revisionID: "c2",
|
| wantName: "AMD Radeon 860M Graphics",
|
| wantExact: true,
|
| wantFound: true,
|
| },
|
| {
|
| name: "exact match 15BF revision 01 returns 760M",
|
| deviceID: "15bf",
|
| revisionID: "01",
|
| wantName: "AMD Radeon 760M Graphics",
|
| wantExact: true,
|
| wantFound: true,
|
| },
|
| {
|
| name: "exact match 15BF revision 00 returns 780M",
|
| deviceID: "15bf",
|
| revisionID: "00",
|
| wantName: "AMD Radeon 780M Graphics",
|
| wantExact: true,
|
| wantFound: true,
|
| },
|
| {
|
| name: "device-only match returns first entry for device",
|
| deviceID: "1506",
|
| revisionID: "",
|
| wantName: "AMD Radeon 610M",
|
| wantExact: false,
|
| wantFound: true,
|
| },
|
| {
|
| name: "unknown device not found",
|
| deviceID: "dead",
|
| revisionID: "00",
|
| wantName: "",
|
| wantExact: false,
|
| wantFound: false,
|
| },
|
| }
|
| for _, tt := range tests {
|
| t.Run(tt.name, func(t *testing.T) {
|
| gotName, gotExact, gotFound := lookupAmdgpuNameInFile(tt.deviceID, tt.revisionID, idsPath)
|
| assert.Equal(t, tt.wantName, gotName, "name")
|
| assert.Equal(t, tt.wantExact, gotExact, "exact")
|
| assert.Equal(t, tt.wantFound, gotFound, "found")
|
| })
|
| }
|
| }
|
|
|
| func TestGetAmdGpuNameFromIdsFile(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
|
| idsPath := filepath.Join("test-data", "amdgpu.ids")
|
| name, exact, found := lookupAmdgpuNameInFile("1435", "ae", idsPath)
|
| require.True(t, found)
|
| require.True(t, exact)
|
| assert.Equal(t, "AMD Custom GPU 0932", name)
|
| assert.Equal(t, "AMD Custom GPU 0932", normalizeAmdgpuName(name))
|
|
|
|
|
| name2 := "AMD Radeon 860M Graphics"
|
| assert.Equal(t, "AMD Radeon 860M", normalizeAmdgpuName(name2))
|
| }
|
|
|