OmniVoice-Studio / tests /test_srt_parser.py
Lê Phi Nam
Deploy to HF Space
94004a2
Raw
History Blame Contribute Delete
3.85 kB
"""Unit tests for the SRT parser used by /dub/import-srt."""
import sys
import os
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "backend"))
from services.srt_parser import parse_srt # noqa: E402
def test_parses_a_well_formed_srt_file():
srt = """1
00:00:01,000 --> 00:00:04,500
Hello world.
2
00:00:05,250 --> 00:00:08,000
Second line here.
"""
result = parse_srt(srt)
assert result.skipped_cues == 0
assert result.dropped_overlaps == 0
assert len(result.segments) == 2
assert result.segments[0]["start"] == 1.0
assert result.segments[0]["end"] == 4.5
assert result.segments[0]["text"] == "Hello world."
assert result.segments[1]["start"] == 5.25
assert result.segments[1]["text"] == "Second line here."
def test_joins_multi_line_cues_with_newline():
srt = """1
00:00:01,000 --> 00:00:04,000
Line one
Line two
"""
result = parse_srt(srt)
assert result.segments[0]["text"] == "Line one\nLine two"
def test_accepts_dot_as_milliseconds_separator():
# WebVTT-style timestamps inside an SRT file are common in the wild.
srt = """1
00:00:01.500 --> 00:00:03.250
Dotty.
"""
result = parse_srt(srt)
assert result.segments[0]["start"] == 1.5
assert result.segments[0]["end"] == 3.25
def test_handles_utf8_bom_at_start_of_file():
srt = "1\n00:00:01,000 --> 00:00:02,000\nBOM cue.\n"
result = parse_srt(srt)
assert len(result.segments) == 1
assert result.segments[0]["text"] == "BOM cue."
def test_handles_crlf_line_endings():
srt = "1\r\n00:00:01,000 --> 00:00:02,000\r\nWindows.\r\n\r\n"
result = parse_srt(srt)
assert len(result.segments) == 1
def test_skips_cue_with_non_positive_duration():
srt = """1
00:00:05,000 --> 00:00:05,000
Zero duration.
2
00:00:06,000 --> 00:00:05,500
End before start.
3
00:00:07,000 --> 00:00:08,000
Good cue.
"""
result = parse_srt(srt)
assert result.skipped_cues == 2
assert len(result.segments) == 1
assert result.segments[0]["text"] == "Good cue."
def test_skips_cue_with_only_whitespace_body():
srt = """1
00:00:01,000 --> 00:00:02,000
2
00:00:03,000 --> 00:00:04,000
Real text.
"""
result = parse_srt(srt)
assert result.skipped_cues == 1
assert len(result.segments) == 1
def test_shifts_overlapping_cue_to_keep_both():
# Cue 2 starts inside cue 1; we expect cue 2's start to be pushed
# forward to cue 1's end so both still play, in order.
srt = """1
00:00:01,000 --> 00:00:05,000
First.
2
00:00:03,000 --> 00:00:07,000
Second.
"""
result = parse_srt(srt)
assert len(result.segments) == 2
assert result.segments[1]["start"] == 5.0
assert result.segments[1]["end"] == 7.0
assert result.dropped_overlaps == 0
def test_drops_overlap_that_would_have_negative_duration():
srt = """1
00:00:01,000 --> 00:00:10,000
First.
2
00:00:03,000 --> 00:00:08,000
Second entirely inside first.
"""
result = parse_srt(srt)
assert len(result.segments) == 1
assert result.dropped_overlaps == 1
def test_parses_missing_index_numbers():
# No "1", "2" lines — just timings + text. This happens with some
# subtitle editors that strip indices.
srt = """00:00:01,000 --> 00:00:02,000
First.
00:00:03,000 --> 00:00:04,000
Second.
"""
result = parse_srt(srt)
assert len(result.segments) == 2
def test_returns_empty_result_for_empty_input():
assert parse_srt("").segments == []
assert parse_srt("").skipped_cues == 0
def test_segments_get_sequential_ids_and_required_fields():
srt = """1
00:00:01,000 --> 00:00:02,000
A
2
00:00:03,000 --> 00:00:04,000
B
"""
result = parse_srt(srt)
for i, seg in enumerate(result.segments):
assert seg["id"] == i
assert seg["text"] == seg["text_original"]
assert seg["speaker_id"] == "Speaker 1"