Spaces:
Running
Running
from typing import Protocol, runtime_checkable | |
from pathlib import Path | |
from io import BytesIO | |
from PIL import Image | |
import pytest | |
from unittest.mock import MagicMock, patch | |
from streamlit.testing.v1 import AppTest | |
# - tests for apptest/demo_multifile_upload | |
# zero test: no inputs -> empty session state | |
# (or maybe even non-existent session state; for file_uploader we are not allowed to initialise the keyed variable, st borks) | |
# many test: list of 2 inputs -> session state with 2 files | |
# for expectations | |
from input.input_handling import spoof_metadata | |
from input.input_validator import get_image_datetime, get_image_latlon | |
class UploadedFile(Protocol): | |
name: str | |
size: int | |
type: str | |
#RANDO: str | |
_file_urls: list | |
def getvalue(self) -> bytes: ... | |
def read(self) -> bytes: ... | |
class MockUploadedFile(BytesIO): | |
def __init__(self, | |
initial_bytes: bytes, | |
*, | |
name: str, | |
size: int, | |
type: str): | |
super().__init__(initial_bytes) | |
self.name = name # Simulate a filename | |
self.size = size # Simulate file size | |
self.type = type # Simulate MIME type | |
self.file_id = None | |
def mock_uploadedFile(): | |
def _mock_uploadedFile(name: str, size: int, type: str): | |
test_data = b'test data' | |
# now load some real data, if fname exists | |
base = Path(__file__).parent.parent | |
fname = Path(base / f"tests/data/{name}") | |
if fname.exists(): | |
with open(fname, 'rb') as f: | |
#test_data = BytesIO(f.read()) | |
test_data = f.read() | |
else: | |
#print(f"[DDDD] {name}, {size}, {type} not found") | |
raise FileNotFoundError(f"file {fname} not found ({name}, {size}, {type})") | |
return MockUploadedFile( | |
test_data, name=name, size=size, type=type,) | |
return _mock_uploadedFile | |
def mock_uploadedFileNoRealData(): | |
class MockGUIClassFakeData(MagicMock): | |
def __init__(self, *args, **kwargs): | |
super().__init__(*args, **kwargs) | |
name = kwargs.get('fname', 'image2.jpg') | |
size = kwargs.get('size', 123456) | |
type = kwargs.get('type', 'image/jpeg') | |
self.bytes_io = MockUploadedFile( | |
b"test data", name=name, size=size, type=type) | |
self.get_data = MagicMock(return_value=self.bytes_io) | |
# it seems unclear to me which member attributes get set by the MockUploadedFile constructor | |
# - for some reason, size and type get set, but name does not, and results in | |
# <MockGUIClass name='mock.name' id='<12345>'>. | |
# so let's sjust explicitly set all the relevant attributes here. | |
self.name = name | |
self.size = size | |
self.type = type | |
return MockGUIClassFakeData | |
def mock_uploadedFile_List(mock_uploadedFileNoRealData): | |
def create_list_of_mocks(num_files=3, **kwargs): | |
return [mock_uploadedFileNoRealData(**kwargs) for _ in range(num_files)] | |
return create_list_of_mocks | |
def mock_uploadedFile_List_ImageData(mock_uploadedFile): | |
def create_list_of_mocks_realdata(num_files=3, **kwargs): | |
print(f"[D] [mock_uploadedFile_List_Img-internal] num_files: {num_files}") | |
data = [ | |
{"name": "cakes.jpg", "size": 1234, "type": "image/jpeg"}, | |
{"name": "cakes_no_exif_datetime.jpg", "size": 12345, "type": "image/jpeg"}, | |
{"name": "cakes_no_exif_gps.jpg", "size": 123456, "type": "image/jpeg"}, | |
] | |
_the_files = [] | |
for i in range(num_files): | |
_the_files.append( mock_uploadedFile(**data[i])) | |
print(f"========== finished init of {num_files} mock_uploaded files | {len(_the_files)} ==========") | |
return _the_files | |
#return [mock_uploadedFile(**kwargs) for _ in range(num_files)] | |
return create_list_of_mocks_realdata | |
def test_no_input_no_interaction(): | |
at = AppTest.from_file("src/apptest/demo_multifile_upload.py").run() | |
assert at.session_state.observations == {} | |
assert at.session_state.input_author_email == spoof_metadata.get("author_email") | |
def test_mockupload_list(mock_file_uploader_rtn: MagicMock, mock_uploadedFile_List): | |
# Create a list of 2 mock files | |
mock_files = mock_uploadedFile_List(num_files=2, fname="test.jpg", size=100, type="image/jpeg") | |
# Set the return value of the mocked file_uploader to the list of mock files | |
mock_file_uploader_rtn.return_value = mock_files | |
# Run the Streamlit app | |
at = AppTest.from_file("src/apptest/demo_multifile_upload.py").run() | |
# put the mocked file_upload into session state, as if it were the result of a file upload, with the key 'file_uploader_data' | |
at.session_state["file_uploader_data"] = mock_files | |
#print(f"[I] session state: {at.session_state}") | |
#print(f"[I] uploaded files: {at.session_state.file_uploader_data}") | |
if 1: | |
print(f"[I] uploaded files: {at.session_state.file_uploader_data}") | |
for _f in at.session_state.file_uploader_data: | |
print(f"[I] props: {dir(_f)}") | |
print(f"[I] name: {_f.name}") | |
print(f"[I] size: {_f.size}") | |
print(f"[I] type: {_f.type}") | |
print(f"[I] data : {type(_f)} | {type(_f.return_value)} | {_f}") | |
# lets make an image from it. | |
#im = Image.open(_f) | |
# Verify behavior in the app | |
assert len(at.session_state.file_uploader_data) == 2 | |
assert at.session_state.file_uploader_data[0].size == 100 # Check properties of the first file | |
assert at.session_state.file_uploader_data[1].name == "test.jpg" # Check properties of the second file | |