342 lines
9.7 KiB
Go
342 lines
9.7 KiB
Go
package sync
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"sub-cli/internal/model"
|
|
)
|
|
|
|
func TestSyncVTTTimeline(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
source model.Subtitle
|
|
target model.Subtitle
|
|
verify func(t *testing.T, result model.Subtitle)
|
|
}{
|
|
{
|
|
name: "Equal entry counts",
|
|
source: model.Subtitle{
|
|
Format: "vtt",
|
|
Title: "Source VTT",
|
|
Entries: []model.SubtitleEntry{
|
|
{
|
|
Index: 1,
|
|
StartTime: model.Timestamp{Seconds: 1},
|
|
EndTime: model.Timestamp{Seconds: 4},
|
|
Text: "Source line one.",
|
|
},
|
|
{
|
|
Index: 2,
|
|
StartTime: model.Timestamp{Seconds: 5},
|
|
EndTime: model.Timestamp{Seconds: 8},
|
|
Text: "Source line two.",
|
|
},
|
|
{
|
|
Index: 3,
|
|
StartTime: model.Timestamp{Seconds: 9},
|
|
EndTime: model.Timestamp{Seconds: 12},
|
|
Text: "Source line three.",
|
|
},
|
|
},
|
|
},
|
|
target: model.Subtitle{
|
|
Format: "vtt",
|
|
Title: "Target VTT",
|
|
Styles: map[string]string{
|
|
"style1": ".style1 { color: red; }",
|
|
},
|
|
Entries: []model.SubtitleEntry{
|
|
{
|
|
Index: 1,
|
|
StartTime: model.Timestamp{Minutes: 1, Seconds: 0},
|
|
EndTime: model.Timestamp{Minutes: 1, Seconds: 3},
|
|
Text: "Target line one.",
|
|
Styles: map[string]string{
|
|
"align": "start",
|
|
"position": "10%",
|
|
},
|
|
},
|
|
{
|
|
Index: 2,
|
|
StartTime: model.Timestamp{Minutes: 1, Seconds: 5},
|
|
EndTime: model.Timestamp{Minutes: 1, Seconds: 8},
|
|
Text: "Target line two.",
|
|
Styles: map[string]string{
|
|
"align": "middle",
|
|
},
|
|
},
|
|
{
|
|
Index: 3,
|
|
StartTime: model.Timestamp{Minutes: 1, Seconds: 10},
|
|
EndTime: model.Timestamp{Minutes: 1, Seconds: 13},
|
|
Text: "Target line three.",
|
|
},
|
|
},
|
|
},
|
|
verify: func(t *testing.T, result model.Subtitle) {
|
|
if len(result.Entries) != 3 {
|
|
t.Errorf("Expected 3 entries, got %d", len(result.Entries))
|
|
return
|
|
}
|
|
|
|
// Check that source timings are applied to target entries
|
|
if result.Entries[0].StartTime.Seconds != 1 || result.Entries[0].EndTime.Seconds != 4 {
|
|
t.Errorf("First entry timing mismatch: got %+v", result.Entries[0])
|
|
}
|
|
|
|
if result.Entries[1].StartTime.Seconds != 5 || result.Entries[1].EndTime.Seconds != 8 {
|
|
t.Errorf("Second entry timing mismatch: got %+v", result.Entries[1])
|
|
}
|
|
|
|
if result.Entries[2].StartTime.Seconds != 9 || result.Entries[2].EndTime.Seconds != 12 {
|
|
t.Errorf("Third entry timing mismatch: got %+v", result.Entries[2])
|
|
}
|
|
|
|
// Check that target content is preserved
|
|
if result.Entries[0].Text != "Target line one." {
|
|
t.Errorf("Content should be preserved, got: %s", result.Entries[0].Text)
|
|
}
|
|
|
|
// Check that styles are preserved
|
|
if result.Entries[0].Styles["align"] != "start" || result.Entries[0].Styles["position"] != "10%" {
|
|
t.Errorf("Styles should be preserved, got: %+v", result.Entries[0].Styles)
|
|
}
|
|
|
|
// Check that global styles are preserved
|
|
if result.Styles["style1"] != ".style1 { color: red; }" {
|
|
t.Errorf("Global styles should be preserved, got: %+v", result.Styles)
|
|
}
|
|
|
|
// Check that numbering is correct
|
|
if result.Entries[0].Index != 1 || result.Entries[1].Index != 2 || result.Entries[2].Index != 3 {
|
|
t.Errorf("Entry indices should be sequential: %+v", result.Entries)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "More target entries than source",
|
|
source: model.Subtitle{
|
|
Format: "vtt",
|
|
Entries: []model.SubtitleEntry{
|
|
{
|
|
Index: 1,
|
|
StartTime: model.Timestamp{Seconds: 1},
|
|
EndTime: model.Timestamp{Seconds: 4},
|
|
Text: "Source line one.",
|
|
},
|
|
{
|
|
Index: 2,
|
|
StartTime: model.Timestamp{Seconds: 5},
|
|
EndTime: model.Timestamp{Seconds: 8},
|
|
Text: "Source line two.",
|
|
},
|
|
},
|
|
},
|
|
target: model.Subtitle{
|
|
Format: "vtt",
|
|
Title: "Target VTT",
|
|
Entries: []model.SubtitleEntry{
|
|
{
|
|
Index: 1,
|
|
StartTime: model.Timestamp{Minutes: 1, Seconds: 0},
|
|
EndTime: model.Timestamp{Minutes: 1, Seconds: 3},
|
|
Text: "Target line one.",
|
|
},
|
|
{
|
|
Index: 2,
|
|
StartTime: model.Timestamp{Minutes: 1, Seconds: 5},
|
|
EndTime: model.Timestamp{Minutes: 1, Seconds: 8},
|
|
Text: "Target line two.",
|
|
},
|
|
{
|
|
Index: 3,
|
|
StartTime: model.Timestamp{Minutes: 1, Seconds: 10},
|
|
EndTime: model.Timestamp{Minutes: 1, Seconds: 13},
|
|
Text: "Target line three.",
|
|
},
|
|
},
|
|
},
|
|
verify: func(t *testing.T, result model.Subtitle) {
|
|
if len(result.Entries) != 3 {
|
|
t.Errorf("Expected 3 entries, got %d", len(result.Entries))
|
|
return
|
|
}
|
|
|
|
// First entry should use first source timing
|
|
if result.Entries[0].StartTime.Seconds != 1 {
|
|
t.Errorf("First entry should have first source timing, got: %+v", result.Entries[0].StartTime)
|
|
}
|
|
|
|
// Last entry should use last source timing
|
|
if result.Entries[2].StartTime.Seconds != 5 {
|
|
t.Errorf("Last entry should have last source timing, got: %+v", result.Entries[2].StartTime)
|
|
}
|
|
|
|
// Check that target content is preserved
|
|
if result.Entries[2].Text != "Target line three." {
|
|
t.Errorf("Content should be preserved, got: %s", result.Entries[2].Text)
|
|
}
|
|
|
|
// Check that title is preserved
|
|
if result.Title != "Target VTT" {
|
|
t.Errorf("Title should be preserved, got: %s", result.Title)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "More source entries than target",
|
|
source: model.Subtitle{
|
|
Format: "vtt",
|
|
Entries: []model.SubtitleEntry{
|
|
{
|
|
Index: 1,
|
|
StartTime: model.Timestamp{Seconds: 1},
|
|
EndTime: model.Timestamp{Seconds: 3},
|
|
Text: "Source line one.",
|
|
},
|
|
{
|
|
Index: 2,
|
|
StartTime: model.Timestamp{Seconds: 4},
|
|
EndTime: model.Timestamp{Seconds: 6},
|
|
Text: "Source line two.",
|
|
},
|
|
{
|
|
Index: 3,
|
|
StartTime: model.Timestamp{Seconds: 7},
|
|
EndTime: model.Timestamp{Seconds: 9},
|
|
Text: "Source line three.",
|
|
},
|
|
{
|
|
Index: 4,
|
|
StartTime: model.Timestamp{Seconds: 10},
|
|
EndTime: model.Timestamp{Seconds: 12},
|
|
Text: "Source line four.",
|
|
},
|
|
},
|
|
},
|
|
target: model.Subtitle{
|
|
Format: "vtt",
|
|
Metadata: map[string]string{
|
|
"Region": "metadata region",
|
|
},
|
|
Entries: []model.SubtitleEntry{
|
|
{
|
|
Index: 1,
|
|
StartTime: model.Timestamp{Minutes: 1, Seconds: 0},
|
|
EndTime: model.Timestamp{Minutes: 1, Seconds: 3},
|
|
Text: "Target line one.",
|
|
},
|
|
{
|
|
Index: 2,
|
|
StartTime: model.Timestamp{Minutes: 1, Seconds: 5},
|
|
EndTime: model.Timestamp{Minutes: 1, Seconds: 8},
|
|
Text: "Target line two.",
|
|
},
|
|
},
|
|
},
|
|
verify: func(t *testing.T, result model.Subtitle) {
|
|
if len(result.Entries) != 2 {
|
|
t.Errorf("Expected 2 entries, got %d", len(result.Entries))
|
|
return
|
|
}
|
|
|
|
// First entry should have first source timing
|
|
if result.Entries[0].StartTime.Seconds != 1 {
|
|
t.Errorf("First entry should have first source timing, got: %+v", result.Entries[0].StartTime)
|
|
}
|
|
|
|
// Last entry should have last source timing
|
|
if result.Entries[1].StartTime.Seconds != 10 {
|
|
t.Errorf("Last entry should have last source timing, got: %+v", result.Entries[1].StartTime)
|
|
}
|
|
|
|
// Check that metadata is preserved
|
|
if result.Metadata["Region"] != "metadata region" {
|
|
t.Errorf("Metadata should be preserved, got: %+v", result.Metadata)
|
|
}
|
|
|
|
// Check that target content is preserved
|
|
if result.Entries[0].Text != "Target line one." || result.Entries[1].Text != "Target line two." {
|
|
t.Errorf("Content should be preserved, got: %+v", result.Entries)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "Empty target entries",
|
|
source: model.Subtitle{
|
|
Format: "vtt",
|
|
Entries: []model.SubtitleEntry{
|
|
{
|
|
Index: 1,
|
|
StartTime: model.Timestamp{Seconds: 1},
|
|
EndTime: model.Timestamp{Seconds: 4},
|
|
Text: "Source line one.",
|
|
},
|
|
},
|
|
},
|
|
target: model.Subtitle{
|
|
Format: "vtt",
|
|
Title: "Empty Target",
|
|
},
|
|
verify: func(t *testing.T, result model.Subtitle) {
|
|
if len(result.Entries) != 0 {
|
|
t.Errorf("Expected 0 entries, got %d", len(result.Entries))
|
|
}
|
|
|
|
// Title should be preserved
|
|
if result.Title != "Empty Target" {
|
|
t.Errorf("Title should be preserved, got: %s", result.Title)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "Empty source entries",
|
|
source: model.Subtitle{
|
|
Format: "vtt",
|
|
},
|
|
target: model.Subtitle{
|
|
Format: "vtt",
|
|
Title: "Target with content",
|
|
Entries: []model.SubtitleEntry{
|
|
{
|
|
Index: 1,
|
|
StartTime: model.Timestamp{Seconds: 10},
|
|
EndTime: model.Timestamp{Seconds: 15},
|
|
Text: "Target line one.",
|
|
},
|
|
},
|
|
},
|
|
verify: func(t *testing.T, result model.Subtitle) {
|
|
if len(result.Entries) != 1 {
|
|
t.Errorf("Expected 1 entry, got %d", len(result.Entries))
|
|
return
|
|
}
|
|
|
|
// Timing should be preserved since source is empty
|
|
if result.Entries[0].StartTime.Seconds != 10 || result.Entries[0].EndTime.Seconds != 15 {
|
|
t.Errorf("Timing should match target when source is empty, got: %+v", result.Entries[0])
|
|
}
|
|
|
|
// Content should be preserved
|
|
if result.Entries[0].Text != "Target line one." {
|
|
t.Errorf("Content should be preserved, got: %s", result.Entries[0].Text)
|
|
}
|
|
|
|
// Title should be preserved
|
|
if result.Title != "Target with content" {
|
|
t.Errorf("Title should be preserved, got: %s", result.Title)
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := syncVTTTimeline(tc.source, tc.target)
|
|
|
|
if tc.verify != nil {
|
|
tc.verify(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|