"""AMD-specific hardware tests.""" from __future__ import annotations from unittest.mock import patch import pytest from openjarvis.core.config import ( GpuInfo, HardwareInfo, _detect_amd_gpu, recommend_engine, ) pytestmark = pytest.mark.amd # --------------------------------------------------------------------------- # Detection / rocm-smi parsing # --------------------------------------------------------------------------- class TestAMDDetection: """Tests for _detect_amd_gpu() against various rocm-smi outputs.""" @patch("openjarvis.core.config.shutil.which", return_value="/usr/bin/rocm-smi") @patch( "openjarvis.core.config._run_cmd", side_effect=[ "AMD Instinct MI300X", # --showproductname "GPU[0] : vram Total Memory (B): 206158430208", # --showmeminfo vram "GPU[0] : Some info", # --showallinfo ], ) def test_rocm_smi_parsing(self, mock_run, mock_which): gpu = _detect_amd_gpu() assert gpu is not None assert gpu.vendor == "amd" assert "MI300X" in gpu.name @patch("openjarvis.core.config.shutil.which", return_value=None) def test_rocm_smi_not_found(self, mock_which): assert _detect_amd_gpu() is None @patch("openjarvis.core.config.shutil.which", return_value="/usr/bin/rocm-smi") @patch( "openjarvis.core.config._run_cmd", side_effect=[ "AMD Instinct MI250X\nAMD Instinct MI250X", # --showproductname "", # --showmeminfo vram (empty) "", # --showallinfo (empty) ], ) def test_amd_gpu_model(self, mock_run, mock_which): """First line of rocm-smi output is used as the GPU name.""" gpu = _detect_amd_gpu() assert gpu is not None assert "MI250X" in gpu.name @patch("openjarvis.core.config.shutil.which", return_value="/usr/bin/rocm-smi") @patch("openjarvis.core.config._run_cmd", side_effect=["", "", ""]) def test_rocm_smi_empty_output(self, mock_run, mock_which): """Empty output from rocm-smi --showproductname returns None.""" assert _detect_amd_gpu() is None @patch("openjarvis.core.config.shutil.which", return_value="/usr/bin/rocm-smi") @patch( "openjarvis.core.config._run_cmd", side_effect=[ "AMD Instinct MI300X", "GPU[0] : vram Total Memory (B): 206158430208", "GPU[0] : Some info", ], ) def test_amd_vram_parsing(self, mock_run, mock_which): """VRAM is parsed from --showmeminfo vram output.""" gpu = _detect_amd_gpu() assert gpu is not None # 206158430208 bytes = ~192.0 GB assert gpu.vram_gb == 192.0 @patch("openjarvis.core.config.shutil.which", return_value="/usr/bin/rocm-smi") @patch( "openjarvis.core.config._run_cmd", side_effect=[ "AMD Instinct MI300X", ( "GPU[0] : vram Total Memory (B): 206158430208\n" "GPU[0] : vram Total Used Memory (B): 0\n" "GPU[1] : vram Total Memory (B): 206158430208\n" "GPU[1] : vram Total Used Memory (B): 0\n" "GPU[2] : vram Total Memory (B): 206158430208\n" "GPU[2] : vram Total Used Memory (B): 0\n" "GPU[3] : vram Total Memory (B): 206158430208\n" "GPU[3] : vram Total Used Memory (B): 0" ), ( "GPU[0] : Info line\n" "GPU[1] : Info line\n" "GPU[2] : Info line\n" "GPU[3] : Info line" ), ], ) def test_amd_multi_gpu_count(self, mock_run, mock_which): """Multiple GPU entries in --showallinfo are counted.""" gpu = _detect_amd_gpu() assert gpu is not None assert gpu.count == 4 @patch("openjarvis.core.config.shutil.which", return_value="/usr/bin/rocm-smi") @patch( "openjarvis.core.config._run_cmd", side_effect=[ "AMD Instinct MI300X", "garbled output with no valid memory info", "GPU[0] : Some info", ], ) def test_amd_vram_parse_failure(self, mock_run, mock_which): """Garbled VRAM output falls back to 0.0.""" gpu = _detect_amd_gpu() assert gpu is not None assert gpu.vram_gb == 0.0 # --------------------------------------------------------------------------- # Engine recommendation # --------------------------------------------------------------------------- class TestAMDEngineRecommendation: """Tests that AMD datacenter cards map to vllm, consumer cards to lemonade.""" def test_mi300x_recommends_vllm(self): hw = HardwareInfo( platform="linux", cpu_brand="AMD EPYC 9654", cpu_count=96, ram_gb=768.0, gpu=GpuInfo( vendor="amd", name="AMD Instinct MI300X", vram_gb=192.0, count=1, ), ) assert recommend_engine(hw) == "vllm" def test_mi350_recommends_vllm(self): hw = HardwareInfo( platform="linux", cpu_brand="AMD EPYC 9654", cpu_count=96, ram_gb=768.0, gpu=GpuInfo( vendor="amd", name="AMD Instinct MI350X", vram_gb=288.0, count=1, ), ) assert recommend_engine(hw) == "vllm" def test_amd_consumer_recommends_lemonade(self): hw = HardwareInfo( platform="linux", cpu_brand="AMD Ryzen 9 7950X", cpu_count=32, ram_gb=64.0, gpu=GpuInfo( vendor="amd", name="AMD Radeon RX 7900 XTX", vram_gb=24.0, count=1, ), ) assert recommend_engine(hw) == "lemonade" def test_amd_generic_recommends_lemonade(self): hw = HardwareInfo( platform="linux", cpu_brand="AMD EPYC", cpu_count=64, ram_gb=256.0, gpu=GpuInfo(vendor="amd", name="AMD GPU", vram_gb=0.0, count=1), ) assert recommend_engine(hw) == "lemonade" def test_amd_multi_gpu_recommends_vllm(self): hw = HardwareInfo( platform="linux", cpu_brand="AMD EPYC 9654", cpu_count=128, ram_gb=1024.0, gpu=GpuInfo( vendor="amd", name="AMD Instinct MI300X", vram_gb=192.0, count=4, ), ) assert recommend_engine(hw) == "vllm"