277 lines
8.1 KiB
Python
277 lines
8.1 KiB
Python
"""
|
|
Unit tests for script_parser.
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
|
|
# Add parent directory to path for imports
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from script_parser import (
|
|
parse_timestamp,
|
|
format_timestamp,
|
|
parse_guide_spec,
|
|
parse_audio_spec,
|
|
parse_scene_script,
|
|
ImageGuide,
|
|
SceneChunk,
|
|
)
|
|
|
|
|
|
def test_parse_timestamp_mmss():
|
|
"""Test parsing MM:SS format."""
|
|
assert parse_timestamp("00:00") == 0.0
|
|
assert parse_timestamp("00:30") == 30.0
|
|
assert parse_timestamp("01:00") == 60.0
|
|
assert parse_timestamp("01:30") == 90.0
|
|
assert parse_timestamp("02:15") == 135.0
|
|
|
|
|
|
def test_parse_timestamp_with_ms():
|
|
"""Test parsing MM:SS.ms format."""
|
|
assert parse_timestamp("00:00.5") == 0.5
|
|
assert parse_timestamp("00:01.25") == 1.25
|
|
assert parse_timestamp("01:30.5") == 90.5
|
|
|
|
|
|
def test_parse_timestamp_seconds_only():
|
|
"""Test parsing seconds-only format."""
|
|
assert parse_timestamp("30") == 30.0
|
|
assert parse_timestamp("90") == 90.0
|
|
assert parse_timestamp("15.5") == 15.5
|
|
|
|
|
|
def test_format_timestamp():
|
|
"""Test formatting seconds to MM:SS."""
|
|
assert format_timestamp(0.0) == "00:00"
|
|
assert format_timestamp(30.0) == "00:30"
|
|
assert format_timestamp(60.0) == "01:00"
|
|
assert format_timestamp(90.0) == "01:30"
|
|
assert format_timestamp(135.0) == "02:15"
|
|
|
|
|
|
def test_parse_guide_spec_first():
|
|
"""Test parsing 'first:' guide spec."""
|
|
guide = parse_guide_spec("first:$0", 0.0, 2.0)
|
|
assert guide is not None
|
|
assert guide.position == "first"
|
|
assert guide.image_ref == "$0"
|
|
assert guide.strength == 1.0
|
|
|
|
|
|
def test_parse_guide_spec_end():
|
|
"""Test parsing 'end:' guide spec."""
|
|
guide = parse_guide_spec("end:$1", 0.0, 2.0)
|
|
assert guide is not None
|
|
assert guide.position == "end"
|
|
assert guide.image_ref == "$1"
|
|
|
|
|
|
def test_parse_guide_spec_middle():
|
|
"""Test parsing 'middle:' guide spec."""
|
|
guide = parse_guide_spec("middle:$2", 0.0, 4.0)
|
|
assert guide is not None
|
|
assert guide.position == "middle"
|
|
assert guide.image_ref == "$2"
|
|
|
|
|
|
def test_parse_guide_spec_timestamp():
|
|
"""Test parsing timestamp guide spec."""
|
|
guide = parse_guide_spec("00:03:$3", 0.0, 6.0)
|
|
assert guide is not None
|
|
assert guide.position == "00" # First part before colon
|
|
assert "$3" in guide.image_ref or guide.image_ref == "03:$3"
|
|
|
|
|
|
def test_parse_guide_spec_with_strength():
|
|
"""Test parsing guide spec with strength modifier."""
|
|
guide = parse_guide_spec("first:$0 @ 0.8", 0.0, 2.0)
|
|
assert guide is not None
|
|
assert guide.position == "first"
|
|
assert guide.image_ref == "$0"
|
|
assert guide.strength == 0.8
|
|
|
|
|
|
def test_parse_audio_spec_silent():
|
|
"""Test parsing audio:silent."""
|
|
assert parse_audio_spec("audio:silent") == "silent"
|
|
assert parse_audio_spec("audio:SILENT") == "silent"
|
|
|
|
|
|
def test_parse_audio_spec_ambient():
|
|
"""Test parsing audio:ambient."""
|
|
assert parse_audio_spec("audio:ambient") == "ambient"
|
|
|
|
|
|
def test_parse_audio_spec_dialogue():
|
|
"""Test parsing audio with dialogue."""
|
|
assert parse_audio_spec('audio:"Hello world"') == "Hello world"
|
|
assert parse_audio_spec("audio:'Hello world'") == "Hello world"
|
|
|
|
|
|
def test_parse_scene_script_simple():
|
|
"""Test parsing a simple scene script."""
|
|
script = """
|
|
[00:00-00:02] A woman speaks | audio:silent | first:$0
|
|
[00:02-00:04] She smiles | audio:"Hello" | first:$1 | end:$2
|
|
"""
|
|
chunks = parse_scene_script(script)
|
|
|
|
assert len(chunks) == 2
|
|
|
|
# First chunk
|
|
assert chunks[0].start_sec == 0.0
|
|
assert chunks[0].end_sec == 2.0
|
|
assert "woman speaks" in chunks[0].prompt
|
|
assert chunks[0].audio_spec == "silent"
|
|
assert len(chunks[0].guides) == 1
|
|
|
|
# Second chunk
|
|
assert chunks[1].start_sec == 2.0
|
|
assert chunks[1].end_sec == 4.0
|
|
assert "smiles" in chunks[1].prompt
|
|
assert chunks[1].audio_spec == "Hello"
|
|
assert len(chunks[1].guides) == 2
|
|
|
|
|
|
def test_parse_scene_script_with_shot_headers():
|
|
"""Test parsing script with shot headers."""
|
|
script = """
|
|
# === SHOT 1: INTRO ===
|
|
|
|
[00:00-00:02] Opening scene | audio:silent | first:$0
|
|
|
|
# === SHOT 2: MAIN ===
|
|
|
|
[00:02-00:04] Main content | audio:"Dialogue" | first:$1
|
|
"""
|
|
chunks = parse_scene_script(script)
|
|
|
|
assert len(chunks) == 2
|
|
assert chunks[0].shot_name == "SHOT 1: INTRO"
|
|
assert chunks[1].shot_name == "SHOT 2: MAIN"
|
|
|
|
|
|
def test_scene_chunk_properties():
|
|
"""Test SceneChunk property methods."""
|
|
chunk = SceneChunk(
|
|
start_sec=0.0,
|
|
end_sec=3.0,
|
|
prompt="Test",
|
|
audio_spec="silent",
|
|
guides=[]
|
|
)
|
|
|
|
assert chunk.duration == 3.0
|
|
assert chunk.is_silent == True
|
|
assert chunk.is_ambient == False
|
|
assert chunk.dialogue is None
|
|
|
|
chunk2 = SceneChunk(
|
|
start_sec=0.0,
|
|
end_sec=2.0,
|
|
prompt="Test",
|
|
audio_spec="Hello world",
|
|
guides=[]
|
|
)
|
|
|
|
assert chunk2.is_silent == False
|
|
assert chunk2.dialogue == "Hello world"
|
|
|
|
|
|
def test_image_guide_get_position_seconds():
|
|
"""Test ImageGuide position to seconds conversion."""
|
|
guide_first = ImageGuide(position="first", image_ref="$0")
|
|
assert guide_first.get_position_seconds(2.0, 5.0) == 2.0
|
|
|
|
guide_end = ImageGuide(position="end", image_ref="$1")
|
|
assert guide_end.get_position_seconds(2.0, 5.0) == 5.0
|
|
|
|
guide_middle = ImageGuide(position="middle", image_ref="$2")
|
|
assert guide_middle.get_position_seconds(2.0, 6.0) == 4.0
|
|
|
|
|
|
def test_woman_speaking_example():
|
|
"""Test the full woman speaking example from requirements."""
|
|
script = """
|
|
# === SHOT 1: WOMAN INTRODUCTION (6 seconds total) ===
|
|
|
|
[00:00-00:02] Closeup of woman's face, soft lighting, neutral expression | audio:silent | first:$0 | end:$1
|
|
[00:02-00:04] Cowboy shot of woman speaking confidently, gesturing with hands | audio:"Hello, welcome to my channel" | first:$2 | end:$4
|
|
[00:04-00:06] Side profile view of woman nodding gently, soft smile | audio:silent | first:$5 | end:$6
|
|
"""
|
|
chunks = parse_scene_script(script)
|
|
|
|
assert len(chunks) == 3
|
|
|
|
# First chunk: closeup, silent
|
|
assert chunks[0].start_sec == 0.0
|
|
assert chunks[0].end_sec == 2.0
|
|
assert "Closeup" in chunks[0].prompt
|
|
assert chunks[0].is_silent
|
|
assert len(chunks[0].guides) == 2
|
|
|
|
# Second chunk: cowboy shot with dialogue
|
|
assert chunks[1].start_sec == 2.0
|
|
assert chunks[1].end_sec == 4.0
|
|
assert chunks[1].dialogue == "Hello, welcome to my channel"
|
|
assert not chunks[1].is_silent
|
|
|
|
# Third chunk: side profile, silent
|
|
assert chunks[2].start_sec == 4.0
|
|
assert chunks[2].end_sec == 6.0
|
|
assert chunks[2].is_silent
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test_parse_timestamp_mmss()
|
|
print("[PASS] test_parse_timestamp_mmss")
|
|
|
|
test_parse_timestamp_with_ms()
|
|
print("[PASS] test_parse_timestamp_with_ms")
|
|
|
|
test_parse_timestamp_seconds_only()
|
|
print("[PASS] test_parse_timestamp_seconds_only")
|
|
|
|
test_format_timestamp()
|
|
print("[PASS] test_format_timestamp")
|
|
|
|
test_parse_guide_spec_first()
|
|
print("[PASS] test_parse_guide_spec_first")
|
|
|
|
test_parse_guide_spec_end()
|
|
print("[PASS] test_parse_guide_spec_end")
|
|
|
|
test_parse_guide_spec_middle()
|
|
print("[PASS] test_parse_guide_spec_middle")
|
|
|
|
test_parse_guide_spec_with_strength()
|
|
print("[PASS] test_parse_guide_spec_with_strength")
|
|
|
|
test_parse_audio_spec_silent()
|
|
print("[PASS] test_parse_audio_spec_silent")
|
|
|
|
test_parse_audio_spec_ambient()
|
|
print("[PASS] test_parse_audio_spec_ambient")
|
|
|
|
test_parse_audio_spec_dialogue()
|
|
print("[PASS] test_parse_audio_spec_dialogue")
|
|
|
|
test_parse_scene_script_simple()
|
|
print("[PASS] test_parse_scene_script_simple")
|
|
|
|
test_parse_scene_script_with_shot_headers()
|
|
print("[PASS] test_parse_scene_script_with_shot_headers")
|
|
|
|
test_scene_chunk_properties()
|
|
print("[PASS] test_scene_chunk_properties")
|
|
|
|
test_image_guide_get_position_seconds()
|
|
print("[PASS] test_image_guide_get_position_seconds")
|
|
|
|
test_woman_speaking_example()
|
|
print("[PASS] test_woman_speaking_example")
|
|
|
|
print("\nAll tests passed!")
|