Spaces:
Sleeping
Sleeping
| """ | |
| Unit tests for model configuration components. | |
| """ | |
| import pytest | |
| import torch | |
| from unittest.mock import patch, MagicMock | |
| import os | |
| import sys | |
| from pathlib import Path | |
| # Add project root to path | |
| project_root = Path(__file__).parent.parent.parent | |
| sys.path.insert(0, str(project_root)) | |
| from app.config.model_config import ( | |
| ModelConfig, | |
| EnvironmentDetector, | |
| DependencyValidator | |
| ) | |
| class TestModelConfig: | |
| """Test ModelConfig dataclass.""" | |
| def test_model_config_creation(self): | |
| """Test ModelConfig creation with valid parameters.""" | |
| config = ModelConfig( | |
| model_id="test/model", | |
| revision="main", | |
| dtype=torch.float32, | |
| device_map="cpu", | |
| attn_implementation="eager", | |
| low_cpu_mem_usage=True, | |
| trust_remote_code=True | |
| ) | |
| assert config.model_id == "test/model" | |
| assert config.revision == "main" | |
| assert config.dtype == torch.float32 | |
| assert config.device_map == "cpu" | |
| assert config.attn_implementation == "eager" | |
| assert config.low_cpu_mem_usage is True | |
| assert config.trust_remote_code is True | |
| def test_is_gpu_available_true(self, mock_cuda): | |
| """Test GPU availability detection when CUDA is available.""" | |
| mock_cuda.return_value = True | |
| config = ModelConfig( | |
| model_id="test/model", | |
| revision=None, | |
| dtype=torch.float32, | |
| device_map="auto", | |
| attn_implementation="sdpa", | |
| low_cpu_mem_usage=False, | |
| trust_remote_code=True | |
| ) | |
| assert config.is_gpu_available is True | |
| def test_is_gpu_available_false(self, mock_cuda): | |
| """Test GPU availability detection when CUDA is not available.""" | |
| mock_cuda.return_value = False | |
| config = ModelConfig( | |
| model_id="test/model", | |
| revision=None, | |
| dtype=torch.float32, | |
| device_map="cpu", | |
| attn_implementation="eager", | |
| low_cpu_mem_usage=True, | |
| trust_remote_code=True | |
| ) | |
| assert config.is_gpu_available is False | |
| def test_device_info_gpu(self, mock_mem_reserved, mock_mem_allocated, | |
| mock_device_name, mock_current_device, | |
| mock_device_count, mock_cuda): | |
| """Test device info when GPU is available.""" | |
| mock_cuda.return_value = True | |
| mock_device_count.return_value = 1 | |
| mock_current_device.return_value = 0 | |
| mock_device_name.return_value = "Test GPU" | |
| mock_mem_allocated.return_value = 1024 | |
| mock_mem_reserved.return_value = 2048 | |
| config = ModelConfig( | |
| model_id="test/model", | |
| revision=None, | |
| dtype=torch.float32, | |
| device_map="auto", | |
| attn_implementation="sdpa", | |
| low_cpu_mem_usage=False, | |
| trust_remote_code=True | |
| ) | |
| device_info = config.device_info | |
| assert device_info["cuda_available"] is True | |
| assert device_info["device_count"] == 1 | |
| assert device_info["current_device"] == 0 | |
| assert device_info["device_name"] == "Test GPU" | |
| assert device_info["memory_allocated"] == 1024 | |
| assert device_info["memory_reserved"] == 2048 | |
| def test_device_info_cpu(self, mock_cuda): | |
| """Test device info when only CPU is available.""" | |
| mock_cuda.return_value = False | |
| config = ModelConfig( | |
| model_id="test/model", | |
| revision=None, | |
| dtype=torch.float32, | |
| device_map="cpu", | |
| attn_implementation="eager", | |
| low_cpu_mem_usage=True, | |
| trust_remote_code=True | |
| ) | |
| device_info = config.device_info | |
| assert device_info["cuda_available"] is False | |
| assert device_info["device_count"] == 0 | |
| assert device_info["current_device"] is None | |
| class TestEnvironmentDetector: | |
| """Test EnvironmentDetector class.""" | |
| def test_detect_environment_cpu(self, mock_cuda): | |
| """Test environment detection on CPU-only system.""" | |
| mock_cuda.return_value = False | |
| with patch('importlib.import_module') as mock_import: | |
| # Mock successful einops import | |
| mock_einops = MagicMock() | |
| mock_einops.__version__ = "0.7.0" | |
| def import_side_effect(name): | |
| if name == "einops": | |
| return mock_einops | |
| elif name == "flash_attn": | |
| raise ImportError("No module named 'flash_attn'") | |
| return MagicMock() | |
| mock_import.side_effect = import_side_effect | |
| env_info = EnvironmentDetector.detect_environment() | |
| assert env_info["cuda_available"] is False | |
| assert env_info["cuda_version"] is None | |
| assert env_info["torch_version"] == "2.0.0" | |
| def test_detect_environment_gpu(self, mock_cuda): | |
| """Test environment detection on GPU system.""" | |
| mock_cuda.return_value = True | |
| env_info = EnvironmentDetector.detect_environment() | |
| assert env_info["cuda_available"] is True | |
| assert env_info["cuda_version"] == "11.8" | |
| def test_create_model_config_cpu_defaults(self, mock_cuda): | |
| """Test model config creation with CPU defaults.""" | |
| mock_cuda.return_value = False | |
| config = EnvironmentDetector.create_model_config() | |
| assert config.model_id == "microsoft/Phi-3.5-MoE-instruct" | |
| assert config.revision is None | |
| assert config.dtype == torch.float32 | |
| assert config.device_map == "cpu" | |
| assert config.attn_implementation == "eager" | |
| assert config.low_cpu_mem_usage is True | |
| assert config.trust_remote_code is True | |
| def test_create_model_config_gpu_defaults(self, mock_cuda): | |
| """Test model config creation with GPU defaults.""" | |
| mock_cuda.return_value = True | |
| config = EnvironmentDetector.create_model_config() | |
| assert config.model_id == "microsoft/Phi-3.5-MoE-instruct" | |
| assert config.revision is None | |
| assert config.dtype == torch.bfloat16 | |
| assert config.device_map == "auto" | |
| assert config.attn_implementation == "sdpa" | |
| assert config.low_cpu_mem_usage is False | |
| assert config.trust_remote_code is True | |
| def test_create_model_config_with_env_vars(self, mock_cuda): | |
| """Test model config creation with environment variables.""" | |
| mock_cuda.return_value = False | |
| config = EnvironmentDetector.create_model_config() | |
| assert config.model_id == "custom/model" | |
| assert config.revision == "abc123" | |
| def test_create_model_config_with_parameters(self): | |
| """Test model config creation with explicit parameters.""" | |
| config = EnvironmentDetector.create_model_config( | |
| model_id="explicit/model", | |
| revision="explicit123" | |
| ) | |
| assert config.model_id == "explicit/model" | |
| assert config.revision == "explicit123" | |
| class TestDependencyValidator: | |
| """Test DependencyValidator class.""" | |
| def test_required_packages_list(self): | |
| """Test that required packages list is properly defined.""" | |
| required = DependencyValidator.REQUIRED_PACKAGES | |
| assert "transformers" in required | |
| assert "accelerate" in required | |
| assert "einops" in required | |
| assert "huggingface_hub" in required | |
| assert "gradio" in required | |
| assert "torch" in required | |
| def test_optional_packages_list(self): | |
| """Test that optional packages list is properly defined.""" | |
| optional = DependencyValidator.OPTIONAL_PACKAGES | |
| assert "flash_attn" in optional | |
| def test_validate_dependencies_all_available(self, mock_import): | |
| """Test dependency validation when all packages are available.""" | |
| mock_import.return_value = MagicMock() | |
| results = DependencyValidator.validate_dependencies() | |
| for package in DependencyValidator.REQUIRED_PACKAGES: | |
| assert results[package] is True | |
| for package in DependencyValidator.OPTIONAL_PACKAGES: | |
| assert results[package] is True | |
| def test_validate_dependencies_some_missing(self, mock_import): | |
| """Test dependency validation when some packages are missing.""" | |
| def import_side_effect(name): | |
| if name == "flash_attn": | |
| raise ImportError(f"No module named '{name}'") | |
| return MagicMock() | |
| mock_import.side_effect = import_side_effect | |
| results = DependencyValidator.validate_dependencies() | |
| # Required packages should be available | |
| for package in DependencyValidator.REQUIRED_PACKAGES: | |
| assert results[package] is True | |
| # flash_attn should be missing | |
| assert results["flash_attn"] is False | |
| def test_get_missing_required_packages(self, mock_import): | |
| """Test getting list of missing required packages.""" | |
| def import_side_effect(name): | |
| if name in ["transformers", "einops"]: | |
| raise ImportError(f"No module named '{name}'") | |
| return MagicMock() | |
| mock_import.side_effect = import_side_effect | |
| missing = DependencyValidator.get_missing_required_packages() | |
| assert "transformers" in missing | |
| assert "einops" in missing | |
| assert "accelerate" not in missing # Should be available | |
| def test_is_environment_ready_true(self, mock_import): | |
| """Test environment readiness when all required packages are available.""" | |
| mock_import.return_value = MagicMock() | |
| assert DependencyValidator.is_environment_ready() is True | |
| def test_is_environment_ready_false(self, mock_import): | |
| """Test environment readiness when required packages are missing.""" | |
| def import_side_effect(name): | |
| if name == "einops": | |
| raise ImportError(f"No module named '{name}'") | |
| return MagicMock() | |
| mock_import.side_effect = import_side_effect | |
| assert DependencyValidator.is_environment_ready() is False | |
| if __name__ == "__main__": | |
| pytest.main([__file__]) | |