package discover import ( "testing" "unsafe" "github.com/ollama/ollama/ml" ) func TestGGMLBackendDevPropsLayout(t *testing.T) { if unsafe.Sizeof(uintptr(0)) != 8 { t.Skip("GGML probe layout assertions are for 64-bit builds") } var props ggmlBackendDevProps if got, want := unsafe.Sizeof(props), uintptr(56); got != want { t.Fatalf("ggmlBackendDevProps size = %d, want %d", got, want) } checks := []struct { name string got uintptr want uintptr }{ {"Name", unsafe.Offsetof(props.Name), 0}, {"Description", unsafe.Offsetof(props.Description), 8}, {"MemoryFree", unsafe.Offsetof(props.MemoryFree), 16}, {"MemoryTotal", unsafe.Offsetof(props.MemoryTotal), 24}, {"Type", unsafe.Offsetof(props.Type), 32}, {"DeviceID", unsafe.Offsetof(props.DeviceID), 40}, {"Caps", unsafe.Offsetof(props.Caps), 48}, } for _, tt := range checks { t.Run(tt.name, func(t *testing.T) { if tt.got != tt.want { t.Fatalf("offset = %d, want %d", tt.got, tt.want) } }) } if got, want := unsafe.Sizeof(ggmlBackendDevCaps{}), uintptr(4); got != want { t.Fatalf("ggmlBackendDevCaps size = %d, want %d", got, want) } } func TestParseLlamaServerDevicesUsesNativeCUDAComputeCapability(t *testing.T) { output := `system_info: n_threads = 4 | CUDA : ARCHS = 750,800 | Available devices: CUDA0: NVIDIA GeForce GTX 1060 6GB (6063 MiB, 5900 MiB free) ` devices := parseLlamaServerDevicesWithNative(output, []string{"/lib/ollama", "/lib/ollama/cuda_v13"}, []nativeProbeDevice{{ Library: "CUDA", Index: 0, IndexMatchesBackend: true, DeviceID: "0000:01:00.0", ComputeMajor: 6, ComputeMinor: 1, CUDADriverMajor: 13, NVIDIADriverMajor: 570, }}) if len(devices) != 0 { t.Fatalf("got %d devices, want unsupported CUDA device filtered", len(devices)) } output = `system_info: n_threads = 4 | CUDA : ARCHS = 610,750,800 | Available devices: CUDA0: NVIDIA GeForce GTX 1060 6GB (6063 MiB, 5900 MiB free) ` devices = parseLlamaServerDevicesWithNative(output, []string{"/lib/ollama", "/lib/ollama/cuda_v12"}, []nativeProbeDevice{{ Library: "CUDA", Index: 0, IndexMatchesBackend: true, DeviceID: "0000:01:00.0", ComputeMajor: 6, ComputeMinor: 1, CUDADriverMajor: 12, NVIDIADriverMajor: 570, }}) if len(devices) != 1 { t.Fatalf("got %d devices, want 1", len(devices)) } got := devices[0] if got.Compute() != "6.1" { t.Fatalf("compute = %q, want 6.1", got.Compute()) } if got.PCIID != "0000:01:00.0" { t.Fatalf("PCIID = %q, want 0000:01:00.0", got.PCIID) } if got.Driver() != "12.0" { t.Fatalf("driver = %q, want 12.0", got.Driver()) } if got.NVIDIADriverMajor != 570 { t.Fatalf("NVIDIADriverMajor = %d, want 570", got.NVIDIADriverMajor) } } func TestParseLlamaServerDevicesUsesNativeROCmMetadata(t *testing.T) { output := `ggml_vulkan: 0 = AMD Radeon RX 7600 | uma: 1 | fp16: 1 | Available devices: ROCm0: AMD Radeon RX 7600 (8176 MiB, 7900 MiB free) ` devices := parseLlamaServerDevicesWithNative(output, []string{"/lib/ollama", "/lib/ollama/rocm_v7_2"}, []nativeProbeDevice{{ Library: "ROCm", Index: 0, IndexMatchesBackend: true, DeviceID: "0000:03:00.0", GFXTarget: "gfx1102", Integrated: false, IntegratedKnown: true, }}) if len(devices) != 1 { t.Fatalf("got %d devices, want 1", len(devices)) } got := devices[0] if got.PCIID != "0000:03:00.0" { t.Fatalf("PCIID = %q, want 0000:03:00.0", got.PCIID) } if got.GFXTarget != "gfx1102" { t.Fatalf("GFXTarget = %q, want gfx1102", got.GFXTarget) } if got.Compute() != "gfx1102" { t.Fatalf("compute = %q, want gfx1102", got.Compute()) } if got.Integrated { t.Fatalf("Integrated = true, want false") } } func TestNVIDIADriverMajorFromDevices(t *testing.T) { devices := []ml.DeviceInfo{ {DeviceID: ml.DeviceID{Library: "CUDA"}, NVIDIADriverMajor: 565}, } if got := nvidiaDriverMajorFromDevices(devices); got != 565 { t.Fatalf("driver = %d, want 565", got) } } func TestMergeNativeProbeDevicesAvoidsUnreliableIndexMatch(t *testing.T) { tests := []struct { name string base []nativeProbeDevice supplement []nativeProbeDevice wantLen int wantPCI string wantKnown bool wantIGPU bool }{ { name: "filtered sysfs cannot overwrite a different backend device by index", base: []nativeProbeDevice{{ Library: "ROCm", Index: 0, IndexMatchesBackend: true, DeviceID: "0000:03:00.0", }}, supplement: []nativeProbeDevice{{ Library: "ROCm", Index: 0, DeviceID: "0000:04:00.0", Integrated: true, IntegratedKnown: true, }}, wantLen: 1, wantPCI: "0000:03:00.0", }, { name: "filtered sysfs can still merge by PCI ID", base: []nativeProbeDevice{{ Library: "ROCm", Index: 0, IndexMatchesBackend: true, DeviceID: "0000:04:00.0", }}, supplement: []nativeProbeDevice{{ Library: "ROCm", Index: 1, DeviceID: "0000:04:00.0", Integrated: true, IntegratedKnown: true, }}, wantLen: 1, wantPCI: "0000:04:00.0", wantKnown: true, wantIGPU: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := mergeNativeProbeDevices(tt.base, tt.supplement) if len(got) != tt.wantLen { t.Fatalf("got %d devices, want %d: %#v", len(got), tt.wantLen, got) } if got[0].DeviceID != tt.wantPCI { t.Fatalf("DeviceID = %q, want %q", got[0].DeviceID, tt.wantPCI) } if got[0].IntegratedKnown != tt.wantKnown { t.Fatalf("IntegratedKnown = %v, want %v", got[0].IntegratedKnown, tt.wantKnown) } if got[0].Integrated != tt.wantIGPU { t.Fatalf("Integrated = %v, want %v", got[0].Integrated, tt.wantIGPU) } }) } }