More video work, planning, etc. -- Need to mark things EXPERIMENTAL.
This commit is contained in:
@@ -451,3 +451,231 @@ class TestEdgeCases:
|
||||
)
|
||||
|
||||
assert decoded.message == special_msg
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# VIDEO STEGANOGRAPHY TESTS (v4.4.0)
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_video_bytes():
|
||||
"""Create a minimal test video using ffmpeg.
|
||||
|
||||
Creates a 2-second test video with solid color frames.
|
||||
Returns None if ffmpeg is not available.
|
||||
"""
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
if not shutil.which("ffmpeg"):
|
||||
return None
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as f:
|
||||
output_path = f.name
|
||||
|
||||
try:
|
||||
# Create a simple 2-second video with colored frames
|
||||
# Using lavfi (libavfilter) to generate test pattern
|
||||
result = subprocess.run(
|
||||
[
|
||||
"ffmpeg",
|
||||
"-y",
|
||||
"-f",
|
||||
"lavfi",
|
||||
"-i",
|
||||
"color=c=blue:s=320x240:d=2:r=10",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
"-g",
|
||||
"5", # GOP size - creates I-frames every 5 frames
|
||||
output_path,
|
||||
],
|
||||
capture_output=True,
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
return None
|
||||
|
||||
with open(output_path, "rb") as f:
|
||||
video_data = f.read()
|
||||
|
||||
return video_data
|
||||
except Exception:
|
||||
return None
|
||||
finally:
|
||||
import os
|
||||
|
||||
try:
|
||||
os.unlink(output_path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
class TestVideoSupport:
|
||||
"""Test video steganography support detection."""
|
||||
|
||||
def test_video_support_flag_exists(self):
|
||||
"""HAS_VIDEO_SUPPORT flag should exist."""
|
||||
assert hasattr(stegasoo, "HAS_VIDEO_SUPPORT")
|
||||
assert isinstance(stegasoo.HAS_VIDEO_SUPPORT, bool)
|
||||
|
||||
def test_video_constants_exist(self):
|
||||
"""Video-related constants should exist."""
|
||||
assert hasattr(stegasoo, "EMBED_MODE_VIDEO_LSB")
|
||||
assert hasattr(stegasoo, "EMBED_MODE_VIDEO_AUTO")
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not stegasoo.HAS_VIDEO_SUPPORT,
|
||||
reason="Video support not available (ffmpeg or dependencies missing)",
|
||||
)
|
||||
class TestVideoFormatDetection:
|
||||
"""Test video format detection."""
|
||||
|
||||
def test_detect_video_format_mp4(self, test_video_bytes):
|
||||
"""Should detect MP4 format from magic bytes."""
|
||||
if test_video_bytes is None:
|
||||
pytest.skip("Could not create test video")
|
||||
|
||||
from stegasoo import detect_video_format
|
||||
|
||||
fmt = detect_video_format(test_video_bytes)
|
||||
assert fmt in ("mp4", "mov")
|
||||
|
||||
def test_detect_video_format_unknown(self):
|
||||
"""Should return 'unknown' for non-video data."""
|
||||
from stegasoo import detect_video_format
|
||||
|
||||
fmt = detect_video_format(b"not a video")
|
||||
assert fmt == "unknown"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not stegasoo.HAS_VIDEO_SUPPORT,
|
||||
reason="Video support not available (ffmpeg or dependencies missing)",
|
||||
)
|
||||
class TestVideoInfo:
|
||||
"""Test video metadata extraction."""
|
||||
|
||||
def test_get_video_info(self, test_video_bytes):
|
||||
"""Should extract video metadata."""
|
||||
if test_video_bytes is None:
|
||||
pytest.skip("Could not create test video")
|
||||
|
||||
from stegasoo import get_video_info
|
||||
|
||||
info = get_video_info(test_video_bytes)
|
||||
|
||||
assert info.width == 320
|
||||
assert info.height == 240
|
||||
assert info.fps > 0
|
||||
assert info.duration_seconds > 0
|
||||
assert info.total_frames > 0
|
||||
assert info.format in ("mp4", "mov")
|
||||
|
||||
def test_validate_video(self, test_video_bytes):
|
||||
"""Should validate video data."""
|
||||
if test_video_bytes is None:
|
||||
pytest.skip("Could not create test video")
|
||||
|
||||
from stegasoo import validate_video
|
||||
|
||||
result = validate_video(test_video_bytes, check_duration=False)
|
||||
|
||||
assert result.is_valid
|
||||
assert result.details.get("format") in ("mp4", "mov")
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not stegasoo.HAS_VIDEO_SUPPORT,
|
||||
reason="Video support not available (ffmpeg or dependencies missing)",
|
||||
)
|
||||
class TestVideoCapacity:
|
||||
"""Test video capacity calculation."""
|
||||
|
||||
def test_calculate_video_capacity(self, test_video_bytes):
|
||||
"""Should calculate steganographic capacity."""
|
||||
if test_video_bytes is None:
|
||||
pytest.skip("Could not create test video")
|
||||
|
||||
from stegasoo import calculate_video_capacity
|
||||
|
||||
capacity_info = calculate_video_capacity(test_video_bytes)
|
||||
|
||||
assert capacity_info.total_frames > 0
|
||||
assert capacity_info.i_frames > 0
|
||||
assert capacity_info.usable_capacity_bytes > 0
|
||||
assert capacity_info.embed_mode == "video_lsb"
|
||||
assert capacity_info.resolution == (320, 240)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not stegasoo.HAS_VIDEO_SUPPORT,
|
||||
reason="Video support not available (ffmpeg or dependencies missing)",
|
||||
)
|
||||
class TestVideoEncodeDecode:
|
||||
"""Test video steganography round-trip."""
|
||||
|
||||
def test_video_roundtrip(self, test_video_bytes, ref_bytes):
|
||||
"""Test encoding and decoding a message in video."""
|
||||
if test_video_bytes is None:
|
||||
pytest.skip("Could not create test video")
|
||||
|
||||
from stegasoo import decode_video, encode_video
|
||||
|
||||
message = "Secret video message!"
|
||||
|
||||
# Encode
|
||||
stego_video, stats = encode_video(
|
||||
message=message,
|
||||
reference_photo=ref_bytes,
|
||||
carrier_video=test_video_bytes,
|
||||
passphrase=TEST_PASSPHRASE,
|
||||
pin=TEST_PIN,
|
||||
)
|
||||
|
||||
assert stego_video
|
||||
assert len(stego_video) > 0
|
||||
assert stats.frames_modified > 0
|
||||
assert stats.codec == "ffv1" # Should use lossless codec
|
||||
|
||||
# Decode
|
||||
result = decode_video(
|
||||
stego_video=stego_video,
|
||||
reference_photo=ref_bytes,
|
||||
passphrase=TEST_PASSPHRASE,
|
||||
pin=TEST_PIN,
|
||||
)
|
||||
|
||||
assert result.is_text
|
||||
assert result.message == message
|
||||
|
||||
def test_video_wrong_passphrase_fails(self, test_video_bytes, ref_bytes):
|
||||
"""Decoding with wrong passphrase should fail."""
|
||||
if test_video_bytes is None:
|
||||
pytest.skip("Could not create test video")
|
||||
|
||||
from stegasoo import decode_video, encode_video
|
||||
|
||||
message = "Secret video message!"
|
||||
|
||||
stego_video, _ = encode_video(
|
||||
message=message,
|
||||
reference_photo=ref_bytes,
|
||||
carrier_video=test_video_bytes,
|
||||
passphrase=TEST_PASSPHRASE,
|
||||
pin=TEST_PIN,
|
||||
)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
decode_video(
|
||||
stego_video=stego_video,
|
||||
reference_photo=ref_bytes,
|
||||
passphrase="wrong passphrase words here",
|
||||
pin=TEST_PIN,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user