""" Tests for BlackRiver GGUF Tools. Run: python -m pytest test_app.py -v """ import os import sys import json import tempfile from pathlib import Path from unittest.mock import patch, MagicMock sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) # Mock Gradio before importing app mock_gr = MagicMock() mock_gr.OAuthToken = MagicMock() mock_gr.Progress = MagicMock() mock_gr.Error = Exception mock_gr.Interface = MagicMock() mock_gr.Blocks = MagicMock() mock_gr.Markdown = MagicMock() mock_gr.HTML = MagicMock() mock_gr.Dropdown = MagicMock() mock_gr.Checkbox = MagicMock() mock_gr.File = MagicMock() mock_gr.Number = MagicMock() mock_gr.Textbox = MagicMock() mock_gr.Image = MagicMock() mock_gr.Row = MagicMock() mock_gr.Column = MagicMock() mock_gr.Group = MagicMock() mock_gr.LoginButton = MagicMock() mock_gr.Button = MagicMock() mock_gr.update = MagicMock() mock_gr.themes = MagicMock() mock_gr.themes.Base = MagicMock(return_value=MagicMock()) sys.modules['gradio'] = mock_gr sys.modules['gradio_huggingfacehub_search'] = MagicMock() sys.modules['huggingface_hub'] = MagicMock() sys.modules['apscheduler'] = MagicMock() sys.modules['apscheduler.schedulers'] = MagicMock() sys.modules['apscheduler.schedulers.background'] = MagicMock() # Now import app import app class TestDetectMultimodal: def test_text_only_model(self, tmp_path): config = {"architectures": ["LlamaForCausalLM"], "model_type": "llama"} (tmp_path / "config.json").write_text(json.dumps(config)) result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is False assert "No multimodal indicators" in result["reason"] def test_vision_model_vision_config(self, tmp_path): config = { "architectures": ["LlavaForConditionalGeneration"], "vision_config": {"hidden_size": 768}, } (tmp_path / "config.json").write_text(json.dumps(config)) result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is True assert result["mmproj_supported"] is True assert "vision_config present" in result["reason"] def test_vision_model_image_token_ids(self, tmp_path): config = { "architectures": ["Gemma3ForConditionalGeneration"], "image_token_id": 256000, "vision_start_token_id": 255999, } (tmp_path / "config.json").write_text(json.dumps(config)) result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is True assert result["mmproj_supported"] is True def test_vision_model_text_config(self, tmp_path): config = { "architectures": ["Qwen2VLForConditionalGeneration"], "text_config": {"vision_config": {"hidden_size": 1024}}, } (tmp_path / "config.json").write_text(json.dumps(config)) result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is True assert result["mmproj_supported"] is True def test_vision_model_mm_projector(self, tmp_path): config = { "architectures": ["MiniCPMV"], "mm_projector": "mlp2x_gelu", } (tmp_path / "config.json").write_text(json.dumps(config)) result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is True assert result["mmproj_supported"] is True def test_vision_model_mm_vision_tower(self, tmp_path): config = { "architectures": ["BailingMoe2ForConditionalGeneration"], "mm_vision_tower": "openai/clip-vit-large-patch14-336", } (tmp_path / "config.json").write_text(json.dumps(config)) result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is True assert result["mmproj_supported"] is True def test_missing_config(self, tmp_path): result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is False assert "No config.json" in result["reason"] def test_invalid_config(self, tmp_path): (tmp_path / "config.json").write_text("not json") result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is False assert "Failed to parse" in result["reason"] def test_preprocessor_detection(self, tmp_path): config = {"architectures": ["LlavaForConditionalGeneration"]} (tmp_path / "config.json").write_text(json.dumps(config)) pp = {"image_processor_type": "CLIPImageProcessor", "size": 336} (tmp_path / "preprocessor_config.json").write_text(json.dumps(pp)) result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is True def test_legacy_llava_detection(self, tmp_path): config = {"architectures": ["LlavaLlamaForCausalLM"], "model_type": "llava"} (tmp_path / "config.json").write_text(json.dumps(config)) result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is True assert result["method"] == "legacy" assert result["legacy_key"] == "llava" def test_legacy_minicpmv_detection(self, tmp_path): config = {"architectures": ["MiniCPMVForCausalLM"], "model_type": "minicpmv"} (tmp_path / "config.json").write_text(json.dumps(config)) result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is True assert result["method"] == "legacy" assert result["legacy_key"] == "minicpmv" def test_legacy_glmedge_detection(self, tmp_path): config = {"architectures": ["GlmEdgeForCausalLM"], "model_type": "glm-edge"} (tmp_path / "config.json").write_text(json.dumps(config)) result = app.detect_multimodal(str(tmp_path)) assert result["is_multimodal"] is True assert result["method"] == "legacy" assert result["legacy_key"] == "glmedge" class TestSanitizeName: def test_simple(self): assert app._sanitize_name("hello-world") == "hello-world" def test_spaces(self): assert app._sanitize_name("hello world") == "hello_world" def test_special_chars(self): assert app._sanitize_name("model@v1.0!") == "model_v1.0_" class TestEscape: def test_html_chars(self): result = app.escape("") assert "