Skip to content

Commit c457245

Browse files
authored
Improve test coverage from 79% to 80% (#811)
1 parent fe0f86e commit c457245

File tree

9 files changed

+563
-1
lines changed

9 files changed

+563
-1
lines changed

coverage.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ ignore = [
168168
]
169169

170170
[tool.ruff.lint.per-file-ignores]
171-
"**/tests/**.py" = ["D103", "D104", "D100"]
171+
"**/tests/**.py" = ["D103", "D104", "D100", "DOC501"]
172172

173173
[tool.ruff.format]
174174
quote-style = "double"
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"""Tests for FFmpeg filter schema definitions."""
2+
3+
from ...common.schema import (
4+
FFMpegFilter,
5+
FFMpegIOType,
6+
StreamType,
7+
)
8+
9+
10+
def test_filter_pre_dict_property() -> None:
11+
"""Test that pre_dict property converts pre list to dict."""
12+
filter_def = FFMpegFilter(
13+
name="scale",
14+
description="Scale video",
15+
pre=(("width", "1280"), ("height", "720")),
16+
stream_typings_input=(),
17+
stream_typings_output=(),
18+
)
19+
20+
pre_dict = filter_def.pre_dict
21+
assert isinstance(pre_dict, dict)
22+
assert pre_dict["width"] == "1280"
23+
assert pre_dict["height"] == "720"
24+
25+
26+
def test_filter_to_def_property() -> None:
27+
"""Test that to_def property creates FFMpegFilterDef."""
28+
from ...common.schema import FFMpegFilterDef
29+
30+
filter_def = FFMpegFilter(
31+
name="scale",
32+
description="Scale video",
33+
stream_typings_input=(FFMpegIOType(name="default", type=StreamType.video),),
34+
stream_typings_output=(FFMpegIOType(name="default", type=StreamType.video),),
35+
)
36+
37+
simple_def = filter_def.to_def
38+
assert isinstance(simple_def, FFMpegFilterDef)
39+
assert simple_def.name == "scale"
40+
assert simple_def.typings_input == ("video",)
41+
assert simple_def.typings_output == ("video",)
42+
43+
44+
def test_filter_input_typings_source() -> None:
45+
"""Test input_typings for filter source (no inputs)."""
46+
filter_def = FFMpegFilter(
47+
name="color",
48+
description="Generate color",
49+
stream_typings_input=(),
50+
stream_typings_output=(),
51+
is_filter_source=True,
52+
)
53+
54+
typings = filter_def.input_typings
55+
assert isinstance(typings, set)
56+
assert len(typings) == 0
57+
58+
59+
def test_filter_input_typings_static() -> None:
60+
"""Test input_typings for static input filter."""
61+
filter_def = FFMpegFilter(
62+
name="scale",
63+
description="Scale video",
64+
stream_typings_input=(FFMpegIOType(name="default", type=StreamType.video),),
65+
stream_typings_output=(),
66+
)
67+
68+
typings = filter_def.input_typings
69+
assert StreamType.video in typings
70+
assert len(typings) == 1
71+
72+
73+
def test_filter_input_typings_dynamic_video() -> None:
74+
"""Test input_typings for dynamic input filter with video only."""
75+
filter_def = FFMpegFilter(
76+
name="concat",
77+
description="Concatenate videos",
78+
stream_typings_input=(),
79+
stream_typings_output=(),
80+
is_dynamic_input=True,
81+
formula_typings_input="video",
82+
)
83+
84+
typings = filter_def.input_typings
85+
assert StreamType.video in typings
86+
assert StreamType.audio not in typings
87+
88+
89+
def test_filter_input_typings_dynamic_audio() -> None:
90+
"""Test input_typings for dynamic input filter with audio only."""
91+
filter_def = FFMpegFilter(
92+
name="amix",
93+
description="Mix audio",
94+
stream_typings_input=(),
95+
stream_typings_output=(),
96+
is_dynamic_input=True,
97+
formula_typings_input="audio",
98+
)
99+
100+
typings = filter_def.input_typings
101+
assert StreamType.audio in typings
102+
assert StreamType.video not in typings
103+
104+
105+
def test_filter_input_typings_dynamic_both() -> None:
106+
"""Test input_typings for dynamic input filter with both video and audio."""
107+
filter_def = FFMpegFilter(
108+
name="amerge",
109+
description="Merge audio and video",
110+
stream_typings_input=(),
111+
stream_typings_output=(),
112+
is_dynamic_input=True,
113+
formula_typings_input="videoaudio",
114+
)
115+
116+
typings = filter_def.input_typings
117+
assert StreamType.video in typings
118+
assert StreamType.audio in typings
119+
120+
121+
def test_filter_output_typings_static() -> None:
122+
"""Test output_typings for static output filter."""
123+
filter_def = FFMpegFilter(
124+
name="scale",
125+
description="Scale video",
126+
stream_typings_input=(),
127+
stream_typings_output=(FFMpegIOType(name="default", type=StreamType.video),),
128+
)
129+
130+
typings = filter_def.output_typings
131+
assert StreamType.video in typings
132+
assert len(typings) == 1
133+
134+
135+
def test_filter_output_typings_multiple() -> None:
136+
"""Test output_typings for filter with multiple outputs."""
137+
filter_def = FFMpegFilter(
138+
name="split",
139+
description="Split video",
140+
stream_typings_input=(),
141+
stream_typings_output=(
142+
FFMpegIOType(name="output1", type=StreamType.video),
143+
FFMpegIOType(name="output2", type=StreamType.video),
144+
),
145+
)
146+
147+
typings = filter_def.output_typings
148+
assert StreamType.video in typings
149+
assert len(typings) == 1 # Both outputs are video, so set has 1 element
150+
151+
152+
def test_filter_output_typings_dynamic() -> None:
153+
"""Test output_typings for dynamic output filter."""
154+
filter_def = FFMpegFilter(
155+
name="split",
156+
description="Split stream",
157+
stream_typings_input=(),
158+
stream_typings_output=(),
159+
is_dynamic_output=True,
160+
formula_typings_output="video",
161+
)
162+
163+
typings = filter_def.output_typings
164+
assert StreamType.video in typings

src/ffmpeg/dag/global_runnable/tests/test_runnable.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,69 @@ async def test_run_async_awaitable_is_awaitable(self) -> None:
343343
process = await result
344344
assert isinstance(process, asyncio.subprocess.Process)
345345
await process.wait()
346+
347+
348+
class TestMergeOutputs:
349+
"""Tests for the merge_outputs method."""
350+
351+
def test_merge_outputs_basic(self) -> None:
352+
"""Test that merge_outputs combines multiple output streams."""
353+
video = input("input.mp4").video
354+
output1 = video.output(filename="output1.mp4")
355+
output2 = video.output(filename="output2.webm")
356+
357+
merged = output1.merge_outputs(output2)
358+
359+
# Verify that the merged output is a GlobalStream
360+
from ...nodes import GlobalStream
361+
362+
assert isinstance(merged, GlobalStream)
363+
364+
# Verify compilation includes both outputs
365+
args = merged.compile()
366+
assert "output1.mp4" in args
367+
assert "output2.webm" in args
368+
369+
def test_merge_outputs_multiple(self) -> None:
370+
"""Test that merge_outputs can handle multiple streams."""
371+
video = input("input.mp4").video
372+
output1 = video.output(filename="out1.mp4")
373+
output2 = video.output(filename="out2.mp4")
374+
output3 = video.output(filename="out3.mp4")
375+
376+
merged = output1.merge_outputs(output2, output3)
377+
378+
# Verify all three outputs are in the compiled command
379+
args = merged.compile()
380+
assert "out1.mp4" in args
381+
assert "out2.mp4" in args
382+
assert "out3.mp4" in args
383+
384+
385+
class TestOverwriteOutput:
386+
"""Tests for the overwrite_output method."""
387+
388+
def test_overwrite_output_adds_y_flag(self) -> None:
389+
"""Test that overwrite_output adds the -y flag."""
390+
stream = input("input.mp4").output(filename="output.mp4")
391+
overwrite_stream = stream.overwrite_output()
392+
393+
# Verify the compiled command includes -y
394+
args = overwrite_stream.compile()
395+
assert "-y" in args
396+
397+
398+
class TestRunWithInput:
399+
"""Tests for run method with input parameter."""
400+
401+
def test_run_with_stdin_input(self) -> None:
402+
"""Test that run method accepts input parameter."""
403+
import inspect
404+
405+
stream = input("test.mp4").output(filename="output.mp4")
406+
sig = inspect.signature(stream.run)
407+
408+
assert "input" in sig.parameters
409+
# Check the type annotation
410+
param = sig.parameters["input"]
411+
assert param.default is None

src/ffmpeg/ffprobe/tests/test_parse.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ def test_get_actual_type_nested_union() -> None:
3535
) # Should return the first non-None type
3636

