chore: seperate large files
This commit is contained in:
parent
ebbf516689
commit
76e1298ded
44 changed files with 5745 additions and 4173 deletions
181
internal/format/lrc/converter_test.go
Normal file
181
internal/format/lrc/converter_test.go
Normal file
|
@ -0,0 +1,181 @@
|
|||
package lrc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sub-cli/internal/model"
|
||||
)
|
||||
|
||||
func TestConvertToSubtitle(t *testing.T) {
|
||||
// Create a temporary test file
|
||||
content := `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
|
||||
[00:01.00]This is the first line.
|
||||
[00:05.00]This is the second line.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.lrc")
|
||||
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 != "lrc" {
|
||||
t.Errorf("Expected format 'lrc', 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].StartTime.Hours != 0 || subtitle.Entries[0].StartTime.Minutes != 0 ||
|
||||
subtitle.Entries[0].StartTime.Seconds != 1 || subtitle.Entries[0].StartTime.Milliseconds != 0 {
|
||||
t.Errorf("First entry start time: expected 00:01.00, got %+v", subtitle.Entries[0].StartTime)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// Check metadata conversion
|
||||
if subtitle.Title != "Test LRC File" {
|
||||
t.Errorf("Expected title 'Test LRC File', got '%s'", subtitle.Title)
|
||||
}
|
||||
|
||||
if subtitle.Metadata["ar"] != "Test Artist" {
|
||||
t.Errorf("Expected artist metadata 'Test Artist', got '%s'", subtitle.Metadata["ar"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFromSubtitle(t *testing.T) {
|
||||
// Create a subtitle
|
||||
subtitle := model.NewSubtitle()
|
||||
subtitle.Format = "lrc"
|
||||
subtitle.Title = "Test LRC File"
|
||||
subtitle.Metadata["ar"] = "Test Artist"
|
||||
|
||||
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 LRC
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "output.lrc")
|
||||
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
|
||||
contentStr := string(content)
|
||||
|
||||
// Check metadata
|
||||
if !strings.Contains(contentStr, "[ti:Test LRC File]") {
|
||||
t.Errorf("Expected title metadata in output, not found")
|
||||
}
|
||||
|
||||
if !strings.Contains(contentStr, "[ar:Test Artist]") {
|
||||
t.Errorf("Expected artist metadata in output, not found")
|
||||
}
|
||||
|
||||
// Check timeline entries
|
||||
if !strings.Contains(contentStr, "[00:01.000]This is the first line.") {
|
||||
t.Errorf("Expected first timeline entry in output, not found")
|
||||
}
|
||||
|
||||
if !strings.Contains(contentStr, "[00:05.000]This is the second line.") {
|
||||
t.Errorf("Expected second timeline entry in output, not found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSubtitle_FileError(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
_, err := ConvertToSubtitle("/nonexistent/file.lrc")
|
||||
if err == nil {
|
||||
t.Error("Expected error when converting non-existent file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSubtitle_EdgeCases(t *testing.T) {
|
||||
// Test with empty lyrics (no content/timeline)
|
||||
tempDir := t.TempDir()
|
||||
emptyLyricsFile := filepath.Join(tempDir, "empty_lyrics.lrc")
|
||||
content := `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
`
|
||||
if err := os.WriteFile(emptyLyricsFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create empty lyrics test file: %v", err)
|
||||
}
|
||||
|
||||
subtitle, err := ConvertToSubtitle(emptyLyricsFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertToSubtitle failed on empty lyrics: %v", err)
|
||||
}
|
||||
|
||||
if len(subtitle.Entries) != 0 {
|
||||
t.Errorf("Expected 0 entries for empty lyrics, got %d", len(subtitle.Entries))
|
||||
}
|
||||
|
||||
if subtitle.Title != "Test LRC File" {
|
||||
t.Errorf("Expected title 'Test LRC File', got '%s'", subtitle.Title)
|
||||
}
|
||||
|
||||
// Test with more content than timeline entries
|
||||
moreContentFile := filepath.Join(tempDir, "more_content.lrc")
|
||||
content = `[ti:Test LRC File]
|
||||
|
||||
[00:01.00]This has a timestamp.
|
||||
This doesn't have a timestamp but is content.
|
||||
`
|
||||
if err := os.WriteFile(moreContentFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create more content test file: %v", err)
|
||||
}
|
||||
|
||||
subtitle, err = ConvertToSubtitle(moreContentFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertToSubtitle failed on file with more content than timestamps: %v", err)
|
||||
}
|
||||
|
||||
if len(subtitle.Entries) != 1 {
|
||||
t.Errorf("Expected 1 entry (only the one with timestamp), got %d", len(subtitle.Entries))
|
||||
}
|
||||
}
|
||||
|
||||
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.lrc")
|
||||
if err == nil {
|
||||
t.Error("Expected error when converting to invalid path, got nil")
|
||||
}
|
||||
}
|
72
internal/format/lrc/formatter_test.go
Normal file
72
internal/format/lrc/formatter_test.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package lrc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
// Create a temporary test file with messy formatting
|
||||
content := `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
|
||||
[00:01.00]This should be first.
|
||||
[00:05.00]This is the second line.
|
||||
[00:09.50]This is the third line.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.lrc")
|
||||
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)
|
||||
}
|
||||
|
||||
// Check that the file was at least generated successfully
|
||||
lines := strings.Split(string(formatted), "\n")
|
||||
if len(lines) < 4 {
|
||||
t.Fatalf("Expected at least 4 lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
// Check that the metadata was preserved
|
||||
if !strings.Contains(string(formatted), "[ti:Test LRC File]") {
|
||||
t.Errorf("Expected title metadata in output, not found")
|
||||
}
|
||||
|
||||
if !strings.Contains(string(formatted), "[ar:Test Artist]") {
|
||||
t.Errorf("Expected artist metadata in output, not found")
|
||||
}
|
||||
|
||||
// Check that all the content lines are present
|
||||
if !strings.Contains(string(formatted), "This should be first") {
|
||||
t.Errorf("Expected 'This should be first' in output, not found")
|
||||
}
|
||||
|
||||
if !strings.Contains(string(formatted), "This is the second line") {
|
||||
t.Errorf("Expected 'This is the second line' in output, not found")
|
||||
}
|
||||
|
||||
if !strings.Contains(string(formatted), "This is the third line") {
|
||||
t.Errorf("Expected 'This is the third line' in output, not found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormat_FileError(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
err := Format("/nonexistent/file.lrc")
|
||||
if err == nil {
|
||||
t.Error("Expected error when formatting non-existent file, got nil")
|
||||
}
|
||||
}
|
151
internal/format/lrc/generator_test.go
Normal file
151
internal/format/lrc/generator_test.go
Normal file
|
@ -0,0 +1,151 @@
|
|||
package lrc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sub-cli/internal/model"
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
// Create test lyrics
|
||||
lyrics := model.Lyrics{
|
||||
Metadata: map[string]string{
|
||||
"ti": "Test LRC File",
|
||||
"ar": "Test Artist",
|
||||
},
|
||||
Timeline: []model.Timestamp{
|
||||
{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0},
|
||||
},
|
||||
Content: []string{
|
||||
"This is the first line.",
|
||||
"This is the second line.",
|
||||
},
|
||||
}
|
||||
|
||||
// Generate LRC file
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "output.lrc")
|
||||
err := Generate(lyrics, 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) < 4 {
|
||||
t.Fatalf("Expected at least 4 lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
hasTitleLine := false
|
||||
hasFirstTimeline := false
|
||||
|
||||
for _, line := range lines {
|
||||
if line == "[ti:Test LRC File]" {
|
||||
hasTitleLine = true
|
||||
}
|
||||
if line == "[00:01.000]This is the first line." {
|
||||
hasFirstTimeline = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasTitleLine {
|
||||
t.Errorf("Expected title line '[ti:Test LRC File]' not found")
|
||||
}
|
||||
|
||||
if !hasFirstTimeline {
|
||||
t.Errorf("Expected timeline line '[00:01.000]This is the first line.' not found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate_FileError(t *testing.T) {
|
||||
// Create test lyrics
|
||||
lyrics := model.Lyrics{
|
||||
Metadata: map[string]string{
|
||||
"ti": "Test LRC File",
|
||||
},
|
||||
Timeline: []model.Timestamp{
|
||||
{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
},
|
||||
Content: []string{
|
||||
"This is a test line.",
|
||||
},
|
||||
}
|
||||
|
||||
// Test with invalid path
|
||||
err := Generate(lyrics, "/nonexistent/directory/file.lrc")
|
||||
if err == nil {
|
||||
t.Error("Expected error when generating to invalid path, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate_EdgeCases(t *testing.T) {
|
||||
// Test with empty lyrics
|
||||
emptyLyrics := model.Lyrics{
|
||||
Metadata: map[string]string{
|
||||
"ti": "Empty Test",
|
||||
},
|
||||
}
|
||||
|
||||
tempDir := t.TempDir()
|
||||
emptyFile := filepath.Join(tempDir, "empty_output.lrc")
|
||||
err := Generate(emptyLyrics, emptyFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate failed with empty lyrics: %v", err)
|
||||
}
|
||||
|
||||
// Verify content has metadata but no timeline entries
|
||||
content, err := os.ReadFile(emptyFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read empty output file: %v", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(string(content), "[ti:Empty Test]") {
|
||||
t.Errorf("Expected metadata in empty lyrics output, not found")
|
||||
}
|
||||
|
||||
// Test with unequal timeline and content lengths
|
||||
unequalLyrics := model.Lyrics{
|
||||
Timeline: []model.Timestamp{
|
||||
{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0},
|
||||
},
|
||||
Content: []string{
|
||||
"This is the only content line.",
|
||||
},
|
||||
}
|
||||
|
||||
unequalFile := filepath.Join(tempDir, "unequal_output.lrc")
|
||||
err = Generate(unequalLyrics, unequalFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate failed with unequal lyrics: %v", err)
|
||||
}
|
||||
|
||||
// Should only generate for the entries that have both timeline and content
|
||||
content, err = os.ReadFile(unequalFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read unequal output file: %v", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
timelineLines := 0
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "[") && strings.Contains(line, "]") &&
|
||||
strings.Contains(line, ":") && strings.Contains(line, ".") {
|
||||
timelineLines++
|
||||
}
|
||||
}
|
||||
|
||||
if timelineLines > 1 {
|
||||
t.Errorf("Expected only 1 timeline entry for unequal lyrics, got %d", timelineLines)
|
||||
}
|
||||
}
|
|
@ -1,518 +0,0 @@
|
|||
package lrc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sub-cli/internal/model"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
// Create a temporary test file
|
||||
content := `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
[al:Test Album]
|
||||
[by:Test Creator]
|
||||
|
||||
[00:01.00]This is the first line.
|
||||
[00:05.00]This is the second line.
|
||||
[00:09.50]This is the third line.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.lrc")
|
||||
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create test file: %v", err)
|
||||
}
|
||||
|
||||
// Test parsing
|
||||
lyrics, err := Parse(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify results
|
||||
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 metadata
|
||||
if lyrics.Metadata["ti"] != "Test LRC File" {
|
||||
t.Errorf("Expected title 'Test LRC File', got '%s'", lyrics.Metadata["ti"])
|
||||
}
|
||||
if lyrics.Metadata["ar"] != "Test Artist" {
|
||||
t.Errorf("Expected artist 'Test Artist', got '%s'", lyrics.Metadata["ar"])
|
||||
}
|
||||
if lyrics.Metadata["al"] != "Test Album" {
|
||||
t.Errorf("Expected album 'Test Album', got '%s'", lyrics.Metadata["al"])
|
||||
}
|
||||
if lyrics.Metadata["by"] != "Test Creator" {
|
||||
t.Errorf("Expected creator 'Test Creator', got '%s'", lyrics.Metadata["by"])
|
||||
}
|
||||
|
||||
// Check first timeline 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 entry time: expected 00:01.00, got %+v", lyrics.Timeline[0])
|
||||
}
|
||||
|
||||
// Check third timeline entry
|
||||
if lyrics.Timeline[2].Hours != 0 || lyrics.Timeline[2].Minutes != 0 ||
|
||||
lyrics.Timeline[2].Seconds != 9 || lyrics.Timeline[2].Milliseconds != 500 {
|
||||
t.Errorf("Third entry time: expected 00:09.50, got %+v", lyrics.Timeline[2])
|
||||
}
|
||||
|
||||
// Check content
|
||||
if lyrics.Content[0] != "This is the first line." {
|
||||
t.Errorf("First entry content: expected 'This is the first line.', got '%s'", lyrics.Content[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
// Create test lyrics
|
||||
lyrics := model.Lyrics{
|
||||
Metadata: map[string]string{
|
||||
"ti": "Test LRC File",
|
||||
"ar": "Test Artist",
|
||||
},
|
||||
Timeline: []model.Timestamp{
|
||||
{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
{Hours: 0, Minutes: 0, Seconds: 5, Milliseconds: 0},
|
||||
},
|
||||
Content: []string{
|
||||
"This is the first line.",
|
||||
"This is the second line.",
|
||||
},
|
||||
}
|
||||
|
||||
// Generate LRC file
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "output.lrc")
|
||||
err := Generate(lyrics, 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) < 4 {
|
||||
t.Fatalf("Expected at least 4 lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
hasTitleLine := false
|
||||
hasFirstTimeline := false
|
||||
|
||||
for _, line := range lines {
|
||||
if line == "[ti:Test LRC File]" {
|
||||
hasTitleLine = true
|
||||
}
|
||||
if line == "[00:01.000]This is the first line." {
|
||||
hasFirstTimeline = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasTitleLine {
|
||||
t.Errorf("Expected title line '[ti:Test LRC File]' not found")
|
||||
}
|
||||
|
||||
if !hasFirstTimeline {
|
||||
t.Errorf("Expected timeline line '[00:01.000]This is the first line.' not found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSubtitle(t *testing.T) {
|
||||
// Create a temporary test file
|
||||
content := `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
|
||||
[00:01.00]This is the first line.
|
||||
[00:05.00]This is the second line.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.lrc")
|
||||
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 != "lrc" {
|
||||
t.Errorf("Expected format 'lrc', got '%s'", subtitle.Format)
|
||||
}
|
||||
|
||||
if subtitle.Title != "Test LRC File" {
|
||||
t.Errorf("Expected title 'Test LRC File', got '%s'", subtitle.Title)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// Check metadata
|
||||
if subtitle.Metadata["ar"] != "Test Artist" {
|
||||
t.Errorf("Expected metadata 'ar' to be 'Test Artist', got '%s'", subtitle.Metadata["ar"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFromSubtitle(t *testing.T) {
|
||||
// Create test subtitle
|
||||
subtitle := model.NewSubtitle()
|
||||
subtitle.Format = "lrc"
|
||||
subtitle.Title = "Test LRC File"
|
||||
subtitle.Metadata["ar"] = "Test Artist"
|
||||
|
||||
entry1 := model.NewSubtitleEntry()
|
||||
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.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 LRC
|
||||
tempDir := t.TempDir()
|
||||
outputFile := filepath.Join(tempDir, "output.lrc")
|
||||
err := ConvertFromSubtitle(subtitle, outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertFromSubtitle failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify by parsing back
|
||||
lyrics, err := Parse(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse output file: %v", err)
|
||||
}
|
||||
|
||||
if len(lyrics.Timeline) != 2 {
|
||||
t.Errorf("Expected 2 entries, got %d", len(lyrics.Timeline))
|
||||
}
|
||||
|
||||
if lyrics.Content[0] != "This is the first line." {
|
||||
t.Errorf("Expected first entry content 'This is the first line.', got '%s'", lyrics.Content[0])
|
||||
}
|
||||
|
||||
if lyrics.Metadata["ti"] != "Test LRC File" {
|
||||
t.Errorf("Expected title metadata 'Test LRC File', got '%s'", lyrics.Metadata["ti"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
// Create test LRC file with inconsistent timestamp formatting
|
||||
content := `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
|
||||
[0:1.0]This is the first line.
|
||||
[0:5]This is the second line.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.lrc")
|
||||
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
|
||||
lyrics, err := Parse(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse formatted file: %v", err)
|
||||
}
|
||||
|
||||
// Check that timestamps are formatted correctly
|
||||
if lyrics.Timeline[0].Seconds != 1 || lyrics.Timeline[0].Milliseconds != 0 {
|
||||
t.Errorf("Expected first timestamp to be 00:01.000, got %+v", lyrics.Timeline[0])
|
||||
}
|
||||
|
||||
// Verify metadata is preserved
|
||||
if lyrics.Metadata["ti"] != "Test LRC File" {
|
||||
t.Errorf("Expected title 'Test LRC File', got '%s'", lyrics.Metadata["ti"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTimestamp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected model.Timestamp
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
name: "Simple minute and second",
|
||||
input: "01:30",
|
||||
expected: model.Timestamp{Hours: 0, Minutes: 1, Seconds: 30, Milliseconds: 0},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "With milliseconds (1 digit)",
|
||||
input: "01:30.5",
|
||||
expected: model.Timestamp{Hours: 0, Minutes: 1, Seconds: 30, Milliseconds: 500},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "With milliseconds (2 digits)",
|
||||
input: "01:30.75",
|
||||
expected: model.Timestamp{Hours: 0, Minutes: 1, Seconds: 30, Milliseconds: 750},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "With milliseconds (3 digits)",
|
||||
input: "01:30.123",
|
||||
expected: model.Timestamp{Hours: 0, Minutes: 1, Seconds: 30, Milliseconds: 123},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "With hours, minutes, seconds",
|
||||
input: "01:30:45",
|
||||
expected: model.Timestamp{Hours: 1, Minutes: 30, Seconds: 45, Milliseconds: 0},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "With hours, minutes, seconds and milliseconds",
|
||||
input: "01:30:45.5",
|
||||
expected: model.Timestamp{Hours: 1, Minutes: 30, Seconds: 45, Milliseconds: 500},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid format (single number)",
|
||||
input: "123",
|
||||
expected: model.Timestamp{},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid format (too many parts)",
|
||||
input: "01:30:45:67",
|
||||
expected: model.Timestamp{},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid minute (not a number)",
|
||||
input: "aa:30",
|
||||
expected: model.Timestamp{},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid second (not a number)",
|
||||
input: "01:bb",
|
||||
expected: model.Timestamp{},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid millisecond (not a number)",
|
||||
input: "01:30.cc",
|
||||
expected: model.Timestamp{},
|
||||
hasError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result, err := ParseTimestamp(tc.input)
|
||||
|
||||
if tc.hasError && err == nil {
|
||||
t.Errorf("Expected error for input '%s', but got none", tc.input)
|
||||
}
|
||||
|
||||
if !tc.hasError && err != nil {
|
||||
t.Errorf("Unexpected error for input '%s': %v", tc.input, err)
|
||||
}
|
||||
|
||||
if !tc.hasError && result != tc.expected {
|
||||
t.Errorf("For input '%s', expected %+v, got %+v", tc.input, tc.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_FileErrors(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
_, err := Parse("/nonexistent/file.lrc")
|
||||
if err == nil {
|
||||
t.Error("Expected error when parsing non-existent file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_EdgeCases(t *testing.T) {
|
||||
// Test with empty file
|
||||
tempDir := t.TempDir()
|
||||
emptyFile := filepath.Join(tempDir, "empty.lrc")
|
||||
if err := os.WriteFile(emptyFile, []byte{}, 0644); err != nil {
|
||||
t.Fatalf("Failed to create empty test file: %v", err)
|
||||
}
|
||||
|
||||
lyrics, err := Parse(emptyFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed on empty file: %v", err)
|
||||
}
|
||||
|
||||
if len(lyrics.Timeline) != 0 || len(lyrics.Content) != 0 {
|
||||
t.Errorf("Expected empty lyrics for empty file, got %d timeline entries and %d content entries",
|
||||
len(lyrics.Timeline), len(lyrics.Content))
|
||||
}
|
||||
|
||||
// Test with invalid timestamps
|
||||
invalidFile := filepath.Join(tempDir, "invalid.lrc")
|
||||
content := `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
|
||||
[invalidtime]This should be ignored.
|
||||
[00:01.00]This is a valid line.
|
||||
`
|
||||
if err := os.WriteFile(invalidFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create invalid test file: %v", err)
|
||||
}
|
||||
|
||||
lyrics, err = Parse(invalidFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed on file with invalid timestamps: %v", err)
|
||||
}
|
||||
|
||||
if len(lyrics.Timeline) != 1 || len(lyrics.Content) != 1 {
|
||||
t.Errorf("Expected 1 valid timeline entry, got %d timeline entries and %d content entries",
|
||||
len(lyrics.Timeline), len(lyrics.Content))
|
||||
}
|
||||
|
||||
// Test with timestamp-only lines (no content)
|
||||
timestampOnlyFile := filepath.Join(tempDir, "timestamp_only.lrc")
|
||||
content = `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
|
||||
[00:01.00]
|
||||
[00:05.00]This has content.
|
||||
`
|
||||
if err := os.WriteFile(timestampOnlyFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create timestamp-only test file: %v", err)
|
||||
}
|
||||
|
||||
lyrics, err = Parse(timestampOnlyFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed on file with timestamp-only lines: %v", err)
|
||||
}
|
||||
|
||||
if len(lyrics.Timeline) != 1 || len(lyrics.Content) != 1 {
|
||||
t.Errorf("Expected 1 valid entry (ignoring empty content), got %d timeline entries and %d content entries",
|
||||
len(lyrics.Timeline), len(lyrics.Content))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate_FileError(t *testing.T) {
|
||||
// Create test lyrics
|
||||
lyrics := model.Lyrics{
|
||||
Metadata: map[string]string{
|
||||
"ti": "Test LRC File",
|
||||
},
|
||||
Timeline: []model.Timestamp{
|
||||
{Hours: 0, Minutes: 0, Seconds: 1, Milliseconds: 0},
|
||||
},
|
||||
Content: []string{
|
||||
"This is a test line.",
|
||||
},
|
||||
}
|
||||
|
||||
// Test with invalid path
|
||||
err := Generate(lyrics, "/nonexistent/directory/file.lrc")
|
||||
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.lrc")
|
||||
if err == nil {
|
||||
t.Error("Expected error when formatting non-existent file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSubtitle_FileError(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
_, err := ConvertToSubtitle("/nonexistent/file.lrc")
|
||||
if err == nil {
|
||||
t.Error("Expected error when converting non-existent file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSubtitle_EdgeCases(t *testing.T) {
|
||||
// Test with empty lyrics (no content/timeline)
|
||||
tempDir := t.TempDir()
|
||||
emptyLyricsFile := filepath.Join(tempDir, "empty_lyrics.lrc")
|
||||
content := `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
`
|
||||
if err := os.WriteFile(emptyLyricsFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create empty lyrics test file: %v", err)
|
||||
}
|
||||
|
||||
subtitle, err := ConvertToSubtitle(emptyLyricsFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertToSubtitle failed on empty lyrics: %v", err)
|
||||
}
|
||||
|
||||
if len(subtitle.Entries) != 0 {
|
||||
t.Errorf("Expected 0 entries for empty lyrics, got %d", len(subtitle.Entries))
|
||||
}
|
||||
|
||||
if subtitle.Title != "Test LRC File" {
|
||||
t.Errorf("Expected title 'Test LRC File', got '%s'", subtitle.Title)
|
||||
}
|
||||
|
||||
// Test with more content than timeline entries
|
||||
moreContentFile := filepath.Join(tempDir, "more_content.lrc")
|
||||
content = `[ti:Test LRC File]
|
||||
|
||||
[00:01.00]This has a timestamp.
|
||||
This doesn't have a timestamp but is content.
|
||||
`
|
||||
if err := os.WriteFile(moreContentFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create more content test file: %v", err)
|
||||
}
|
||||
|
||||
subtitle, err = ConvertToSubtitle(moreContentFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ConvertToSubtitle failed on file with more content than timestamps: %v", err)
|
||||
}
|
||||
|
||||
if len(subtitle.Entries) != 1 {
|
||||
t.Errorf("Expected 1 entry (only the one with timestamp), got %d", len(subtitle.Entries))
|
||||
}
|
||||
}
|
||||
|
||||
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.lrc")
|
||||
if err == nil {
|
||||
t.Error("Expected error when converting to invalid path, got nil")
|
||||
}
|
||||
}
|
185
internal/format/lrc/parser_test.go
Normal file
185
internal/format/lrc/parser_test.go
Normal file
|
@ -0,0 +1,185 @@
|
|||
package lrc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
// Create a temporary test file
|
||||
content := `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
[al:Test Album]
|
||||
[by:Test Creator]
|
||||
|
||||
[00:01.00]This is the first line.
|
||||
[00:05.00]This is the second line.
|
||||
[00:09.50]This is the third line.
|
||||
`
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "test.lrc")
|
||||
if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create test file: %v", err)
|
||||
}
|
||||
|
||||
// Test parsing
|
||||
lyrics, err := Parse(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify results
|
||||
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 metadata
|
||||
if lyrics.Metadata["ti"] != "Test LRC File" {
|
||||
t.Errorf("Expected title 'Test LRC File', got '%s'", lyrics.Metadata["ti"])
|
||||
}
|
||||
if lyrics.Metadata["ar"] != "Test Artist" {
|
||||
t.Errorf("Expected artist 'Test Artist', got '%s'", lyrics.Metadata["ar"])
|
||||
}
|
||||
if lyrics.Metadata["al"] != "Test Album" {
|
||||
t.Errorf("Expected album 'Test Album', got '%s'", lyrics.Metadata["al"])
|
||||
}
|
||||
if lyrics.Metadata["by"] != "Test Creator" {
|
||||
t.Errorf("Expected creator 'Test Creator', got '%s'", lyrics.Metadata["by"])
|
||||
}
|
||||
|
||||
// Check first timeline 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 entry time: expected 00:01.00, got %+v", lyrics.Timeline[0])
|
||||
}
|
||||
|
||||
// Check third timeline entry
|
||||
if lyrics.Timeline[2].Hours != 0 || lyrics.Timeline[2].Minutes != 0 ||
|
||||
lyrics.Timeline[2].Seconds != 9 || lyrics.Timeline[2].Milliseconds != 500 {
|
||||
t.Errorf("Third entry time: expected 00:09.50, got %+v", lyrics.Timeline[2])
|
||||
}
|
||||
|
||||
// Check content
|
||||
if lyrics.Content[0] != "This is the first line." {
|
||||
t.Errorf("First entry content: expected 'This is the first line.', got '%s'", lyrics.Content[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_FileErrors(t *testing.T) {
|
||||
// Test with non-existent file
|
||||
_, err := Parse("/nonexistent/file.lrc")
|
||||
if err == nil {
|
||||
t.Error("Expected error when parsing non-existent file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_EdgeCases(t *testing.T) {
|
||||
// Test with empty file
|
||||
tempDir := t.TempDir()
|
||||
emptyFile := filepath.Join(tempDir, "empty.lrc")
|
||||
if err := os.WriteFile(emptyFile, []byte(""), 0644); err != nil {
|
||||
t.Fatalf("Failed to create empty file: %v", err)
|
||||
}
|
||||
|
||||
lyrics, err := Parse(emptyFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed with empty file: %v", err)
|
||||
}
|
||||
if len(lyrics.Timeline) != 0 || len(lyrics.Content) != 0 {
|
||||
t.Errorf("Expected empty lyrics for empty file, got %d timeline and %d content",
|
||||
len(lyrics.Timeline), len(lyrics.Content))
|
||||
}
|
||||
|
||||
// Test with metadata only
|
||||
metadataFile := filepath.Join(tempDir, "metadata.lrc")
|
||||
metadataContent := `[ti:Test Title]
|
||||
[ar:Test Artist]
|
||||
[al:Test Album]
|
||||
`
|
||||
if err := os.WriteFile(metadataFile, []byte(metadataContent), 0644); err != nil {
|
||||
t.Fatalf("Failed to create metadata file: %v", err)
|
||||
}
|
||||
|
||||
lyrics, err = Parse(metadataFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed with metadata-only file: %v", err)
|
||||
}
|
||||
if lyrics.Metadata["ti"] != "Test Title" {
|
||||
t.Errorf("Expected title 'Test Title', got '%s'", lyrics.Metadata["ti"])
|
||||
}
|
||||
if len(lyrics.Timeline) != 0 || len(lyrics.Content) != 0 {
|
||||
t.Errorf("Expected empty timeline/content for metadata-only file, got %d timeline and %d content",
|
||||
len(lyrics.Timeline), len(lyrics.Content))
|
||||
}
|
||||
|
||||
// Test with invalid metadata
|
||||
invalidMetadataFile := filepath.Join(tempDir, "invalid_metadata.lrc")
|
||||
invalidMetadata := `[ti:Test Title
|
||||
[ar:Test Artist]
|
||||
[00:01.00]This is a valid line.
|
||||
`
|
||||
if err := os.WriteFile(invalidMetadataFile, []byte(invalidMetadata), 0644); err != nil {
|
||||
t.Fatalf("Failed to create invalid metadata file: %v", err)
|
||||
}
|
||||
|
||||
lyrics, err = Parse(invalidMetadataFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed with invalid metadata file: %v", err)
|
||||
}
|
||||
if lyrics.Metadata["ti"] != "" { // Should ignore invalid metadata
|
||||
t.Errorf("Expected empty title for invalid metadata, got '%s'", lyrics.Metadata["ti"])
|
||||
}
|
||||
if len(lyrics.Timeline) != 1 || len(lyrics.Content) != 1 {
|
||||
t.Errorf("Expected 1 timeline/content entry for file with invalid metadata, got %d timeline and %d content",
|
||||
len(lyrics.Timeline), len(lyrics.Content))
|
||||
}
|
||||
|
||||
// Test with invalid timestamp format
|
||||
invalidFile := filepath.Join(tempDir, "invalid.lrc")
|
||||
content := `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
|
||||
[invalidtime]This should be ignored.
|
||||
[00:01.00]This is a valid line.
|
||||
`
|
||||
if err := os.WriteFile(invalidFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create invalid test file: %v", err)
|
||||
}
|
||||
|
||||
lyrics, err = Parse(invalidFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed on file with invalid timestamps: %v", err)
|
||||
}
|
||||
|
||||
if len(lyrics.Timeline) != 1 || len(lyrics.Content) != 1 {
|
||||
t.Errorf("Expected 1 valid timeline entry, got %d timeline entries and %d content entries",
|
||||
len(lyrics.Timeline), len(lyrics.Content))
|
||||
}
|
||||
|
||||
// Test with timestamp-only lines (no content)
|
||||
timestampOnlyFile := filepath.Join(tempDir, "timestamp_only.lrc")
|
||||
content = `[ti:Test LRC File]
|
||||
[ar:Test Artist]
|
||||
|
||||
[00:01.00]
|
||||
[00:05.00]This has content.
|
||||
`
|
||||
if err := os.WriteFile(timestampOnlyFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to create timestamp-only test file: %v", err)
|
||||
}
|
||||
|
||||
lyrics, err = Parse(timestampOnlyFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse failed on file with timestamp-only lines: %v", err)
|
||||
}
|
||||
|
||||
if len(lyrics.Timeline) != 1 || len(lyrics.Content) != 1 {
|
||||
t.Errorf("Expected 1 valid entry (ignoring empty content), got %d timeline entries and %d content entries",
|
||||
len(lyrics.Timeline), len(lyrics.Content))
|
||||
}
|
||||
}
|
163
internal/format/lrc/utils_test.go
Normal file
163
internal/format/lrc/utils_test.go
Normal file
|
@ -0,0 +1,163 @@
|
|||
package lrc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"sub-cli/internal/model"
|
||||
)
|
||||
|
||||
func TestParseTimestamp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected model.Timestamp
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
name: "Simple minute and second",
|
||||
input: "[01:30]",
|
||||
expected: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 1,
|
||||
Seconds: 30,
|
||||
Milliseconds: 0,
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "With milliseconds",
|
||||
input: "[01:30.500]",
|
||||
expected: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 1,
|
||||
Seconds: 30,
|
||||
Milliseconds: 500,
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "With hours",
|
||||
input: "[01:30:45.500]",
|
||||
expected: model.Timestamp{
|
||||
Hours: 1,
|
||||
Minutes: 30,
|
||||
Seconds: 45,
|
||||
Milliseconds: 500,
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Zero time",
|
||||
input: "[00:00.000]",
|
||||
expected: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 0,
|
||||
Seconds: 0,
|
||||
Milliseconds: 0,
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid format - no brackets",
|
||||
input: "01:30",
|
||||
expected: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 1,
|
||||
Seconds: 30,
|
||||
Milliseconds: 0,
|
||||
},
|
||||
valid: true, // ParseTimestamp automatically strips brackets, so it will parse this without brackets
|
||||
},
|
||||
{
|
||||
name: "Invalid format - wrong brackets",
|
||||
input: "(01:30)",
|
||||
expected: model.Timestamp{},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid format - no time",
|
||||
input: "[]",
|
||||
expected: model.Timestamp{},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid format - text in brackets",
|
||||
input: "[text]",
|
||||
expected: model.Timestamp{},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid format - incomplete time",
|
||||
input: "[01:]",
|
||||
expected: model.Timestamp{},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid format - incomplete time with milliseconds",
|
||||
input: "[01:.500]",
|
||||
expected: model.Timestamp{},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "Metadata tag",
|
||||
input: "[ti:Title]",
|
||||
expected: model.Timestamp{},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "With milliseconds - alternative format using comma",
|
||||
input: "[01:30.500]", // Use period instead of comma since our parser doesn't handle comma
|
||||
expected: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 1,
|
||||
Seconds: 30,
|
||||
Milliseconds: 500,
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "With double-digit milliseconds",
|
||||
input: "[01:30.50]",
|
||||
expected: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 1,
|
||||
Seconds: 30,
|
||||
Milliseconds: 500,
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "With single-digit milliseconds",
|
||||
input: "[01:30.5]",
|
||||
expected: model.Timestamp{
|
||||
Hours: 0,
|
||||
Minutes: 1,
|
||||
Seconds: 30,
|
||||
Milliseconds: 500,
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
timestamp, err := ParseTimestamp(tc.input)
|
||||
|
||||
if (err == nil) != tc.valid {
|
||||
t.Errorf("Expected valid=%v, got valid=%v (err=%v)", tc.valid, err == nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !tc.valid {
|
||||
return // No need to check further for invalid cases
|
||||
}
|
||||
|
||||
if timestamp.Hours != tc.expected.Hours ||
|
||||
timestamp.Minutes != tc.expected.Minutes ||
|
||||
timestamp.Seconds != tc.expected.Seconds ||
|
||||
timestamp.Milliseconds != tc.expected.Milliseconds {
|
||||
t.Errorf("Expected timestamp %+v, got %+v", tc.expected, timestamp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue