MogensR commited on
Commit
811c5bc
·
1 Parent(s): 1b16c79

Create tests/conftest.py

Browse files
Files changed (1) hide show
  1. tests/conftest.py +351 -0
tests/conftest.py ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Pytest configuration and fixtures for BackgroundFX Pro tests.
3
+ """
4
+
5
+ import pytest
6
+ import numpy as np
7
+ import torch
8
+ import cv2
9
+ import tempfile
10
+ import shutil
11
+ from pathlib import Path
12
+ from unittest.mock import Mock, MagicMock
13
+ import os
14
+ import sys
15
+
16
+ # Add parent directory to path for imports
17
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
18
+
19
+
20
+ # ============================================================================
21
+ # Configuration
22
+ # ============================================================================
23
+
24
+ @pytest.fixture(scope="session")
25
+ def test_config():
26
+ """Test configuration."""
27
+ return {
28
+ 'device': 'cpu', # Use CPU for testing
29
+ 'test_data_dir': Path(__file__).parent / 'data',
30
+ 'temp_dir': tempfile.mkdtemp(prefix='bgfx_test_'),
31
+ 'max_test_duration': 30, # seconds
32
+ 'use_gpu': torch.cuda.is_available()
33
+ }
34
+
35
+
36
+ @pytest.fixture(scope="session", autouse=True)
37
+ def cleanup(test_config):
38
+ """Cleanup after all tests."""
39
+ yield
40
+ # Clean up temp directory
41
+ if os.path.exists(test_config['temp_dir']):
42
+ shutil.rmtree(test_config['temp_dir'])
43
+
44
+
45
+ # ============================================================================
46
+ # Image and Video Fixtures
47
+ # ============================================================================
48
+
49
+ @pytest.fixture
50
+ def sample_image():
51
+ """Create a sample image for testing."""
52
+ # Create 512x512 RGB image with a person-like shape
53
+ image = np.zeros((512, 512, 3), dtype=np.uint8)
54
+
55
+ # Add background
56
+ image[:, :] = [100, 150, 200] # Blue background
57
+
58
+ # Add person-like shape (simple rectangle for testing)
59
+ cv2.rectangle(image, (150, 100), (350, 450), (50, 100, 50), -1)
60
+
61
+ # Add some texture
62
+ noise = np.random.randint(0, 20, (512, 512, 3), dtype=np.uint8)
63
+ image = cv2.add(image, noise)
64
+
65
+ return image
66
+
67
+
68
+ @pytest.fixture
69
+ def sample_mask():
70
+ """Create a sample mask for testing."""
71
+ mask = np.zeros((512, 512), dtype=np.uint8)
72
+ # Create person mask
73
+ cv2.rectangle(mask, (150, 100), (350, 450), 255, -1)
74
+ # Add some edge refinement
75
+ mask = cv2.GaussianBlur(mask, (5, 5), 2)
76
+ return mask
77
+
78
+
79
+ @pytest.fixture
80
+ def sample_background():
81
+ """Create a sample background image."""
82
+ background = np.zeros((512, 512, 3), dtype=np.uint8)
83
+ # Create gradient background
84
+ for i in range(512):
85
+ background[i, :] = [
86
+ int(255 * (i / 512)), # Red gradient
87
+ 100, # Fixed green
88
+ int(255 * (1 - i / 512)) # Blue inverse gradient
89
+ ]
90
+ return background
91
+
92
+
93
+ @pytest.fixture
94
+ def sample_video(test_config):
95
+ """Create a sample video file for testing."""
96
+ video_path = Path(test_config['temp_dir']) / 'test_video.mp4'
97
+
98
+ # Create video writer
99
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
100
+ out = cv2.VideoWriter(str(video_path), fourcc, 30.0, (512, 512))
101
+
102
+ # Write 30 frames (1 second at 30fps)
103
+ for i in range(30):
104
+ frame = np.zeros((512, 512, 3), dtype=np.uint8)
105
+ # Animate a moving rectangle
106
+ x = 100 + i * 5
107
+ cv2.rectangle(frame, (x, 200), (x + 100, 400), (0, 255, 0), -1)
108
+ out.write(frame)
109
+
110
+ out.release()
111
+ return str(video_path)
112
+
113
+
114
+ # ============================================================================
115
+ # Model Fixtures
116
+ # ============================================================================
117
+
118
+ @pytest.fixture
119
+ def mock_model():
120
+ """Create a mock ML model for testing."""
121
+ model = MagicMock()
122
+ model.eval = MagicMock(return_value=None)
123
+ model.to = MagicMock(return_value=model)
124
+
125
+ # Mock forward pass
126
+ def forward(x):
127
+ batch_size = x.shape[0] if hasattr(x, 'shape') else 1
128
+ return torch.randn(batch_size, 1, 512, 512)
129
+
130
+ model.__call__ = MagicMock(side_effect=forward)
131
+ model.forward = MagicMock(side_effect=forward)
132
+
133
+ return model
134
+
135
+
136
+ @pytest.fixture
137
+ def mock_sam2_predictor():
138
+ """Create a mock SAM2 predictor."""
139
+ predictor = MagicMock()
140
+
141
+ def predict(image):
142
+ h, w = image.shape[:2] if len(image.shape) > 2 else (512, 512)
143
+ return np.random.randint(0, 2, (h, w), dtype=np.uint8) * 255
144
+
145
+ predictor.predict = MagicMock(side_effect=predict)
146
+ predictor.set_image = MagicMock(return_value=None)
147
+
148
+ return predictor
149
+
150
+
151
+ @pytest.fixture
152
+ def mock_matanyone_model():
153
+ """Create a mock MatAnyone model."""
154
+ model = MagicMock()
155
+
156
+ def refine(image, mask):
157
+ return cv2.GaussianBlur(mask, (5, 5), 2)
158
+
159
+ model.refine = MagicMock(side_effect=refine)
160
+
161
+ return model
162
+
163
+
164
+ # ============================================================================
165
+ # Pipeline and Processing Fixtures
166
+ # ============================================================================
167
+
168
+ @pytest.fixture
169
+ def pipeline_config():
170
+ """Create pipeline configuration for testing."""
171
+ from api.pipeline import PipelineConfig
172
+
173
+ return PipelineConfig(
174
+ use_gpu=False, # CPU for testing
175
+ quality_preset='medium',
176
+ enable_cache=False, # Disable cache for testing
177
+ batch_size=1,
178
+ max_workers=2
179
+ )
180
+
181
+
182
+ @pytest.fixture
183
+ def mock_pipeline(pipeline_config):
184
+ """Create a mock processing pipeline."""
185
+ from api.pipeline import ProcessingPipeline
186
+
187
+ # Mock the pipeline to avoid loading real models
188
+ with pytest.MonkeyPatch().context() as m:
189
+ m.setattr('api.pipeline.ModelFactory.load_model',
190
+ lambda self, *args, **kwargs: Mock())
191
+ pipeline = ProcessingPipeline(pipeline_config)
192
+
193
+ return pipeline
194
+
195
+
196
+ # ============================================================================
197
+ # API and Server Fixtures
198
+ # ============================================================================
199
+
200
+ @pytest.fixture
201
+ def api_client():
202
+ """Create a test client for the API."""
203
+ from fastapi.testclient import TestClient
204
+ from api.api_server import app
205
+
206
+ return TestClient(app)
207
+
208
+
209
+ @pytest.fixture
210
+ def mock_job_manager():
211
+ """Create a mock job manager."""
212
+ manager = MagicMock()
213
+ manager.create_job = MagicMock(return_value='test-job-123')
214
+ manager.get_job = MagicMock(return_value={'status': 'processing'})
215
+ manager.update_job = MagicMock(return_value=None)
216
+
217
+ return manager
218
+
219
+
220
+ # ============================================================================
221
+ # File System Fixtures
222
+ # ============================================================================
223
+
224
+ @pytest.fixture
225
+ def temp_dir(test_config):
226
+ """Create a temporary directory for test files."""
227
+ temp_path = Path(test_config['temp_dir']) / 'test_run'
228
+ temp_path.mkdir(parents=True, exist_ok=True)
229
+ yield temp_path
230
+ # Cleanup
231
+ if temp_path.exists():
232
+ shutil.rmtree(temp_path)
233
+
234
+
235
+ @pytest.fixture
236
+ def sample_files(temp_dir, sample_image):
237
+ """Create sample files in temp directory."""
238
+ files = {}
239
+
240
+ # Save sample image
241
+ image_path = temp_dir / 'sample.jpg'
242
+ cv2.imwrite(str(image_path), sample_image)
243
+ files['image'] = image_path
244
+
245
+ # Create multiple images for batch testing
246
+ for i in range(3):
247
+ path = temp_dir / f'image_{i}.jpg'
248
+ cv2.imwrite(str(path), sample_image)
249
+ files[f'image_{i}'] = path
250
+
251
+ return files
252
+
253
+
254
+ # ============================================================================
255
+ # Model Registry Fixtures
256
+ # ============================================================================
257
+
258
+ @pytest.fixture
259
+ def mock_registry():
260
+ """Create a mock model registry."""
261
+ from models.registry import ModelRegistry, ModelInfo, ModelTask, ModelFramework
262
+
263
+ registry = ModelRegistry(models_dir=Path(tempfile.mkdtemp()))
264
+
265
+ # Add test model
266
+ test_model = ModelInfo(
267
+ model_id='test-model',
268
+ name='Test Model',
269
+ version='1.0',
270
+ task=ModelTask.SEGMENTATION,
271
+ framework=ModelFramework.PYTORCH,
272
+ url='http://example.com/model.pth',
273
+ filename='test_model.pth',
274
+ file_size=1000000
275
+ )
276
+
277
+ registry.register_model(test_model)
278
+
279
+ return registry
280
+
281
+
282
+ # ============================================================================
283
+ # WebSocket Fixtures
284
+ # ============================================================================
285
+
286
+ @pytest.fixture
287
+ def mock_websocket():
288
+ """Create a mock WebSocket connection."""
289
+ ws = MagicMock()
290
+ ws.accept = MagicMock(return_value=None)
291
+ ws.send_json = MagicMock(return_value=None)
292
+ ws.receive_text = MagicMock(return_value='{"type": "ping", "data": {}}')
293
+
294
+ return ws
295
+
296
+
297
+ # ============================================================================
298
+ # Utility Fixtures
299
+ # ============================================================================
300
+
301
+ @pytest.fixture
302
+ def mock_progress_callback():
303
+ """Create a mock progress callback."""
304
+ callback = MagicMock()
305
+ return callback
306
+
307
+
308
+ @pytest.fixture
309
+ def device():
310
+ """Get device for testing."""
311
+ return 'cuda' if torch.cuda.is_available() else 'cpu'
312
+
313
+
314
+ @pytest.fixture
315
+ def performance_timer():
316
+ """Timer for performance testing."""
317
+ import time
318
+
319
+ class Timer:
320
+ def __init__(self):
321
+ self.start_time = None
322
+ self.elapsed = 0
323
+
324
+ def __enter__(self):
325
+ self.start_time = time.time()
326
+ return self
327
+
328
+ def __exit__(self, *args):
329
+ self.elapsed = time.time() - self.start_time
330
+
331
+ return Timer
332
+
333
+
334
+ # ============================================================================
335
+ # Markers
336
+ # ============================================================================
337
+
338
+ def pytest_configure(config):
339
+ """Register custom markers."""
340
+ config.addinivalue_line(
341
+ "markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')"
342
+ )
343
+ config.addinivalue_line(
344
+ "markers", "gpu: marks tests that require GPU"
345
+ )
346
+ config.addinivalue_line(
347
+ "markers", "integration: marks integration tests"
348
+ )
349
+ config.addinivalue_line(
350
+ "markers", "unit: marks unit tests"
351
+ )