202 lines
6.4 KiB
Python
202 lines
6.4 KiB
Python
"""
|
|
Tests for Python primitive effects.
|
|
|
|
Tests that ascii_art, ascii_zones, and other Python primitives
|
|
can be executed via the EffectExecutor.
|
|
"""
|
|
|
|
import subprocess
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
try:
|
|
import numpy as np
|
|
from PIL import Image
|
|
HAS_DEPS = True
|
|
except ImportError:
|
|
HAS_DEPS = False
|
|
|
|
from .primitives import (
|
|
ascii_art_frame,
|
|
ascii_zones_frame,
|
|
get_primitive,
|
|
list_primitives,
|
|
)
|
|
from .ffmpeg_compiler import FFmpegCompiler
|
|
|
|
|
|
def create_test_video(path: Path, duration: float = 0.5, size: str = "64x64") -> bool:
|
|
"""Create a short test video using ffmpeg."""
|
|
cmd = [
|
|
"ffmpeg", "-y",
|
|
"-f", "lavfi", "-i", f"testsrc=duration={duration}:size={size}:rate=10",
|
|
"-c:v", "libx264", "-preset", "ultrafast",
|
|
str(path)
|
|
]
|
|
result = subprocess.run(cmd, capture_output=True)
|
|
return result.returncode == 0
|
|
|
|
|
|
@pytest.mark.skipif(not HAS_DEPS, reason="numpy/PIL not available")
|
|
class TestPrimitives:
|
|
"""Test primitive functions directly."""
|
|
|
|
def test_ascii_art_frame_basic(self):
|
|
"""Test ascii_art_frame produces output of same shape."""
|
|
frame = np.random.randint(0, 255, (64, 64, 3), dtype=np.uint8)
|
|
result = ascii_art_frame(frame, char_size=8)
|
|
assert result.shape == frame.shape
|
|
assert result.dtype == np.uint8
|
|
|
|
def test_ascii_zones_frame_basic(self):
|
|
"""Test ascii_zones_frame produces output of same shape."""
|
|
frame = np.random.randint(0, 255, (64, 64, 3), dtype=np.uint8)
|
|
result = ascii_zones_frame(frame, char_size=8)
|
|
assert result.shape == frame.shape
|
|
assert result.dtype == np.uint8
|
|
|
|
def test_get_primitive(self):
|
|
"""Test primitive lookup."""
|
|
assert get_primitive("ascii_art_frame") is ascii_art_frame
|
|
assert get_primitive("ascii_zones_frame") is ascii_zones_frame
|
|
assert get_primitive("nonexistent") is None
|
|
|
|
def test_list_primitives(self):
|
|
"""Test listing primitives."""
|
|
primitives = list_primitives()
|
|
assert "ascii_art_frame" in primitives
|
|
assert "ascii_zones_frame" in primitives
|
|
assert len(primitives) > 5
|
|
|
|
|
|
class TestFFmpegCompilerPrimitives:
|
|
"""Test FFmpegCompiler python_primitive mappings."""
|
|
|
|
def test_has_python_primitive_ascii_art(self):
|
|
"""Test ascii_art has python_primitive."""
|
|
compiler = FFmpegCompiler()
|
|
assert compiler.has_python_primitive("ascii_art") == "ascii_art_frame"
|
|
|
|
def test_has_python_primitive_ascii_zones(self):
|
|
"""Test ascii_zones has python_primitive."""
|
|
compiler = FFmpegCompiler()
|
|
assert compiler.has_python_primitive("ascii_zones") == "ascii_zones_frame"
|
|
|
|
def test_has_python_primitive_ffmpeg_effect(self):
|
|
"""Test FFmpeg effects don't have python_primitive."""
|
|
compiler = FFmpegCompiler()
|
|
assert compiler.has_python_primitive("brightness") is None
|
|
assert compiler.has_python_primitive("blur") is None
|
|
|
|
def test_compile_effect_returns_none_for_primitives(self):
|
|
"""Test compile_effect returns None for primitive effects."""
|
|
compiler = FFmpegCompiler()
|
|
assert compiler.compile_effect("ascii_art", {}) is None
|
|
assert compiler.compile_effect("ascii_zones", {}) is None
|
|
|
|
|
|
@pytest.mark.skipif(not HAS_DEPS, reason="numpy/PIL not available")
|
|
class TestEffectExecutorPrimitives:
|
|
"""Test EffectExecutor with Python primitives."""
|
|
|
|
def test_executor_loads_primitive(self):
|
|
"""Test that executor finds primitive effects."""
|
|
from ..nodes.effect import _get_python_primitive_effect
|
|
|
|
effect_fn = _get_python_primitive_effect("ascii_art")
|
|
assert effect_fn is not None
|
|
|
|
effect_fn = _get_python_primitive_effect("ascii_zones")
|
|
assert effect_fn is not None
|
|
|
|
def test_executor_rejects_unknown_effect(self):
|
|
"""Test that executor returns None for unknown effects."""
|
|
from ..nodes.effect import _get_python_primitive_effect
|
|
|
|
effect_fn = _get_python_primitive_effect("nonexistent_effect")
|
|
assert effect_fn is None
|
|
|
|
def test_execute_ascii_art_effect(self, tmp_path):
|
|
"""Test executing ascii_art effect on a video."""
|
|
from ..nodes.effect import EffectExecutor
|
|
|
|
# Create test video
|
|
input_path = tmp_path / "input.mp4"
|
|
if not create_test_video(input_path):
|
|
pytest.skip("Could not create test video")
|
|
|
|
output_path = tmp_path / "output.mkv"
|
|
executor = EffectExecutor()
|
|
|
|
result = executor.execute(
|
|
config={"effect": "ascii_art", "char_size": 8},
|
|
inputs=[input_path],
|
|
output_path=output_path,
|
|
)
|
|
|
|
assert result.exists()
|
|
assert result.stat().st_size > 0
|
|
|
|
|
|
def run_all_tests():
|
|
"""Run tests manually."""
|
|
import sys
|
|
|
|
# Check dependencies
|
|
if not HAS_DEPS:
|
|
print("SKIP: numpy/PIL not available")
|
|
return
|
|
|
|
print("Testing primitives...")
|
|
|
|
# Test primitive functions
|
|
frame = np.random.randint(0, 255, (64, 64, 3), dtype=np.uint8)
|
|
|
|
print(" ascii_art_frame...", end=" ")
|
|
result = ascii_art_frame(frame, char_size=8)
|
|
assert result.shape == frame.shape
|
|
print("PASS")
|
|
|
|
print(" ascii_zones_frame...", end=" ")
|
|
result = ascii_zones_frame(frame, char_size=8)
|
|
assert result.shape == frame.shape
|
|
print("PASS")
|
|
|
|
# Test FFmpegCompiler mappings
|
|
print("\nTesting FFmpegCompiler mappings...")
|
|
compiler = FFmpegCompiler()
|
|
|
|
print(" ascii_art python_primitive...", end=" ")
|
|
assert compiler.has_python_primitive("ascii_art") == "ascii_art_frame"
|
|
print("PASS")
|
|
|
|
print(" ascii_zones python_primitive...", end=" ")
|
|
assert compiler.has_python_primitive("ascii_zones") == "ascii_zones_frame"
|
|
print("PASS")
|
|
|
|
# Test executor lookup
|
|
print("\nTesting EffectExecutor...")
|
|
try:
|
|
from ..nodes.effect import _get_python_primitive_effect
|
|
|
|
print(" _get_python_primitive_effect(ascii_art)...", end=" ")
|
|
effect_fn = _get_python_primitive_effect("ascii_art")
|
|
assert effect_fn is not None
|
|
print("PASS")
|
|
|
|
print(" _get_python_primitive_effect(ascii_zones)...", end=" ")
|
|
effect_fn = _get_python_primitive_effect("ascii_zones")
|
|
assert effect_fn is not None
|
|
print("PASS")
|
|
|
|
except ImportError as e:
|
|
print(f"SKIP: {e}")
|
|
|
|
print("\n=== All tests passed ===")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
run_all_tests()
|