Spaces:
Running
Running
File size: 6,016 Bytes
b0e0bde 8e89085 b0e0bde |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
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
@runtime_checkable
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
@pytest.fixture
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
@pytest.fixture
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
@pytest.fixture
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
@pytest.fixture
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")
@patch("streamlit.file_uploader")
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
|