diff --git a/LrcParser.Tests/Parser/Kar/Lines/KarLyricParserTest.cs b/LrcParser.Tests/Parser/Kar/Lines/KarLyricParserTest.cs index f5916bb..393b065 100644 --- a/LrcParser.Tests/Parser/Kar/Lines/KarLyricParserTest.cs +++ b/LrcParser.Tests/Parser/Kar/Lines/KarLyricParserTest.cs @@ -1,6 +1,7 @@ // Copyright (c) karaoke.dev . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using LrcParser.Parser.Kar.Lines; using LrcParser.Parser.Kar.Metadata; using LrcParser.Tests.Helper; @@ -22,39 +23,115 @@ public void TestCanDecode(string text, bool expected) Assert.That(actual, Is.EqualTo(expected)); } - [TestCase("[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]", "帰り道は", new[] { "[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220" })] - [TestCase("帰[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]", "帰り道は", new[] { "[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220" })] - [TestCase("[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は", "帰り道は", new[] { "[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940" })] - [TestCase("帰り道は", "帰り道は", new string[] { })] - [TestCase("", "", new string[] { })] - [TestCase(null, "", new string[] { })] - public void TestDecode(string lyric, string text, string[] timeTags) + [TestCaseSource(nameof(testDecodeSource))] + public void TestDecode(string lyric, KarLyric expected) { - var expected = new KarLyric - { - Text = text, - TimeTags = TestCaseTagHelper.ParseTimeTags(timeTags), - }; var actual = Decode(lyric); - Assert.That(actual.Text, Is.EqualTo(expected.Text)); - Assert.That(actual.TimeTags, Is.EqualTo(expected.TimeTags)); + Assert.That(actual, Is.EqualTo(expected)); } - [TestCase("帰り道は", new[] { "[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220" }, "[00:17.97]帰[00:18.37]り[00:18.55]道[00:18.94]は[00:19.22]")] - [TestCase("帰り道は", new[] { "[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220" }, "帰[00:18.37]り[00:18.55]道[00:18.94]は[00:19.22]")] - [TestCase("帰り道は", new[] { "[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940" }, "[00:17.97]帰[00:18.37]り[00:18.55]道[00:18.94]は")] - [TestCase("帰り道は", new string[] { }, "帰り道は")] - [TestCase("", new string[] { }, "")] - public void TestEncode(string text, string[] timeTags, string expected) + private static IEnumerable testDecodeSource => new object[][] + { + [ + "[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]", + new KarLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]), + }, + ], + [ + "帰[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]", + new KarLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]), + }, + ], + [ + "[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は", + new KarLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940"]), + }, + ], + [ + "帰り道は", + new KarLyric + { + Text = "帰り道は", + TimeTags = [], + }, + ], + [ + "", + new KarLyric + { + Text = "", + TimeTags = [], + }, + ], + [ + null!, + new KarLyric + { + Text = "", + TimeTags = [], + }, + ], + }; + + [TestCaseSource(nameof(testEncodeSource))] + public void TestEncode(KarLyric lyric, string expected) { - var lyric = new KarLyric - { - Text = text, - TimeTags = TestCaseTagHelper.ParseTimeTags(timeTags), - }; var actual = Encode(lyric); Assert.That(actual, Is.EqualTo(expected)); } + + private static IEnumerable testEncodeSource => new object[][] + { + [ + new KarLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]), + }, + "[00:17.97]帰[00:18.37]り[00:18.55]道[00:18.94]は[00:19.22]", + ], + [ + new KarLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]), + }, + "帰[00:18.37]り[00:18.55]道[00:18.94]は[00:19.22]", + ], + [ + new KarLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940"]), + }, + "[00:17.97]帰[00:18.37]り[00:18.55]道[00:18.94]は", + ], + [ + new KarLyric + { + Text = "帰り道は", + TimeTags = [], + }, + "帰り道は", + ], + [ + new KarLyric + { + Text = "", + TimeTags = [], + }, + "", + ], + }; } diff --git a/LrcParser.Tests/Parser/Kar/Lines/KarRubyParserTest.cs b/LrcParser.Tests/Parser/Kar/Lines/KarRubyParserTest.cs index bdab6d9..ec9f5ab 100644 --- a/LrcParser.Tests/Parser/Kar/Lines/KarRubyParserTest.cs +++ b/LrcParser.Tests/Parser/Kar/Lines/KarRubyParserTest.cs @@ -1,6 +1,8 @@ // Copyright (c) karaoke.dev . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using LrcParser.Parser.Kar.Lines; using LrcParser.Parser.Kar.Metadata; using LrcParser.Tests.Helper; @@ -22,47 +24,137 @@ public void TestCanDecode(string text, bool expected) Assert.That(actual, Is.EqualTo(expected)); } - [TestCase("@Ruby1=帰,かえ,[00:53:19],[01:24:77]", "帰", "かえ", new string[] { }, 53190, 84770)] - [TestCase("@Ruby1=帰,かえ,[01:24:77]", "帰", "かえ", new string[] { }, 84770, null)] - [TestCase("@Ruby1=帰,かえ,,[01:24:77]", "帰", "かえ", new string[] { }, null, 84770)] - [TestCase("@Ruby1=帰,かえ", "帰", "かえ", new string[] { }, null, null)] - [TestCase("@Ruby1=帰,か[00:00:50]え", "帰", "かえ", new[] { "[1,start]:500" }, null, null)] - public void TestDecode(string rubyTag, string parent, string ruby, string[] timeTags, int? startTime, int? endTime) + [TestCaseSource(nameof(testDecodeSource))] + public void TestDecode(string rubyTag, KarRuby expected) { - var expected = new KarRuby - { - Parent = parent, - Ruby = ruby, - TimeTags = TestCaseTagHelper.ParseTimeTags(timeTags), - StartTime = startTime, - EndTime = endTime, - }; var actual = Decode(rubyTag); - Assert.That(actual.Ruby, Is.EqualTo(expected.Ruby)); - Assert.That(actual.Parent, Is.EqualTo(expected.Parent)); - Assert.That(actual.TimeTags, Is.EqualTo(expected.TimeTags)); - Assert.That(actual.StartTime, Is.EqualTo(expected.StartTime)); - Assert.That(actual.EndTime, Is.EqualTo(expected.EndTime)); + Assert.That(actual, Is.EqualTo(expected)); } - [TestCase("帰", "かえ", new string[] { }, 53190, 84770, "@Ruby1=帰,かえ,[00:53.19],[01:24.77]")] - [TestCase("帰", "かえ", new string[] { }, 84770, null, "@Ruby1=帰,かえ,[01:24.77]")] - [TestCase("帰", "かえ", new string[] { }, null, 84770, "@Ruby1=帰,かえ,,[01:24.77]")] - [TestCase("帰", "かえ", new string[] { }, null, null, "@Ruby1=帰,かえ")] - [TestCase("帰", "かえ", new[] { "[1,start]:500" }, null, null, "@Ruby1=帰,か[00:00.50]え")] - public void TestEncode(string parent, string ruby, string[] timeTags, int? startTime, int? endTime, string expected) + private static IEnumerable testDecodeSource => new object[][] + { + [ + "@Ruby1=帰,かえ,[00:53:19],[01:24:77]", + new KarRuby + { + Parent = "帰", + Ruby = "かえ", + TimeTags = TestCaseTagHelper.ParseTimeTags(Array.Empty()), + StartTime = 53190, + EndTime = 84770, + }, + ], + [ + "@Ruby1=帰,かえ,[01:24:77]", + new KarRuby + { + Parent = "帰", + Ruby = "かえ", + TimeTags = TestCaseTagHelper.ParseTimeTags(Array.Empty()), + StartTime = 84770, + EndTime = null, + }, + ], + [ + "@Ruby1=帰,かえ,,[01:24:77]", + new KarRuby + { + Parent = "帰", + Ruby = "かえ", + TimeTags = TestCaseTagHelper.ParseTimeTags(Array.Empty()), + StartTime = null, + EndTime = 84770, + }, + ], + [ + "@Ruby1=帰,かえ", + new KarRuby + { + Parent = "帰", + Ruby = "かえ", + TimeTags = TestCaseTagHelper.ParseTimeTags(Array.Empty()), + StartTime = null, + EndTime = null, + }, + ], + [ + "@Ruby1=帰,か[00:00:50]え", + new KarRuby + { + Parent = "帰", + Ruby = "かえ", + TimeTags = TestCaseTagHelper.ParseTimeTags(new[] { "[1,start]:500" }), + StartTime = null, + EndTime = null, + }, + ], + }; + + [TestCaseSource(nameof(testEncodeSources))] + public void TestEncode(KarRuby rubyTag, string expected) { - var rubyTag = new KarRuby - { - Parent = parent, - Ruby = ruby, - TimeTags = TestCaseTagHelper.ParseTimeTags(timeTags), - StartTime = startTime, - EndTime = endTime, - }; var actual = Encode(rubyTag); Assert.That(actual, Is.EqualTo(expected)); } + + private static IEnumerable testEncodeSources => new object[][] + { + [ + new KarRuby + { + Parent = "帰", + Ruby = "かえ", + TimeTags = [], + StartTime = 53190, + EndTime = 84770, + }, + "@Ruby1=帰,かえ,[00:53.19],[01:24.77]", + ], + [ + new KarRuby + { + Parent = "帰", + Ruby = "かえ", + TimeTags = [], + StartTime = 84770, + EndTime = null, + }, + "@Ruby1=帰,かえ,[01:24.77]", + ], + [ + new KarRuby + { + Parent = "帰", + Ruby = "かえ", + TimeTags = [], + StartTime = null, + EndTime = 84770, + }, + "@Ruby1=帰,かえ,,[01:24.77]", + ], + [ + new KarRuby + { + Parent = "帰", + Ruby = "かえ", + TimeTags = [], + StartTime = null, + EndTime = null, + }, + "@Ruby1=帰,かえ", + ], + [ + new KarRuby + { + Parent = "帰", + Ruby = "かえ", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[1,start]:500"]), + StartTime = null, + EndTime = null, + }, + "@Ruby1=帰,か[00:00.50]え", + ], + }; } diff --git a/LrcParser.Tests/Parser/Lines/BaseSingleLineParserTest.cs b/LrcParser.Tests/Parser/Lines/BaseSingleLineParserTest.cs index a73facc..08b0386 100644 --- a/LrcParser.Tests/Parser/Lines/BaseSingleLineParserTest.cs +++ b/LrcParser.Tests/Parser/Lines/BaseSingleLineParserTest.cs @@ -1,11 +1,13 @@ // Copyright (c) karaoke.dev . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using LrcParser.Parser.Lines; namespace LrcParser.Tests.Parser.Lines; -public class BaseSingleLineParserTest where TParser : SingleLineParser, new() where TModel : class +public class BaseSingleLineParserTest + where TParser : SingleLineParser, new() where TModel : struct, IEquatable { protected bool CanDecode(string text) => new TParser().CanDecode(text); diff --git a/LrcParser.Tests/Parser/Lrc/Lines/LrcLyricParserTest.cs b/LrcParser.Tests/Parser/Lrc/Lines/LrcLyricParserTest.cs index 517f38f..333bf3e 100644 --- a/LrcParser.Tests/Parser/Lrc/Lines/LrcLyricParserTest.cs +++ b/LrcParser.Tests/Parser/Lrc/Lines/LrcLyricParserTest.cs @@ -1,6 +1,7 @@ // Copyright (c) karaoke.dev . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using LrcParser.Parser.Lrc.Lines; using LrcParser.Parser.Lrc.Metadata; using LrcParser.Tests.Helper; @@ -15,46 +16,121 @@ public class LrcLyricParserTest : BaseSingleLineParserTest testDecodeSource => new object[][] + { + [ + "[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]", + new LrcLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]), + }, + ], + [ + "帰[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]", + new LrcLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]), + }, + ], + [ + "[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は", + new LrcLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940"]), + }, + ], + [ + "帰り道は", + new LrcLyric + { + Text = "帰り道は", + TimeTags = [], + }, + ], + [ + "", + new LrcLyric + { + Text = "", + TimeTags = [], + }, + ], + [ + null!, + new LrcLyric + { + Text = "", + TimeTags = [], + }, + ], + }; + + [TestCaseSource(nameof(testEncodeSource))] + public void TestEncode(LrcLyric lyric, string expected) { - var lyric = new LrcLyric - { - Text = text, - TimeTags = TestCaseTagHelper.ParseTimeTags(timeTags), - }; var actual = Encode(lyric); Assert.That(actual, Is.EqualTo(expected)); } + + private static IEnumerable testEncodeSource => new object[][] + { + [ + new LrcLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]), + }, + "[00:17.97]帰[00:18.37]り[00:18.55]道[00:18.94]は[00:19.22]", + ], + [ + new LrcLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]), + }, + "帰[00:18.37]り[00:18.55]道[00:18.94]は[00:19.22]", + ], + [ + new LrcLyric + { + Text = "帰り道は", + TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940"]), + }, + "[00:17.97]帰[00:18.37]り[00:18.55]道[00:18.94]は", + ], + [ + new LrcLyric + { + Text = "帰り道は", + TimeTags = [], + }, + "帰り道は", + ], + [ + new LrcLyric + { + Text = "", + TimeTags = [], + }, + "", + ], + }; } diff --git a/LrcParser.Tests/Parser/LyricParserTest.cs b/LrcParser.Tests/Parser/LyricParserTest.cs index 8664f3e..7581301 100644 --- a/LrcParser.Tests/Parser/LyricParserTest.cs +++ b/LrcParser.Tests/Parser/LyricParserTest.cs @@ -1,6 +1,7 @@ // Copyright (c) karaoke.dev . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using LrcParser.Model; @@ -45,13 +46,13 @@ public TestLyricParser() protected override Song PostProcess(List values) { - var lines = values.OfType(); + var lines = values.OfType(); return new Song { Lyrics = lines.Select(l => new Lyric { - Text = l, + Text = l.Text, }).ToList(), }; } @@ -60,14 +61,31 @@ protected override IEnumerable PreProcess(Song song) => song.Lyrics.Select(lyric => lyric.Text); } - private class TestLineParser : SingleLineParser + private class TestLineParser : SingleLineParser { public override bool CanDecode(string text) => true; - public override string Decode(string text) => text; + public override Line Decode(string text) => new() + { + Text = text, + }; + + public override string Encode(Line component, int index) + => $"index:{index}, value: {component.Text}"; + } + + private struct Line : IEquatable + { + public Line() + { + } + + public string Text { get; set; } = string.Empty; - public override string Encode(string component, int index) - => $"index:{index}, value: {component}"; + public bool Equals(Line other) + { + return Text == other.Text; + } } } diff --git a/LrcParser/Model/Lyric.cs b/LrcParser/Model/Lyric.cs index e4259f1..6be44bb 100644 --- a/LrcParser/Model/Lyric.cs +++ b/LrcParser/Model/Lyric.cs @@ -8,7 +8,7 @@ public class Lyric /// /// Text /// - public string Text { get; set; } = ""; + public string Text { get; set; } = string.Empty; /// /// Time tags diff --git a/LrcParser/Model/RubyTag.cs b/LrcParser/Model/RubyTag.cs index c1e0daf..7a91133 100644 --- a/LrcParser/Model/RubyTag.cs +++ b/LrcParser/Model/RubyTag.cs @@ -5,7 +5,7 @@ namespace LrcParser.Model; public class RubyTag { - public string Text { get; set; } = ""; + public string Text { get; set; } = string.Empty; /// /// Time tags diff --git a/LrcParser/Parser/Kar/Metadata/KarLyric.cs b/LrcParser/Parser/Kar/Metadata/KarLyric.cs index 997b4f7..0142a0e 100644 --- a/LrcParser/Parser/Kar/Metadata/KarLyric.cs +++ b/LrcParser/Parser/Kar/Metadata/KarLyric.cs @@ -5,15 +5,24 @@ namespace LrcParser.Parser.Kar.Metadata; -public class KarLyric +public struct KarLyric : IEquatable { + public KarLyric() + { + } + /// /// Text /// - public string Text { get; set; } = ""; + public string Text { get; set; } = string.Empty; /// /// Time tags /// public SortedDictionary TimeTags { get; set; } = new(); + + public bool Equals(KarLyric other) + { + return Text == other.Text && TimeTags.SequenceEqual(other.TimeTags); + } } diff --git a/LrcParser/Parser/Kar/Metadata/KarRuby.cs b/LrcParser/Parser/Kar/Metadata/KarRuby.cs index fcc4470..3a9f962 100644 --- a/LrcParser/Parser/Kar/Metadata/KarRuby.cs +++ b/LrcParser/Parser/Kar/Metadata/KarRuby.cs @@ -13,17 +13,21 @@ namespace LrcParser.Parser.Kar.Metadata; /// @Ruby25=時,じか,,[00:38:45] /// @Ruby49=時,とき,[00:38:45],[01:04:49] /// -public class KarRuby +public struct KarRuby : IEquatable { + public KarRuby() + { + } + /// /// Parent kanji /// - public string Parent { get; set; } = ""; + public string Parent { get; set; } = string.Empty; /// /// Ruby /// - public string Ruby { get; set; } = ""; + public string Ruby { get; set; } = string.Empty; /// /// Time tags @@ -33,10 +37,19 @@ public class KarRuby /// /// Start position /// - public int? StartTime { get; set; } + public int? StartTime { get; set; } = null; /// /// End position /// - public int? EndTime { get; set; } + public int? EndTime { get; set; } = null; + + public bool Equals(KarRuby other) + { + return Parent == other.Parent + && Ruby == other.Ruby + && TimeTags.SequenceEqual(other.TimeTags) + && StartTime == other.StartTime + && EndTime == other.EndTime; + } } diff --git a/LrcParser/Parser/Kar/Utils/KarTimedTextUtils.cs b/LrcParser/Parser/Kar/Utils/KarTimedTextUtils.cs index 78cd695..1d6dffb 100644 --- a/LrcParser/Parser/Kar/Utils/KarTimedTextUtils.cs +++ b/LrcParser/Parser/Kar/Utils/KarTimedTextUtils.cs @@ -21,7 +21,7 @@ internal static Tuple> TimedTextToObjec var startIndex = 0; - var text = ""; + var text = string.Empty; var timeTags = new SortedDictionary(); foreach (var match in matchTimeTags.ToArray()) diff --git a/LrcParser/Parser/Lines/SingleLineParser.cs b/LrcParser/Parser/Lines/SingleLineParser.cs index 82fd333..f8f8718 100644 --- a/LrcParser/Parser/Lines/SingleLineParser.cs +++ b/LrcParser/Parser/Lines/SingleLineParser.cs @@ -7,7 +7,7 @@ namespace LrcParser.Parser.Lines; /// Base component pass string /// /// Encode and decode object type -public abstract class SingleLineParser : ISingleLineParser where T : class +public abstract class SingleLineParser : ISingleLineParser where T : struct, IEquatable { public abstract bool CanDecode(string text); diff --git a/LrcParser/Parser/Lrc/Metadata/LrcLyric.cs b/LrcParser/Parser/Lrc/Metadata/LrcLyric.cs index 21f3aa9..3d12e20 100644 --- a/LrcParser/Parser/Lrc/Metadata/LrcLyric.cs +++ b/LrcParser/Parser/Lrc/Metadata/LrcLyric.cs @@ -5,15 +5,24 @@ namespace LrcParser.Parser.Lrc.Metadata; -public class LrcLyric +public struct LrcLyric : IEquatable { + public LrcLyric() + { + } + /// /// Text /// - public string Text { get; set; } = ""; + public string Text { get; set; } = string.Empty; /// /// Time tags /// public SortedDictionary TimeTags { get; set; } = new(); + + public bool Equals(LrcLyric other) + { + return Text == other.Text && TimeTags.SequenceEqual(other.TimeTags); + } } diff --git a/LrcParser/Parser/Lrc/Utils/LrcTimedTextUtils.cs b/LrcParser/Parser/Lrc/Utils/LrcTimedTextUtils.cs index 5c27775..6ce012b 100644 --- a/LrcParser/Parser/Lrc/Utils/LrcTimedTextUtils.cs +++ b/LrcParser/Parser/Lrc/Utils/LrcTimedTextUtils.cs @@ -21,7 +21,7 @@ internal static Tuple> TimedTextToObjec var startIndex = 0; - var text = ""; + var text = string.Empty; var timeTags = new SortedDictionary(); foreach (var match in matchTimeTags.ToArray())