"""Subtitle 领域模型单测.""" import pytest from packages.domain.subtitle import SubtitleSegment, SubtitleTimeline, SubtitleWord class TestSubtitleWord: def test_duration_normal(self): word = SubtitleWord(text="你好", start=1.0, end=2.5) assert word.duration == pytest.approx(1.5) def test_duration_zero(self): word = SubtitleWord(text="啊", start=3.0, end=3.0) assert word.duration == 0.0 def test_duration_negative_returns_zero(self): word = SubtitleWord(text="test", start=5.0, end=3.0) assert word.duration == 0.0 class TestSubtitleSegment: def test_duration(self): seg = SubtitleSegment(text="大家好", start=0.0, end=3.0) assert seg.duration == pytest.approx(3.0) def test_char_count(self): seg = SubtitleSegment(text="今天天气真好", start=0.0, end=5.0) assert seg.char_count == 6 def test_empty_text(self): seg = SubtitleSegment(text="", start=0.0, end=1.0) assert seg.char_count == 0 def test_default_words_empty(self): seg = SubtitleSegment(text="test", start=0.0, end=1.0) assert seg.words == [] class TestSubtitleTimeline: def test_empty_timeline(self): tl = SubtitleTimeline() assert tl.segment_count == 0 assert tl.total_chars == 0 assert tl.language == "zh" assert tl.total_duration == 0.0 def test_segment_count(self): tl = SubtitleTimeline( segments=[ SubtitleSegment(text="第一段", start=0.0, end=2.0), SubtitleSegment(text="第二段", start=2.0, end=5.0), ] ) assert tl.segment_count == 2 assert tl.total_chars == 6 def test_total_chars(self): tl = SubtitleTimeline( segments=[ SubtitleSegment(text="abc", start=0, end=1), SubtitleSegment(text="defg", start=1, end=2), ] ) assert tl.total_chars == 7 class TestMergeShortSegments: def test_single_segment_no_change(self): tl = SubtitleTimeline( segments=[ SubtitleSegment(text="你好", start=0.0, end=1.0), ] ) result = tl.merge_short_segments(min_chars=8) assert result.segment_count == 1 assert result.segments[0].text == "你好" def test_empty_timeline(self): tl = SubtitleTimeline() result = tl.merge_short_segments() assert result.segment_count == 0 def test_merge_short_segments(self): tl = SubtitleTimeline( segments=[ SubtitleSegment(text="你好", start=0.0, end=1.0), SubtitleSegment(text="今天", start=1.0, end=2.0), SubtitleSegment(text="天气", start=2.0, end=3.0), SubtitleSegment(text="真好", start=3.0, end=4.0), ] ) result = tl.merge_short_segments(min_chars=4) # 每段2字,min=4,应该每2段合并 assert result.segment_count == 2 assert result.segments[0].text == "你好今天" assert result.segments[0].start == 0.0 assert result.segments[0].end == 2.0 assert result.segments[1].text == "天气真好" assert result.segments[1].start == 2.0 assert result.segments[1].end == 4.0 def test_remaining_merged_to_last(self): # 3段,每段2字,min=5 → 前5字合并,剩余1字并到最后 tl = SubtitleTimeline( segments=[ SubtitleSegment(text="一二", start=0, end=1), SubtitleSegment(text="三四", start=1, end=2), SubtitleSegment(text="五", start=2, end=3), ] ) result = tl.merge_short_segments(min_chars=5) assert result.segment_count == 1 assert result.segments[0].text == "一二三四五" def test_merge_with_words(self): tl = SubtitleTimeline( segments=[ SubtitleSegment( text="你好", start=0.0, end=1.0, words=[ SubtitleWord(text="你", start=0.0, end=0.5), SubtitleWord(text="好", start=0.5, end=1.0), ], ), SubtitleSegment( text="世界", start=1.0, end=2.0, words=[ SubtitleWord(text="世", start=1.0, end=1.5), SubtitleWord(text="界", start=1.5, end=2.0), ], ), ] ) result = tl.merge_short_segments(min_chars=10) assert result.segment_count == 1 assert len(result.segments[0].words) == 4 class TestSplitLongSegments: def test_short_segments_no_split(self): tl = SubtitleTimeline( segments=[ SubtitleSegment(text="短文本", start=0.0, end=1.0), ] ) result = tl.split_long_segments(max_chars=20) assert result.segment_count == 1 def test_split_by_punctuation(self): text = "今天天气真好。我们出去玩吧!" tl = SubtitleTimeline( segments=[ SubtitleSegment(text=text, start=0.0, end=5.0), ] ) result = tl.split_long_segments(max_chars=10) assert result.segment_count >= 2 # 合并起来应该等于原文 assert "".join(s.text for s in result.segments) == text def test_split_preserves_time_order(self): tl = SubtitleTimeline( segments=[ SubtitleSegment(text="一二三四五六七八九十。十一二三四五六七八九十。", start=0.0, end=10.0), ] ) result = tl.split_long_segments(max_chars=10) # 时间应该是递增的 for i in range(len(result.segments) - 1): assert result.segments[i].end <= result.segments[i + 1].start + 0.001 def test_empty_timeline(self): tl = SubtitleTimeline() result = tl.split_long_segments() assert result.segment_count == 0 class TestSplitTextByPunctuation: def test_no_punctuation_short(self): result = SubtitleTimeline._split_text_by_punctuation("你好世界", 20) assert len(result) == 1 assert result[0] == "你好世界" def test_sentence_end_punctuation_long_enough(self): # 每段超过 max_chars//2 才会在句末标点断开 text = "今天天气真的非常好。明天天气也不错。" result = SubtitleTimeline._split_text_by_punctuation(text, 10) assert len(result) >= 2 assert "".join(result) == text def test_short_text_with_punctuation_no_split(self): # 文本太短(< max_chars//2),即使有标点也不断开 result = SubtitleTimeline._split_text_by_punctuation("你好。世界。", 20) assert len(result) == 1 def test_long_text_hard_split(self): text = "一二三四五六七八九十十一二三四五六七八九十" result = SubtitleTimeline._split_text_by_punctuation(text, 10) assert len(result) >= 2 assert "".join(result) == text def test_empty_text(self): result = SubtitleTimeline._split_text_by_punctuation("", 10) assert result == [] class TestMergeSegments: def test_merge_two_segments(self): segs = [ SubtitleSegment(text="你好", start=0.0, end=1.0), SubtitleSegment(text="世界", start=1.0, end=2.0), ] result = SubtitleTimeline._merge_segments(segs) assert result.text == "你好世界" assert result.start == 0.0 assert result.end == 2.0 def test_merge_empty_list(self): result = SubtitleTimeline._merge_segments([]) assert result.text == "" assert result.start == 0 assert result.end == 0