chore: seperate large files
This commit is contained in:
parent
ebbf516689
commit
76e1298ded
44 changed files with 5745 additions and 4173 deletions
255
internal/format/srt/converter_test.go
Normal file
255
internal/format/srt/converter_test.go
Normal file
|
@ -0,0 +1,255 @@
|
|||
package srt
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sub-cli/internal/model"
|
||||
)
|
||||
|
||||
func TestConvertToSubtitle(t *testing.T) {
|
||||
// Create a temporary test file
|
||||
content := `1
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
This is the first line.
|
||||
|
||||
2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
This is the second line.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.srt")
|
||||
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create test file: %v", err)
|
||||
}
|
||||
|
||||
// Convert to subtitle
|
||||
subtitle, err := ConvertToSubtitle(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertToSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Check result
|
||||
if subtitle.Format != "srt" {
|
||||
t.Errorf("Expected format 'srt', got '%s'", subtitle.Format)
|
||||
}
|
||||
|
||||
if len(subtitle.Entries) != 2 {
|
||||
t.Errorf("Expected 2 entries, got %d", len(subtitle.Entries))
|
||||
}
|
||||
|
||||
// Check first entry
|
||||
if subtitle.Entries[0].Index != 1 {
|
||||
t.Errorf("First entry index: expected 1, got %d", subtitle.Entries[0].Index)
|
||||
}
|
||||
if subtitle.Entries[0].Text != "This is the first line." {
|
||||
t.Errorf("First entry text: expected 'This is the first line.', got '%s'", subtitle.Entries[0].Text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFromSubtitle(t *testing.T) {
|
||||
// Create a subtitle
|
||||
subtitle := model.NewSubtitle()
|
||||
subtitle.Format = "srt"
|
||||
|
||||
entry1 := model.NewSubtitleEntry()
|
||||
entry1.Index = 1
|
||||
entry1.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0}
|
||||
entry1.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0}
|
||||
entry1.Text = "This is the first line."
|
||||
|
||||
entry2 := model.NewSubtitleEntry()
|
||||
entry2.Index = 2
|
||||
entry2.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0}
|
||||
entry2.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 8, Milliseconds: 0}
|
||||
entry2.Text = "This is the second line."
|
||||
|
||||
subtitle.Entries = append(subtitle.Entries, entry1, entry2)
|
||||
|
||||
// Convert to SRT
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "output.srt")
|
||||
err := ConvertFromSubtitle(subtitle, outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertFromSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify by reading the file directly
|
||||
content, err := os.ReadFile(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read output file: %v", err)
|
||||
}
|
||||
|
||||
// Check content
|
||||
lines := strings.Split(string(content), "\n")
|
||||
if len(lines) < 7 {
|
||||
t.Fatalf("Expected at least 7 lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
// Check that the SRT entries were created correctly
|
||||
if lines[0] != "1" {
|
||||
t.Errorf("Expected first entry number to be '1', got '%s'", lines[0])
|
||||
}
|
||||
if !strings.Contains(lines[1], "00:00:01,000 --> 00:00:04,000") {
|
||||
t.Errorf("Expected first entry time range to match, got '%s'", lines[1])
|
||||
}
|
||||
if lines[2] != "This is the first line." {
|
||||
t.Errorf("Expected first entry content to match, got '%s'", lines[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSubtitle_WithHTMLTags(t *testing.T) {
|
||||
// Create a temporary test file with HTML styling tags
|
||||
content := `1
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
<i>This is italic.</i>
|
||||
|
||||
2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
<b>This is bold.</b>
|
||||
|
||||
3
|
||||
00:00:09,000 --> 00:00:12,000
|
||||
<u>This is underlined.</u>
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "styled.srt")
|
||||
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create test file: %v", err)
|
||||
}
|
||||
|
||||
// Convert to subtitle
|
||||
subtitle, err := ConvertToSubtitle(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertToSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Check style detection
|
||||
if value, ok := subtitle.Entries[0].Styles["italic"]; !ok || value != "true" {
|
||||
t.Errorf("Expected Styles to contain italic=true for entry with <i> tag")
|
||||
}
|
||||
|
||||
if value, ok := subtitle.Entries[1].Styles["bold"]; !ok || value != "true" {
|
||||
t.Errorf("Expected Styles to contain bold=true for entry with <b> tag")
|
||||
}
|
||||
|
||||
if value, ok := subtitle.Entries[2].Styles["underline"]; !ok || value != "true" {
|
||||
t.Errorf("Expected Styles to contain underline=true for entry with <u> tag")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSubtitle_FileError(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
_, err := ConvertToSubtitle("/nonexistent/file.srt")
|
||||
if err == nil {
|
||||
t.Error("Expected error when converting non-existent file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFromSubtitle_WithStyling(t *testing.T) {
|
||||
// Create a subtitle with style attributes
|
||||
subtitle := model.NewSubtitle()
|
||||
subtitle.Format = "srt"
|
||||
|
||||
// Create an entry with italics
|
||||
entry1 := model.NewSubtitleEntry()
|
||||
entry1.Index = 1
|
||||
entry1.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0}
|
||||
entry1.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0}
|
||||
entry1.Text = "This should be italic."
|
||||
entry1.Styles["italic"] = "true"
|
||||
|
||||
// Create an entry with bold
|
||||
entry2 := model.NewSubtitleEntry()
|
||||
entry2.Index = 2
|
||||
entry2.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0}
|
||||
entry2.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 8, Milliseconds: 0}
|
||||
entry2.Text = "This should be bold."
|
||||
entry2.Styles["bold"] = "true"
|
||||
|
||||
// Create an entry with underline
|
||||
entry3 := model.NewSubtitleEntry()
|
||||
entry3.Index = 3
|
||||
entry3.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 9, Milliseconds: 0}
|
||||
entry3.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 12, Milliseconds: 0}
|
||||
entry3.Text = "This should be underlined."
|
||||
entry3.Styles["underline"] = "true"
|
||||
|
||||
subtitle.Entries = append(subtitle.Entries, entry1, entry2, entry3)
|
||||
|
||||
// Convert from subtitle to SRT
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "styled.srt")
|
||||
err := ConvertFromSubtitle(subtitle, outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertFromSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify by reading the file directly
|
||||
content, err := os.ReadFile(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read output file: %v", err)
|
||||
}
|
||||
|
||||
// Check that HTML tags were applied
|
||||
contentStr := string(content)
|
||||
if !strings.Contains(contentStr, "<i>This should be italic.</i>") {
|
||||
t.Errorf("Expected italic HTML tags to be applied")
|
||||
}
|
||||
if !strings.Contains(contentStr, "<b>This should be bold.</b>") {
|
||||
t.Errorf("Expected bold HTML tags to be applied")
|
||||
}
|
||||
if !strings.Contains(contentStr, "<u>This should be underlined.</u>") {
|
||||
t.Errorf("Expected underline HTML tags to be applied")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFromSubtitle_FileError(t *testing.T) {
|
||||
// Create simple subtitle
|
||||
subtitle := model.NewSubtitle()
|
||||
subtitle.Entries = append(subtitle.Entries, model.NewSubtitleEntry())
|
||||
|
||||
// Test with invalid path
|
||||
err := ConvertFromSubtitle(subtitle, "/nonexistent/directory/file.srt")
|
||||
if err == nil {
|
||||
t.Error("Expected error when converting to invalid path, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFromSubtitle_WithExistingHTMLTags(t *testing.T) {
|
||||
// Create a subtitle with text that already contains HTML tags
|
||||
subtitle := model.NewSubtitle()
|
||||
subtitle.Format = "srt"
|
||||
|
||||
// Create an entry with existing italic tags but also style attribute
|
||||
entry := model.NewSubtitleEntry()
|
||||
entry.Index = 1
|
||||
entry.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0}
|
||||
entry.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0}
|
||||
entry.Text = "<i>Already italic text.</i>"
|
||||
entry.Styles["italic"] = "true" // Should not double-wrap with <i> tags
|
||||
|
||||
subtitle.Entries = append(subtitle.Entries, entry)
|
||||
|
||||
// Convert from subtitle to SRT
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "existing_tags.srt")
|
||||
err := ConvertFromSubtitle(subtitle, outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertFromSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify by reading the file directly
|
||||
content, err := os.ReadFile(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read output file: %v", err)
|
||||
}
|
||||
|
||||
// Should not have double tags
|
||||
contentStr := string(content)
|
||||
if strings.Contains(contentStr, "<i><i>") {
|
||||
t.Errorf("Expected no duplicate italic tags, but found them")
|
||||
}
|
||||
}
|
70
internal/format/srt/formatter_test.go
Normal file
70
internal/format/srt/formatter_test.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package srt
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
// Create a temporary test file with out-of-order numbers
|
||||
content := `2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
This is the second line.
|
||||
|
||||
1
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
This is the first line.
|
||||
|
||||
3
|
||||
00:00:09,500 --> 00:00:12,800
|
||||
This is the third line.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.srt")
|
||||
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create test file: %v", err)
|
||||
}
|
||||
|
||||
// Format the file
|
||||
err := Format(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Format failed: %v", err)
|
||||
}
|
||||
|
||||
// Read the formatted file
|
||||
formatted, err := os.ReadFile(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read formatted file: %v", err)
|
||||
}
|
||||
|
||||
// The Format function should standardize the numbering
|
||||
lines := strings.Split(string(formatted), "\n")
|
||||
|
||||
// The numbers should be sequential starting from 1
|
||||
if !strings.HasPrefix(lines[0], "1") {
|
||||
t.Errorf("First entry should be renumbered to 1, got '%s'", lines[0])
|
||||
}
|
||||
|
||||
// Find the second entry (after the first entry's content and a blank line)
|
||||
var secondEntryIndex int
|
||||
for i := 1; i < len(lines); i++ {
|
||||
if lines[i] == "" && i+1 < len(lines) && lines[i+1] != "" {
|
||||
secondEntryIndex = i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if secondEntryIndex > 0 && !strings.HasPrefix(lines[secondEntryIndex], "2") {
|
||||
t.Errorf("Second entry should be renumbered to 2, got '%s'", lines[secondEntryIndex])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormat_FileError(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
err := Format("/nonexistent/file.srt")
|
||||
if err == nil {
|
||||
t.Error("Expected error when formatting non-existent file, got nil")
|
||||
}
|
||||
}
|
84
internal/format/srt/generator_test.go
Normal file
84
internal/format/srt/generator_test.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package srt
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sub-cli/internal/model"
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
// Create test entries
|
||||
entries := []model.SRTEntry{
|
||||
{
|
||||
Number: 1,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0},
|
||||
Content: "This is the first line.",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 8, Milliseconds: 0},
|
||||
Content: "This is the second line.",
|
||||
},
|
||||
}
|
||||
|
||||
// Generate SRT file
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "output.srt")
|
||||
err := Generate(entries, outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify generated content
|
||||
content, err := os.ReadFile(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read output file: %v", err)
|
||||
}
|
||||
|
||||
// Check content
|
||||
lines := strings.Split(string(content), "\n")
|
||||
if len(lines) < 6 {
|
||||
t.Fatalf("Expected at least 6 lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
if lines[0] != "1" {
|
||||
t.Errorf("Expected first line to be '1', got '%s'", lines[0])
|
||||
}
|
||||
|
||||
if lines[1] != "00:00:01,000 --> 00:00:04,000" {
|
||||
t.Errorf("Expected second line to be time range, got '%s'", lines[1])
|
||||
}
|
||||
|
||||
if lines[2] != "This is the first line." {
|
||||
t.Errorf("Expected third line to be content, got '%s'", lines[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate_FileError(t *testing.T) {
|
||||
// Test with invalid path
|
||||
entries := []model.SRTEntry{
|
||||
{
|
||||
Number: 1,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 0, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 0, Milliseconds: 0},
|
||||
Content: "Test",
|
||||
},
|
||||
}
|
||||
|
||||
err := Generate(entries, "/nonexistent/directory/file.srt")
|
||||
if err == nil {
|
||||
t.Error("Expected error when generating to invalid path, got nil")
|
||||
}
|
||||
|
||||
// Test with directory as file
|
||||
tempDir := t.TempDir()
|
||||
err = Generate(entries, tempDir)
|
||||
if err == nil {
|
||||
t.Error("Expected error when generating to a directory, got nil")
|
||||
}
|
||||
}
|
58
internal/format/srt/lyrics_test.go
Normal file
58
internal/format/srt/lyrics_test.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package srt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"sub-cli/internal/model"
|
||||
)
|
||||
|
||||
func TestConvertToLyrics(t *testing.T) {
|
||||
// Create test entries
|
||||
entries := []model.SRTEntry{
|
||||
{
|
||||
Number: 1,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0},
|
||||
Content: "This is the first line.",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 8, Milliseconds: 0},
|
||||
Content: "This is the second line.",
|
||||
},
|
||||
{
|
||||
Number: 3,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 9, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 12, Milliseconds: 0},
|
||||
Content: "This is the third line.",
|
||||
},
|
||||
}
|
||||
|
||||
// Convert to Lyrics
|
||||
lyrics := ConvertToLyrics(entries)
|
||||
|
||||
// Check result
|
||||
if len(lyrics.Timeline) != 3 {
|
||||
t.Errorf("Expected 3 timeline entries, got %d", len(lyrics.Timeline))
|
||||
}
|
||||
if len(lyrics.Content) != 3 {
|
||||
t.Errorf("Expected 3 content entries, got %d", len(lyrics.Content))
|
||||
}
|
||||
|
||||
// Check first entry
|
||||
if lyrics.Timeline[0].Hours != 0 || lyrics.Timeline[0].Minutes != 0 ||
|
||||
lyrics.Timeline[0].Seconds != 1 || lyrics.Timeline[0].Milliseconds != 0 {
|
||||
t.Errorf("First timeline: expected 00:00:01,000, got %+v", lyrics.Timeline[0])
|
||||
}
|
||||
if lyrics.Content[0] != "This is the first line." {
|
||||
t.Errorf("First content: expected 'This is the first line.', got '%s'", lyrics.Content[0])
|
||||
}
|
||||
|
||||
// Check with empty entries
|
||||
emptyLyrics := ConvertToLyrics([]model.SRTEntry{})
|
||||
if len(emptyLyrics.Timeline) != 0 || len(emptyLyrics.Content) != 0 {
|
||||
t.Errorf("Expected empty lyrics for empty entries, got %d timeline and %d content",
|
||||
len(emptyLyrics.Timeline), len(emptyLyrics.Content))
|
||||
}
|
||||
}
|
159
internal/format/srt/parser_test.go
Normal file
159
internal/format/srt/parser_test.go
Normal file
|
@ -0,0 +1,159 @@
|
|||
package srt
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
// Create a temporary test file
|
||||
content := `1
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
This is the first line.
|
||||
|
||||
2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
This is the second line.
|
||||
|
||||
3
|
||||
00:00:09,500 --> 00:00:12,800
|
||||
This is the third line
|
||||
with a line break.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.srt")
|
||||
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create test file: %v", err)
|
||||
}
|
||||
|
||||
// Test parsing
|
||||
entries, err := Parse(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify results
|
||||
if len(entries) != 3 {
|
||||
t.Errorf("Expected 3 entries, got %d", len(entries))
|
||||
}
|
||||
|
||||
// Check first entry
|
||||
if entries[0].Number != 1 {
|
||||
t.Errorf("First entry number: expected 1, got %d", entries[0].Number)
|
||||
}
|
||||
if entries[0].StartTime.Hours != 0 || entries[0].StartTime.Minutes != 0 ||
|
||||
entries[0].StartTime.Seconds != 1 || entries[0].StartTime.Milliseconds != 0 {
|
||||
t.Errorf("First entry start time: expected 00:00:01,000, got %+v", entries[0].StartTime)
|
||||
}
|
||||
if entries[0].EndTime.Hours != 0 || entries[0].EndTime.Minutes != 0 ||
|
||||
entries[0].EndTime.Seconds != 4 || entries[0].EndTime.Milliseconds != 0 {
|
||||
t.Errorf("First entry end time: expected 00:00:04,000, got %+v", entries[0].EndTime)
|
||||
}
|
||||
if entries[0].Content != "This is the first line." {
|
||||
t.Errorf("First entry content: expected 'This is the first line.', got '%s'", entries[0].Content)
|
||||
}
|
||||
|
||||
// Check third entry
|
||||
if entries[2].Number != 3 {
|
||||
t.Errorf("Third entry number: expected 3, got %d", entries[2].Number)
|
||||
}
|
||||
expectedContent := "This is the third line\nwith a line break."
|
||||
if entries[2].Content != expectedContent {
|
||||
t.Errorf("Third entry content: expected '%s', got '%s'", expectedContent, entries[2].Content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_EdgeCases(t *testing.T) {
|
||||
// Test with empty file
|
||||
tempDir := t.TempDir()
|
||||
emptyFile := filepath.Join(tempDir, "empty.srt")
|
||||
if err := os.WriteFile(emptyFile, []byte(""), 0644); err != nil {
|
||||
t.Fatalf("Failed to create empty file: %v", err)
|
||||
}
|
||||
|
||||
entries, err := Parse(emptyFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed with empty file: %v", err)
|
||||
}
|
||||
if len(entries) != 0 {
|
||||
t.Errorf("Expected 0 entries for empty file, got %d", len(entries))
|
||||
}
|
||||
|
||||
// Test with malformed timestamp
|
||||
malformedContent := `1
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
First entry.
|
||||
|
||||
2
|
||||
bad timestamp format
|
||||
Second entry.
|
||||
`
|
||||
malformedFile := filepath.Join(tempDir, "malformed.srt")
|
||||
if err := os.WriteFile(malformedFile, []byte(malformedContent), 0644); err != nil {
|
||||
t.Fatalf("Failed to create malformed file: %v", err)
|
||||
}
|
||||
|
||||
entries, err = Parse(malformedFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed with malformed file: %v", err)
|
||||
}
|
||||
// Should still parse the first entry correctly
|
||||
if len(entries) != 1 {
|
||||
t.Errorf("Expected 1 entry for malformed file, got %d", len(entries))
|
||||
}
|
||||
|
||||
// Test with missing numbers
|
||||
missingNumContent := `00:00:01,000 --> 00:00:04,000
|
||||
First entry without number.
|
||||
|
||||
2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
Second entry with number.
|
||||
`
|
||||
missingNumFile := filepath.Join(tempDir, "missing_num.srt")
|
||||
if err := os.WriteFile(missingNumFile, []byte(missingNumContent), 0644); err != nil {
|
||||
t.Fatalf("Failed to create missing num file: %v", err)
|
||||
}
|
||||
|
||||
entries, err = Parse(missingNumFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed with missing num file: %v", err)
|
||||
}
|
||||
// Parsing behavior may vary, but it should not crash
|
||||
// In this case, it will typically parse just the second entry
|
||||
|
||||
// Test with extra empty lines
|
||||
extraLineContent := `1
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
First entry with extra spaces.
|
||||
|
||||
2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
Second entry with extra spaces.
|
||||
`
|
||||
extraLineFile := filepath.Join(tempDir, "extra_lines.srt")
|
||||
if err := os.WriteFile(extraLineFile, []byte(extraLineContent), 0644); err != nil {
|
||||
t.Fatalf("Failed to create extra lines file: %v", err)
|
||||
}
|
||||
|
||||
entries, err = Parse(extraLineFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed with extra lines file: %v", err)
|
||||
}
|
||||
if len(entries) != 2 {
|
||||
t.Errorf("Expected 2 entries for extra lines file, got %d", len(entries))
|
||||
}
|
||||
// Check content was trimmed correctly
|
||||
if entries[0].Content != "First entry with extra spaces." {
|
||||
t.Errorf("Expected trimmed content, got '%s'", entries[0].Content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_FileError(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
_, err := Parse("/nonexistent/file.srt")
|
||||
if err == nil {
|
||||
t.Error("Expected error when parsing non-existent file, got nil")
|
||||
}
|
||||
}
|
|
@ -1,646 +0,0 @@
|
|||
package srt
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sub-cli/internal/model"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
// Create a temporary test file
|
||||
content := `1
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
This is the first line.
|
||||
|
||||
2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
This is the second line.
|
||||
|
||||
3
|
||||
00:00:09,500 --> 00:00:12,800
|
||||
This is the third line
|
||||
with a line break.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.srt")
|
||||
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create test file: %v", err)
|
||||
}
|
||||
|
||||
// Test parsing
|
||||
entries, err := Parse(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify results
|
||||
if len(entries) != 3 {
|
||||
t.Errorf("Expected 3 entries, got %d", len(entries))
|
||||
}
|
||||
|
||||
// Check first entry
|
||||
if entries[0].Number != 1 {
|
||||
t.Errorf("First entry number: expected 1, got %d", entries[0].Number)
|
||||
}
|
||||
if entries[0].StartTime.Hours != 0 || entries[0].StartTime.Minutes != 0 ||
|
||||
entries[0].StartTime.Seconds != 1 || entries[0].StartTime.Milliseconds != 0 {
|
||||
t.Errorf("First entry start time: expected 00:00:01,000, got %+v", entries[0].StartTime)
|
||||
}
|
||||
if entries[0].EndTime.Hours != 0 || entries[0].EndTime.Minutes != 0 ||
|
||||
entries[0].EndTime.Seconds != 4 || entries[0].EndTime.Milliseconds != 0 {
|
||||
t.Errorf("First entry end time: expected 00:00:04,000, got %+v", entries[0].EndTime)
|
||||
}
|
||||
if entries[0].Content != "This is the first line." {
|
||||
t.Errorf("First entry content: expected 'This is the first line.', got '%s'", entries[0].Content)
|
||||
}
|
||||
|
||||
// Check third entry
|
||||
if entries[2].Number != 3 {
|
||||
t.Errorf("Third entry number: expected 3, got %d", entries[2].Number)
|
||||
}
|
||||
expectedContent := "This is the third line\nwith a line break."
|
||||
if entries[2].Content != expectedContent {
|
||||
t.Errorf("Third entry content: expected '%s', got '%s'", expectedContent, entries[2].Content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
// Create test entries
|
||||
entries := []model.SRTEntry{
|
||||
{
|
||||
Number: 1,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0},
|
||||
Content: "This is the first line.",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 8, Milliseconds: 0},
|
||||
Content: "This is the second line.",
|
||||
},
|
||||
}
|
||||
|
||||
// Generate SRT file
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "output.srt")
|
||||
err := Generate(entries, outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify generated content
|
||||
content, err := os.ReadFile(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read output file: %v", err)
|
||||
}
|
||||
|
||||
// Check content
|
||||
lines := strings.Split(string(content), "\n")
|
||||
if len(lines) < 6 {
|
||||
t.Fatalf("Expected at least 6 lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
if lines[0] != "1" {
|
||||
t.Errorf("Expected first line to be '1', got '%s'", lines[0])
|
||||
}
|
||||
|
||||
if lines[1] != "00:00:01,000 --> 00:00:04,000" {
|
||||
t.Errorf("Expected second line to be time range, got '%s'", lines[1])
|
||||
}
|
||||
|
||||
if lines[2] != "This is the first line." {
|
||||
t.Errorf("Expected third line to be content, got '%s'", lines[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSubtitle(t *testing.T) {
|
||||
// Create a temporary test file
|
||||
content := `1
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
This is the first line.
|
||||
|
||||
2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
This is the second line.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.srt")
|
||||
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create test file: %v", err)
|
||||
}
|
||||
|
||||
// Convert to subtitle
|
||||
subtitle, err := ConvertToSubtitle(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertToSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Check result
|
||||
if subtitle.Format != "srt" {
|
||||
t.Errorf("Expected format 'srt', got '%s'", subtitle.Format)
|
||||
}
|
||||
|
||||
if len(subtitle.Entries) != 2 {
|
||||
t.Errorf("Expected 2 entries, got %d", len(subtitle.Entries))
|
||||
}
|
||||
|
||||
// Check first entry
|
||||
if subtitle.Entries[0].Text != "This is the first line." {
|
||||
t.Errorf("Expected first entry text 'This is the first line.', got '%s'", subtitle.Entries[0].Text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFromSubtitle(t *testing.T) {
|
||||
// Create test subtitle
|
||||
subtitle := model.NewSubtitle()
|
||||
subtitle.Format = "srt"
|
||||
|
||||
entry1 := model.NewSubtitleEntry()
|
||||
entry1.Index = 1
|
||||
entry1.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0}
|
||||
entry1.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0}
|
||||
entry1.Text = "This is the first line."
|
||||
|
||||
entry2 := model.NewSubtitleEntry()
|
||||
entry2.Index = 2
|
||||
entry2.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0}
|
||||
entry2.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 8, Milliseconds: 0}
|
||||
entry2.Text = "This is the second line."
|
||||
|
||||
subtitle.Entries = append(subtitle.Entries, entry1, entry2)
|
||||
|
||||
// Convert from subtitle to SRT
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "output.srt")
|
||||
err := ConvertFromSubtitle(subtitle, outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertFromSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify by parsing back
|
||||
entries, err := Parse(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse output file: %v", err)
|
||||
}
|
||||
|
||||
if len(entries) != 2 {
|
||||
t.Errorf("Expected 2 entries, got %d", len(entries))
|
||||
}
|
||||
|
||||
if entries[0].Content != "This is the first line." {
|
||||
t.Errorf("Expected first entry content 'This is the first line.', got '%s'", entries[0].Content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
// Create test file with non-sequential numbers
|
||||
content := `2
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
This is the first line.
|
||||
|
||||
5
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
This is the second line.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.srt")
|
||||
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create test file: %v", err)
|
||||
}
|
||||
|
||||
// Format the file
|
||||
err := Format(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Format failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify by parsing back
|
||||
entries, err := Parse(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse formatted file: %v", err)
|
||||
}
|
||||
|
||||
// Check that numbers are sequential
|
||||
if entries[0].Number != 1 {
|
||||
t.Errorf("Expected first entry number to be 1, got %d", entries[0].Number)
|
||||
}
|
||||
if entries[1].Number != 2 {
|
||||
t.Errorf("Expected second entry number to be 2, got %d", entries[1].Number)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSRTTimestamp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected model.Timestamp
|
||||
}{
|
||||
{
|
||||
name: "Standard format",
|
||||
input: "00:00:01,000",
|
||||
expected: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
},
|
||||
{
|
||||
name: "With milliseconds",
|
||||
input: "00:00:01,500",
|
||||
expected: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 500},
|
||||
},
|
||||
{
|
||||
name: "Full hours, minutes, seconds",
|
||||
input: "01:02:03,456",
|
||||
expected: model.Timestamp{Hours: 1, Minutes: 2, Seconds: 3, Milliseconds: 456},
|
||||
},
|
||||
{
|
||||
name: "With dot instead of comma",
|
||||
input: "00:00:01.000", // Should auto-convert . to ,
|
||||
expected: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
},
|
||||
{
|
||||
name: "Invalid format",
|
||||
input: "invalid",
|
||||
expected: model.Timestamp{}, // Should return zero timestamp
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := parseSRTTimestamp(tc.input)
|
||||
if result != tc.expected {
|
||||
t.Errorf("For input '%s', expected %+v, got %+v", tc.input, tc.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatSRTTimestamp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
input model.Timestamp
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Zero timestamp",
|
||||
input: model.Timestamp{},
|
||||
expected: "00:00:00,000",
|
||||
},
|
||||
{
|
||||
name: "Simple seconds",
|
||||
input: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
expected: "00:00:01,000",
|
||||
},
|
||||
{
|
||||
name: "With milliseconds",
|
||||
input: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 500},
|
||||
expected: "00:00:01,500",
|
||||
},
|
||||
{
|
||||
name: "Full timestamp",
|
||||
input: model.Timestamp{Hours: 1, Minutes: 2, Seconds: 3, Milliseconds: 456},
|
||||
expected: "01:02:03,456",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := formatSRTTimestamp(tc.input)
|
||||
if result != tc.expected {
|
||||
t.Errorf("For timestamp %+v, expected '%s', got '%s'", tc.input, tc.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEntryTimeStampUnset(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
entry model.SRTEntry
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "Unset timestamp",
|
||||
entry: model.SRTEntry{Number: 1},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Set timestamp",
|
||||
entry: model.SRTEntry{
|
||||
Number: 1,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := isEntryTimeStampUnset(tc.entry)
|
||||
if result != tc.expected {
|
||||
t.Errorf("For entry %+v, expected isEntryTimeStampUnset to be %v, got %v", tc.entry, tc.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToLyrics(t *testing.T) {
|
||||
// Create test entries
|
||||
entries := []model.SRTEntry{
|
||||
{
|
||||
Number: 1,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0},
|
||||
Content: "This is the first line.",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 8, Milliseconds: 0},
|
||||
Content: "This is the second line.",
|
||||
},
|
||||
}
|
||||
|
||||
// Convert to lyrics
|
||||
lyrics := ConvertToLyrics(entries)
|
||||
|
||||
// Check result
|
||||
if len(lyrics.Timeline) != 2 {
|
||||
t.Errorf("Expected 2 timeline entries, got %d", len(lyrics.Timeline))
|
||||
}
|
||||
if len(lyrics.Content) != 2 {
|
||||
t.Errorf("Expected 2 content entries, got %d", len(lyrics.Content))
|
||||
}
|
||||
|
||||
// Check timeline entries
|
||||
if lyrics.Timeline[0] != entries[0].StartTime {
|
||||
t.Errorf("First timeline entry: expected %+v, got %+v", entries[0].StartTime, lyrics.Timeline[0])
|
||||
}
|
||||
if lyrics.Timeline[1] != entries[1].StartTime {
|
||||
t.Errorf("Second timeline entry: expected %+v, got %+v", entries[1].StartTime, lyrics.Timeline[1])
|
||||
}
|
||||
|
||||
// Check content entries
|
||||
if lyrics.Content[0] != entries[0].Content {
|
||||
t.Errorf("First content entry: expected '%s', got '%s'", entries[0].Content, lyrics.Content[0])
|
||||
}
|
||||
if lyrics.Content[1] != entries[1].Content {
|
||||
t.Errorf("Second content entry: expected '%s', got '%s'", entries[1].Content, lyrics.Content[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_EdgeCases(t *testing.T) {
|
||||
// Test with empty file
|
||||
tempDir := t.TempDir()
|
||||
emptyFile := filepath.Join(tempDir, "empty.srt")
|
||||
if err := os.WriteFile(emptyFile, []byte{}, 0644); err != nil {
|
||||
t.Fatalf("Failed to create empty test file: %v", err)
|
||||
}
|
||||
|
||||
entries, err := Parse(emptyFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed on empty file: %v", err)
|
||||
}
|
||||
|
||||
if len(entries) != 0 {
|
||||
t.Errorf("Expected 0 entries for empty file, got %d", len(entries))
|
||||
}
|
||||
|
||||
// Test with malformed file (missing timestamp line)
|
||||
malformedFile := filepath.Join(tempDir, "malformed.srt")
|
||||
content := `1
|
||||
This is missing a timestamp line.
|
||||
|
||||
2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
This is valid.
|
||||
`
|
||||
if err := os.WriteFile(malformedFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create malformed test file: %v", err)
|
||||
}
|
||||
|
||||
entries, err = Parse(malformedFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed on malformed file: %v", err)
|
||||
}
|
||||
|
||||
// SRT解析器更宽容,可能会解析出两个条目
|
||||
if len(entries) != 2 {
|
||||
t.Errorf("Expected 2 entries, got %d", len(entries))
|
||||
}
|
||||
|
||||
// Test with incomplete last entry
|
||||
incompleteFile := filepath.Join(tempDir, "incomplete.srt")
|
||||
content = `1
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
This is complete.
|
||||
|
||||
2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
`
|
||||
if err := os.WriteFile(incompleteFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create incomplete test file: %v", err)
|
||||
}
|
||||
|
||||
entries, err = Parse(incompleteFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed on incomplete file: %v", err)
|
||||
}
|
||||
|
||||
// Should have one complete entry, the incomplete one is discarded due to empty content
|
||||
if len(entries) != 1 {
|
||||
t.Errorf("Expected 1 entry (only the completed one), got %d", len(entries))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_FileError(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
_, err := Parse("/nonexistent/file.srt")
|
||||
if err == nil {
|
||||
t.Error("Expected error when parsing non-existent file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate_FileError(t *testing.T) {
|
||||
// Create test entries
|
||||
entries := []model.SRTEntry{
|
||||
{
|
||||
Number: 1,
|
||||
StartTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
EndTime: model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0},
|
||||
Content: "This is a test line.",
|
||||
},
|
||||
}
|
||||
|
||||
// Test with invalid path
|
||||
err := Generate(entries, "/nonexistent/directory/file.srt")
|
||||
if err == nil {
|
||||
t.Error("Expected error when generating to invalid path, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormat_FileError(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
err := Format("/nonexistent/file.srt")
|
||||
if err == nil {
|
||||
t.Error("Expected error when formatting non-existent file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSubtitle_WithHTMLTags(t *testing.T) {
|
||||
// Create a temporary test file with HTML tags
|
||||
content := `1
|
||||
00:00:01,000 --> 00:00:04,000
|
||||
<i>This is in italic.</i>
|
||||
|
||||
2
|
||||
00:00:05,000 --> 00:00:08,000
|
||||
<b>This is in bold.</b>
|
||||
|
||||
3
|
||||
00:00:09,000 --> 00:00:12,000
|
||||
<u>This is underlined.</u>
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "styles.srt")
|
||||
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create test file with HTML tags: %v", err)
|
||||
}
|
||||
|
||||
// Convert to subtitle
|
||||
subtitle, err := ConvertToSubtitle(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertToSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Check if HTML tags were detected
|
||||
if value, ok := subtitle.Entries[0].FormatData["has_html_tags"]; !ok || value != true {
|
||||
t.Errorf("Expected FormatData to contain has_html_tags=true for entry with italic")
|
||||
}
|
||||
if value, ok := subtitle.Entries[0].Styles["italic"]; !ok || value != "true" {
|
||||
t.Errorf("Expected Styles to contain italic=true for entry with <i> tag")
|
||||
}
|
||||
|
||||
if value, ok := subtitle.Entries[1].Styles["bold"]; !ok || value != "true" {
|
||||
t.Errorf("Expected Styles to contain bold=true for entry with <b> tag")
|
||||
}
|
||||
|
||||
if value, ok := subtitle.Entries[2].Styles["underline"]; !ok || value != "true" {
|
||||
t.Errorf("Expected Styles to contain underline=true for entry with <u> tag")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSubtitle_FileError(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
_, err := ConvertToSubtitle("/nonexistent/file.srt")
|
||||
if err == nil {
|
||||
t.Error("Expected error when converting non-existent file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFromSubtitle_WithStyling(t *testing.T) {
|
||||
// Create a subtitle with style attributes
|
||||
subtitle := model.NewSubtitle()
|
||||
subtitle.Format = "srt"
|
||||
|
||||
// Create an entry with italics
|
||||
entry1 := model.NewSubtitleEntry()
|
||||
entry1.Index = 1
|
||||
entry1.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0}
|
||||
entry1.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0}
|
||||
entry1.Text = "This should be italic."
|
||||
entry1.Styles["italic"] = "true"
|
||||
|
||||
// Create an entry with bold
|
||||
entry2 := model.NewSubtitleEntry()
|
||||
entry2.Index = 2
|
||||
entry2.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0}
|
||||
entry2.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 8, Milliseconds: 0}
|
||||
entry2.Text = "This should be bold."
|
||||
entry2.Styles["bold"] = "true"
|
||||
|
||||
// Create an entry with underline
|
||||
entry3 := model.NewSubtitleEntry()
|
||||
entry3.Index = 3
|
||||
entry3.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 9, Milliseconds: 0}
|
||||
entry3.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 12, Milliseconds: 0}
|
||||
entry3.Text = "This should be underlined."
|
||||
entry3.Styles["underline"] = "true"
|
||||
|
||||
subtitle.Entries = append(subtitle.Entries, entry1, entry2, entry3)
|
||||
|
||||
// Convert from subtitle to SRT
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "styled.srt")
|
||||
err := ConvertFromSubtitle(subtitle, outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertFromSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify by reading the file directly
|
||||
content, err := os.ReadFile(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read output file: %v", err)
|
||||
}
|
||||
|
||||
// Check that HTML tags were applied
|
||||
contentStr := string(content)
|
||||
if !strings.Contains(contentStr, "<i>This should be italic.</i>") {
|
||||
t.Errorf("Expected italic HTML tags to be applied")
|
||||
}
|
||||
if !strings.Contains(contentStr, "<b>This should be bold.</b>") {
|
||||
t.Errorf("Expected bold HTML tags to be applied")
|
||||
}
|
||||
if !strings.Contains(contentStr, "<u>This should be underlined.</u>") {
|
||||
t.Errorf("Expected underline HTML tags to be applied")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFromSubtitle_FileError(t *testing.T) {
|
||||
// Create simple subtitle
|
||||
subtitle := model.NewSubtitle()
|
||||
subtitle.Entries = append(subtitle.Entries, model.NewSubtitleEntry())
|
||||
|
||||
// Test with invalid path
|
||||
err := ConvertFromSubtitle(subtitle, "/nonexistent/directory/file.srt")
|
||||
if err == nil {
|
||||
t.Error("Expected error when converting to invalid path, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFromSubtitle_WithExistingHTMLTags(t *testing.T) {
|
||||
// Create a subtitle with text that already contains HTML tags
|
||||
subtitle := model.NewSubtitle()
|
||||
subtitle.Format = "srt"
|
||||
|
||||
// Create an entry with existing italic tags but also style attribute
|
||||
entry := model.NewSubtitleEntry()
|
||||
entry.Index = 1
|
||||
entry.StartTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0}
|
||||
entry.EndTime = model.Timestamp{Hours: 0, Minutes: 0, Seconds: 4, Milliseconds: 0}
|
||||
entry.Text = "<i>Already italic text.</i>"
|
||||
entry.Styles["italic"] = "true" // Should not double-wrap with <i> tags
|
||||
|
||||
subtitle.Entries = append(subtitle.Entries, entry)
|
||||
|
||||
// Convert from subtitle to SRT
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "existing_tags.srt")
|
||||
err := ConvertFromSubtitle(subtitle, outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertFromSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify by reading the file directly
|
||||
content, err := os.ReadFile(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read output file: %v", err)
|
||||
}
|
||||
|
||||
// Should not have double tags
|
||||
contentStr := string(content)
|
||||
if strings.Contains(contentStr, "<i><i>") {
|
||||
t.Errorf("Expected no duplicate italic tags, but found them")
|
||||
}
|
||||
}
|
182
internal/format/srt/utils_test.go
Normal file
182
internal/format/srt/utils_test.go
Normal file
|
@ -0,0 +1,182 @@
|
|||
package srt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"sub-cli/internal/model"
|
||||
)
|
||||
|
||||
func TestParseSRTTimestamp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected model.Timestamp
|
||||
}{
|
||||
{
|
||||
input: "00:00:01,000",
|
||||
expected: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 0,
|
||||
Seconds: 1,
|
||||
Milliseconds: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "01:02:03,456",
|
||||
expected: model.Timestamp{
|
||||
Hours: 1,
|
||||
Minutes: 2,
|
||||
Seconds: 3,
|
||||
Milliseconds: 456,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "10:20:30,789",
|
||||
expected: model.Timestamp{
|
||||
Hours: 10,
|
||||
Minutes: 20,
|
||||
Seconds: 30,
|
||||
Milliseconds: 789,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Test invalid format
|
||||
input: "invalid",
|
||||
expected: model.Timestamp{},
|
||||
},
|
||||
{
|
||||
// Test with dot instead of comma
|
||||
input: "01:02:03.456",
|
||||
expected: model.Timestamp{
|
||||
Hours: 1,
|
||||
Minutes: 2,
|
||||
Seconds: 3,
|
||||
Milliseconds: 456,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
result := parseSRTTimestamp(tc.input)
|
||||
if result.Hours != tc.expected.Hours ||
|
||||
result.Minutes != tc.expected.Minutes ||
|
||||
result.Seconds != tc.expected.Seconds ||
|
||||
result.Milliseconds != tc.expected.Milliseconds {
|
||||
t.Errorf("parseSRTTimestamp(%s) = %+v, want %+v",
|
||||
tc.input, result, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatSRTTimestamp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input model.Timestamp
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
input: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 0,
|
||||
Seconds: 1,
|
||||
Milliseconds: 0,
|
||||
},
|
||||
expected: "00:00:01,000",
|
||||
},
|
||||
{
|
||||
input: model.Timestamp{
|
||||
Hours: 1,
|
||||
Minutes: 2,
|
||||
Seconds: 3,
|
||||
Milliseconds: 456,
|
||||
},
|
||||
expected: "01:02:03,456",
|
||||
},
|
||||
{
|
||||
input: model.Timestamp{
|
||||
Hours: 10,
|
||||
Minutes: 20,
|
||||
Seconds: 30,
|
||||
Milliseconds: 789,
|
||||
},
|
||||
expected: "10:20:30,789",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
result := formatSRTTimestamp(tc.input)
|
||||
if result != tc.expected {
|
||||
t.Errorf("formatSRTTimestamp(%+v) = %s, want %s",
|
||||
tc.input, result, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEntryTimeStampUnset(t *testing.T) {
|
||||
testCases := []struct {
|
||||
entry model.SRTEntry
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
entry: model.SRTEntry{
|
||||
StartTime: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 0,
|
||||
Seconds: 0,
|
||||
Milliseconds: 0,
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
entry: model.SRTEntry{
|
||||
StartTime: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 0,
|
||||
Seconds: 1,
|
||||
Milliseconds: 0,
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
entry: model.SRTEntry{
|
||||
StartTime: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 1,
|
||||
Seconds: 0,
|
||||
Milliseconds: 0,
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
entry: model.SRTEntry{
|
||||
StartTime: model.Timestamp{
|
||||
Hours: 1,
|
||||
Minutes: 0,
|
||||
Seconds: 0,
|
||||
Milliseconds: 0,
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
entry: model.SRTEntry{
|
||||
StartTime: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 0,
|
||||
Seconds: 0,
|
||||
Milliseconds: 1,
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
result := isEntryTimeStampUnset(tc.entry)
|
||||
if result != tc.expected {
|
||||
t.Errorf("Case %d: isEntryTimeStampUnset(%+v) = %v, want %v",
|
||||
i, tc.entry, result, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue