from io import BytesIO from pathlib import Path from typing import Any, Dict import pytest from PIL import Image as PILImage from evalscope.api.benchmark import BenchmarkMeta, VisionLanguageAdapter from evalscope.api.dataset import Sample from evalscope.api.messages import ContentAudio, ContentImage, ContentText, ContentVideo from evalscope.config import TaskConfig from evalscope.utils.io_utils import bytes_to_base64 class DummyVisionLanguageAdapter(VisionLanguageAdapter): def record_to_sample(self, record: Dict[str, Any]) -> Sample: return Sample(input='', target='') @pytest.fixture def adapter() -> DummyVisionLanguageAdapter: return DummyVisionLanguageAdapter( benchmark_meta=BenchmarkMeta(name='dummy_vlm', dataset_id='dummy', eval_split='test'), task_config=TaskConfig(datasets=['dummy_vlm']), ) @pytest.fixture def png_bytes() -> bytes: image = PILImage.new(mode='RGB', size=(10, 10), color=(255, 0, 0)) buffer = BytesIO() image.save(buffer, format='PNG') return buffer.getvalue() @pytest.fixture def jpeg_bytes() -> bytes: image = PILImage.new(mode='RGB', size=(10, 10), color=(0, 0, 255)) buffer = BytesIO() image.save(buffer, format='JPEG') return buffer.getvalue() def test_bytes_to_base64_guess_mimetype_uses_detected_header(jpeg_bytes: bytes) -> None: base64_image = bytes_to_base64(jpeg_bytes, add_header=True, guess_mimetype=True) assert base64_image.startswith('data:image/jpeg;base64,') def test_bytes_to_base64_guess_mimetype_falls_back_when_unknown() -> None: base64_blob = bytes_to_base64( b'not-an-image', format='png', add_header=True, content_type='image', guess_mimetype=True ) assert base64_blob.startswith('data:image/png;base64,') def test_extract_media_normalizes_hf_image_bytes(adapter: DummyVisionLanguageAdapter, png_bytes: bytes) -> None: image_map = adapter._extract_media({'images': [{'bytes': png_bytes}]}, media_type='image') assert isinstance(image_map[1], dict) and image_map[1]['url'].startswith('data:image/png;base64,') def test_extract_media_accepts_api_ready_image_dict(adapter: DummyVisionLanguageAdapter) -> None: image_map = adapter._extract_media({'images': [{'url': 'https://example.com/cat.png'}]}, media_type='image') content_list = adapter._parse_text_with_media(' Describe the animal.', image_map=image_map) assert any( isinstance(content, ContentImage) and content.image == 'https://example.com/cat.png' for content in content_list ) def test_extract_media_rejects_plural_scalar_container(adapter: DummyVisionLanguageAdapter) -> None: with pytest.raises(TypeError): adapter._extract_media({'images': 'https://example.com/cat.png'}, media_type='image') def test_extract_media_rejects_wrong_mime_for_audio_bytes( adapter: DummyVisionLanguageAdapter, png_bytes: bytes ) -> None: with pytest.raises(ValueError): adapter._extract_media({'audios': [{'bytes': png_bytes}]}, media_type='audio') def test_extract_media_skips_empty_cells(adapter: DummyVisionLanguageAdapter) -> None: """Sparse csv/tsv columns yield empty strings, whose placeholders must stay unresolved.""" record = {'image_1': 'https://example.com/cat.png', 'image_2': '', 'image_3': None} image_map = adapter._extract_media(record, media_type='image') content_list = adapter._parse_text_with_media(' vs vs ', image_map=image_map) assert set(image_map) == {1} assert len([content for content in content_list if isinstance(content, ContentImage)]) == 1 def test_extract_media_raises_on_malformed_empty_dict(adapter: DummyVisionLanguageAdapter) -> None: with pytest.raises(ValueError): adapter._extract_media({'image_1': {}}, media_type='image') def test_extract_media_bytes_drops_stale_path(adapter: DummyVisionLanguageAdapter, png_bytes: bytes) -> None: image_map = adapter._extract_media({'image_1': {'bytes': png_bytes, 'path': 'stale.png'}}, media_type='image') assert image_map[1]['url'].startswith('data:image/png;base64,') assert 'path' not in image_map[1] def test_extract_media_rejects_unsupported_format_hint(adapter: DummyVisionLanguageAdapter) -> None: with pytest.raises(ValueError): adapter._extract_media({'audio_1': 'crowd.wav', 'audio_1_format': 'flac'}, media_type='audio') def test_parse_text_warns_once_per_missing_placeholder(adapter: DummyVisionLanguageAdapter) -> None: adapter._parse_text_with_media(' and and ', image_map={}) assert adapter._missing_media_warned == {'', ''} @pytest.mark.parametrize( 'audio, expected_format', [ ('data:audio/wav;base64,UklGRiQAAABXQVZF', 'wav'), ('data:audio/x-wav;base64,UklGRiQAAABXQVZF', 'wav'), ('data:audio/mp3;base64,SUQzAwAA', 'mp3'), ('data:audio/mpeg;base64,SUQzAwAA', 'mp3'), ('https://example.com/crowd.wav', 'wav'), ('https://example.com/crowd.wav?token=abc', 'wav'), ], ) def test_content_audio_keeps_declared_format( adapter: DummyVisionLanguageAdapter, audio: str, expected_format: str ) -> None: audio_map = adapter._extract_media({'audio_1': audio}, media_type='audio') content_list = adapter._parse_text_with_media('Hear