11"""Tests for the colorspace detection and conversion module."""
22
3- import pytest
3+ import shutil
4+ import subprocess
45from unittest .mock import patch , MagicMock
56
7+ import pytest
8+
69from sharp_frames .processing .colorspace import (
710 VideoColorInfo ,
811 ColorPrimaries ,
@@ -198,6 +201,8 @@ def test_filter_for_display_p3(self):
198201 assert 'colorspace' in filter_str
199202 assert 'bt709' in filter_str
200203 assert 'smpte432' in filter_str
204+ assert 'iprimaries=smpte432' in filter_str
205+ assert 'iall=smpte432' not in filter_str
201206
202207 @patch ('sharp_frames.processing.colorspace.is_zscale_available' , return_value = True )
203208 def test_hdr_filter_with_zscale (self , mock_zscale ):
@@ -219,17 +224,77 @@ def test_hdr_filter_with_zscale(self, mock_zscale):
219224
220225 @patch ('sharp_frames.processing.colorspace.is_zscale_available' , return_value = False )
221226 def test_hdr_filter_fallback_without_zscale (self , mock_zscale ):
222- """HDR filter falls back to colorspace when zscale unavailable."""
227+ """HDR conversion fails clearly when tone mapping is unavailable."""
223228 info = VideoColorInfo (
224229 color_primaries = ColorPrimaries .BT2020 ,
225230 transfer_function = TransferFunction .PQ ,
226231 color_matrix = ColorMatrix .BT2020_NCL ,
227232 is_hdr = True
228233 )
229- filter_str = build_colorspace_filter (info )
230- assert filter_str is not None
231- assert 'colorspace' in filter_str
232- assert 'zscale' not in filter_str
234+ with pytest .raises (RuntimeError , match = 'zscale' ):
235+ build_colorspace_filter (info )
236+
237+
238+ @pytest .mark .skipif (shutil .which ('ffmpeg' ) is None , reason = 'FFmpeg is not installed' )
239+ class TestColorspaceFilterIntegration :
240+ """Validate generated filters with a real FFmpeg process."""
241+
242+ @staticmethod
243+ def _run_filter (source_params , filter_string ):
244+ source = (
245+ "testsrc2=size=64x64:rate=1,format=yuv420p,"
246+ f"setparams={ source_params } "
247+ )
248+ result = subprocess .run (
249+ [
250+ 'ffmpeg' , '-hide_banner' , '-loglevel' , 'error' ,
251+ '-f' , 'lavfi' , '-i' , source ,
252+ '-frames:v' , '1' , '-vf' , filter_string ,
253+ '-f' , 'null' , '-'
254+ ],
255+ capture_output = True ,
256+ text = True ,
257+ timeout = 30 ,
258+ )
259+ assert result .returncode == 0 , result .stderr
260+
261+ def test_display_p3_filter_executes (self ):
262+ info = VideoColorInfo (
263+ color_primaries = ColorPrimaries .DISPLAY_P3 ,
264+ transfer_function = TransferFunction .BT709 ,
265+ color_matrix = ColorMatrix .BT709 ,
266+ is_hdr = False ,
267+ )
268+ self ._run_filter (
269+ 'color_primaries=smpte432:color_trc=bt709:colorspace=bt709' ,
270+ build_colorspace_filter (info ),
271+ )
272+
273+ def test_bt2020_sdr_filter_executes (self ):
274+ info = VideoColorInfo (
275+ color_primaries = ColorPrimaries .BT2020 ,
276+ transfer_function = TransferFunction .BT709 ,
277+ color_matrix = ColorMatrix .BT2020_NCL ,
278+ is_hdr = False ,
279+ )
280+ self ._run_filter (
281+ 'color_primaries=bt2020:color_trc=bt709:colorspace=bt2020nc' ,
282+ build_colorspace_filter (info ),
283+ )
284+
285+ def test_hdr_filter_executes (self ):
286+ if not is_zscale_available ():
287+ pytest .skip ("FFmpeg's zscale filter is not available" )
288+ info = VideoColorInfo (
289+ color_primaries = ColorPrimaries .BT2020 ,
290+ transfer_function = TransferFunction .PQ ,
291+ color_matrix = ColorMatrix .BT2020_NCL ,
292+ is_hdr = True ,
293+ )
294+ self ._run_filter (
295+ 'color_primaries=bt2020:color_trc=smpte2084:colorspace=bt2020nc' ,
296+ build_colorspace_filter (info ),
297+ )
233298
234299
235300class TestGetColorInfoDescription :
0 commit comments