3737

38+
def test_get_actual_type_types_union() -> None:
39+
"""Test _get_actual_type with types.UnionType (Python 3.10+)."""
40+
import types
41+
42+
# Test that it can handle types.UnionType properly
43+
if hasattr(types, "UnionType"):
44+
# Create a union type using the | operator (Python 3.10+)
45+
union_type = int | str | None
46+
assert _get_actual_type(union_type) is int
47+
48+
3849
def test_get_actual_type_string_annotation() -> None:
3950
# Simulate string annotation as might be found in dataclasses
4051
assert _get_actual_type("int") is int
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Tests for audio channel layout definitions."""
2+
3+
from ..channel_layout import CHANNEL_LAYOUT
4+
5+
6+
def test_channel_layout_basic() -> None:
7+
"""Test basic channel layout definitions."""
8+
assert CHANNEL_LAYOUT["mono"] == 1
9+
assert CHANNEL_LAYOUT["stereo"] == 2
10+
11+
12+
def test_channel_layout_surround() -> None:
13+
"""Test surround sound channel layouts."""
14+
assert CHANNEL_LAYOUT["5.1"] == 6
15+
assert CHANNEL_LAYOUT["7.1"] == 8
16+
17+
18+
def test_channel_layout_complex() -> None:
19+
"""Test complex channel layouts."""
20+
assert CHANNEL_LAYOUT["5.1.2"] == 8
21+
assert CHANNEL_LAYOUT["7.1.4"] == 12
22+
assert CHANNEL_LAYOUT["22.2"] == 24
23+
24+
25+
def test_channel_layout_special() -> None:
26+
"""Test special channel layouts."""
27+
assert CHANNEL_LAYOUT["quad"] == 4
28+
assert CHANNEL_LAYOUT["hexagonal"] == 6
29+
assert CHANNEL_LAYOUT["octagonal"] == 8
30+
assert CHANNEL_LAYOUT["hexadecagonal"] == 16
31+
32+
33+
def test_channel_layout_downmix() -> None:
34+
"""Test downmix channel layout."""
35+
assert CHANNEL_LAYOUT["downmix"] == 2
36+
37+
38+
def test_channel_layout_all_defined() -> None:
39+
"""Test that all channel layouts are defined with valid channel counts."""
40+
for layout, channels in CHANNEL_LAYOUT.items():
41+
assert isinstance(layout, str)
42+
assert isinstance(channels, int)
43+
assert channels > 0

0 commit comments

Comments
 (0)