MogensR commited on
Commit
f11fb39
·
1 Parent(s): d02fbe8

Create tests/test_app.py

Browse files
Files changed (1) hide show
  1. tests/test_app.py +332 -0
tests/test_app.py ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tests for the main application and video processor.
3
+ """
4
+
5
+ import pytest
6
+ from unittest.mock import Mock, patch, MagicMock
7
+ import numpy as np
8
+ import cv2
9
+ from pathlib import Path
10
+ import tempfile
11
+
12
+ # Import from main app
13
+ from app import VideoProcessor, processor
14
+
15
+
16
+ class TestVideoProcessor:
17
+ """Test the main VideoProcessor class."""
18
+
19
+ @pytest.fixture
20
+ def video_processor(self):
21
+ """Create a test video processor."""
22
+ with patch('app.model_loader.ModelLoader') as mock_loader:
23
+ with patch('app.device_manager.DeviceManager') as mock_device:
24
+ mock_device.return_value.get_optimal_device.return_value = 'cpu'
25
+ vp = VideoProcessor()
26
+ return vp
27
+
28
+ def test_initialization(self, video_processor):
29
+ """Test VideoProcessor initialization."""
30
+ assert video_processor is not None
31
+ assert video_processor.models_loaded == False
32
+ assert video_processor.cancel_event is not None
33
+
34
+ @patch('app.model_loader.ModelLoader.load_all_models')
35
+ def test_load_models(self, mock_load, video_processor):
36
+ """Test model loading."""
37
+ mock_sam2 = Mock()
38
+ mock_matanyone = Mock()
39
+ mock_load.return_value = (mock_sam2, mock_matanyone)
40
+
41
+ result = video_processor.load_models()
42
+
43
+ assert mock_load.called
44
+ assert video_processor.models_loaded == True
45
+ assert "Models already loaded" in result or "loaded" in result.lower()
46
+
47
+ def test_load_models_with_progress(self, video_processor):
48
+ """Test model loading with progress callback."""
49
+ progress_values = []
50
+
51
+ def progress_callback(value, message):
52
+ progress_values.append((value, message))
53
+
54
+ with patch.object(video_processor.model_loader, 'load_all_models') as mock_load:
55
+ mock_load.return_value = (Mock(), Mock())
56
+ video_processor.load_models(progress_callback)
57
+
58
+ assert video_processor.models_loaded == True
59
+
60
+ def test_process_video_without_models(self, video_processor):
61
+ """Test video processing without loaded models."""
62
+ result_path, message = video_processor.process_video(
63
+ "test.mp4", "blur"
64
+ )
65
+
66
+ assert result_path is None
67
+ assert "not loaded" in message.lower()
68
+
69
+ @patch('app.validate_video_file')
70
+ @patch.object(VideoProcessor, '_process_single_stage')
71
+ def test_process_video_single_stage(self, mock_process, mock_validate, video_processor):
72
+ """Test single-stage video processing."""
73
+ mock_validate.return_value = (True, "Valid")
74
+ mock_process.return_value = ("/tmp/output.mp4", "Success")
75
+
76
+ video_processor.models_loaded = True
77
+ video_processor.core_processor = Mock()
78
+
79
+ result_path, message = video_processor.process_video(
80
+ "test.mp4", "blur", use_two_stage=False
81
+ )
82
+
83
+ assert mock_process.called
84
+ assert result_path == "/tmp/output.mp4"
85
+ assert "Success" in message
86
+
87
+ @patch('app.TWO_STAGE_AVAILABLE', True)
88
+ @patch('app.validate_video_file')
89
+ @patch.object(VideoProcessor, '_process_two_stage')
90
+ def test_process_video_two_stage(self, mock_process, mock_validate, video_processor):
91
+ """Test two-stage video processing."""
92
+ mock_validate.return_value = (True, "Valid")
93
+ mock_process.return_value = ("/tmp/output.mp4", "Success")
94
+
95
+ video_processor.models_loaded = True
96
+ video_processor.core_processor = Mock()
97
+ video_processor.two_stage_processor = Mock()
98
+
99
+ result_path, message = video_processor.process_video(
100
+ "test.mp4", "blur", use_two_stage=True
101
+ )
102
+
103
+ assert mock_process.called
104
+ assert result_path == "/tmp/output.mp4"
105
+
106
+ def test_cancel_processing(self, video_processor):
107
+ """Test processing cancellation."""
108
+ video_processor.cancel_processing()
109
+ assert video_processor.cancel_event.is_set()
110
+
111
+ def test_get_status(self, video_processor):
112
+ """Test getting processor status."""
113
+ status = video_processor.get_status()
114
+
115
+ assert "models_loaded" in status
116
+ assert "device" in status
117
+ assert "memory_usage" in status
118
+ assert status["models_loaded"] == False
119
+
120
+ def test_cleanup_resources(self, video_processor):
121
+ """Test resource cleanup."""
122
+ with patch.object(video_processor.memory_manager, 'cleanup_aggressive'):
123
+ with patch.object(video_processor.model_loader, 'cleanup'):
124
+ video_processor.cleanup_resources()
125
+
126
+ assert True # Cleanup should not raise exceptions
127
+
128
+
129
+ class TestCoreVideoProcessor:
130
+ """Test the CoreVideoProcessor from video_processor module."""
131
+
132
+ @pytest.fixture
133
+ def core_processor(self, mock_sam2_predictor, mock_matanyone_model):
134
+ """Create a test core processor."""
135
+ from video_processor import CoreVideoProcessor
136
+ from app_config import ProcessingConfig
137
+ from memory_manager import MemoryManager
138
+
139
+ config = ProcessingConfig()
140
+ memory_mgr = MemoryManager('cpu')
141
+
142
+ processor = CoreVideoProcessor(
143
+ sam2_predictor=mock_sam2_predictor,
144
+ matanyone_model=mock_matanyone_model,
145
+ config=config,
146
+ memory_mgr=memory_mgr
147
+ )
148
+ return processor
149
+
150
+ def test_core_processor_initialization(self, core_processor):
151
+ """Test CoreVideoProcessor initialization."""
152
+ assert core_processor is not None
153
+ assert core_processor.processing_active == False
154
+ assert core_processor.stats is not None
155
+
156
+ def test_prepare_background(self, core_processor):
157
+ """Test background preparation."""
158
+ # Test professional background
159
+ background = core_processor.prepare_background(
160
+ "blur", None, 512, 512
161
+ )
162
+ # May return None if utilities not available
163
+ assert background is None or background.shape == (512, 512, 3)
164
+
165
+ def test_get_processing_capabilities(self, core_processor):
166
+ """Test getting processing capabilities."""
167
+ capabilities = core_processor.get_processing_capabilities()
168
+
169
+ assert "sam2_available" in capabilities
170
+ assert "matanyone_available" in capabilities
171
+ assert "quality_preset" in capabilities
172
+ assert "supported_formats" in capabilities
173
+
174
+ def test_get_status(self, core_processor):
175
+ """Test getting processor status."""
176
+ status = core_processor.get_status()
177
+
178
+ assert "processing_active" in status
179
+ assert "models_available" in status
180
+ assert "statistics" in status
181
+ assert "memory_usage" in status
182
+
183
+
184
+ class TestApplicationIntegration:
185
+ """Integration tests for the main application."""
186
+
187
+ @pytest.mark.integration
188
+ def test_global_processor_instance(self):
189
+ """Test the global processor instance."""
190
+ assert processor is not None
191
+ assert isinstance(processor, VideoProcessor)
192
+
193
+ @pytest.mark.integration
194
+ @patch('app.model_loader.ModelLoader.load_all_models')
195
+ def test_model_loading_flow(self, mock_load):
196
+ """Test complete model loading flow."""
197
+ mock_load.return_value = (Mock(), Mock())
198
+
199
+ # Use global processor
200
+ result = processor.load_models()
201
+
202
+ assert processor.models_loaded == True
203
+ assert result is not None
204
+
205
+ @pytest.mark.integration
206
+ @pytest.mark.slow
207
+ def test_memory_management(self):
208
+ """Test memory management during processing."""
209
+ import psutil
210
+ import os
211
+
212
+ process = psutil.Process(os.getpid())
213
+ initial_memory = process.memory_info().rss / 1024 / 1024 # MB
214
+
215
+ # Simulate processing
216
+ for _ in range(5):
217
+ # Create and discard large arrays
218
+ data = np.random.randint(0, 255, (1024, 1024, 3), dtype=np.uint8)
219
+ del data
220
+
221
+ processor.cleanup_resources()
222
+
223
+ final_memory = process.memory_info().rss / 1024 / 1024 # MB
224
+ memory_increase = final_memory - initial_memory
225
+
226
+ # Memory increase should be reasonable
227
+ assert memory_increase < 200 # Less than 200MB increase
228
+
229
+
230
+ class TestBackwardCompatibility:
231
+ """Test backward compatibility functions."""
232
+
233
+ def test_load_models_wrapper(self):
234
+ """Test load_models_with_validation wrapper."""
235
+ from app import load_models_with_validation
236
+
237
+ with patch.object(processor, 'load_models') as mock_load:
238
+ mock_load.return_value = "Success"
239
+ result = load_models_with_validation()
240
+
241
+ assert mock_load.called
242
+ assert result == "Success"
243
+
244
+ def test_process_video_wrapper(self):
245
+ """Test process_video_fixed wrapper."""
246
+ from app import process_video_fixed
247
+
248
+ with patch.object(processor, 'process_video') as mock_process:
249
+ mock_process.return_value = ("/tmp/out.mp4", "Success")
250
+
251
+ result = process_video_fixed(
252
+ "test.mp4", "blur", None
253
+ )
254
+
255
+ assert mock_process.called
256
+ assert result[0] == "/tmp/out.mp4"
257
+
258
+ def test_get_model_status_wrapper(self):
259
+ """Test get_model_status wrapper."""
260
+ from app import get_model_status
261
+
262
+ with patch.object(processor, 'get_status') as mock_status:
263
+ mock_status.return_value = {"status": "ok"}
264
+ result = get_model_status()
265
+
266
+ assert mock_status.called
267
+ assert result["status"] == "ok"
268
+
269
+
270
+ class TestErrorHandling:
271
+ """Test error handling in the application."""
272
+
273
+ def test_invalid_video_handling(self):
274
+ """Test handling of invalid video files."""
275
+ with patch('app.validate_video_file') as mock_validate:
276
+ mock_validate.return_value = (False, "Invalid format")
277
+
278
+ processor.models_loaded = True
279
+ result_path, message = processor.process_video(
280
+ "invalid.txt", "blur"
281
+ )
282
+
283
+ assert result_path is None
284
+ assert "Invalid" in message
285
+
286
+ def test_model_loading_failure(self):
287
+ """Test handling of model loading failures."""
288
+ with patch.object(processor.model_loader, 'load_all_models') as mock_load:
289
+ mock_load.side_effect = Exception("Model not found")
290
+
291
+ result = processor.load_models()
292
+
293
+ assert processor.models_loaded == False
294
+ assert "failed" in result.lower()
295
+
296
+ def test_processing_exception_handling(self):
297
+ """Test exception handling during processing."""
298
+ processor.models_loaded = True
299
+ processor.core_processor = Mock()
300
+ processor.core_processor.process_video.side_effect = Exception("Processing failed")
301
+
302
+ with patch('app.validate_video_file', return_value=(True, "Valid")):
303
+ result_path, message = processor.process_video(
304
+ "test.mp4", "blur"
305
+ )
306
+
307
+ assert result_path is None
308
+ assert "error" in message.lower() or "failed" in message.lower()
309
+
310
+
311
+ class TestPerformance:
312
+ """Performance tests for the application."""
313
+
314
+ @pytest.mark.slow
315
+ def test_initialization_speed(self, performance_timer):
316
+ """Test application initialization speed."""
317
+ with performance_timer as timer:
318
+ vp = VideoProcessor()
319
+
320
+ assert timer.elapsed < 1.0 # Should initialize in under 1 second
321
+
322
+ @pytest.mark.slow
323
+ @patch('app.model_loader.ModelLoader.load_all_models')
324
+ def test_model_loading_speed(self, mock_load, performance_timer):
325
+ """Test model loading speed."""
326
+ mock_load.return_value = (Mock(), Mock())
327
+
328
+ with performance_timer as timer:
329
+ processor.load_models()
330
+
331
+ # Mock loading should be very fast
332
+ assert timer.elapsed < 0.5