diff --git a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs index 4e545724a..779bc1792 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs @@ -56,9 +56,9 @@ private static Beatmap decode(string filename, out Beatmap encoded) [TestCase(new[] { "[0,start]:1100", "[0,end]:2000", "[1,start]:2100", "[1,end]:3000" }, new double[] { 1100, 2000, 2100, 3000 })] [TestCase(new[] { "[1,end]:3000", "[1,start]:2100", "[0,end]:2000", "[0,start]:1100" }, new double[] { 1100, 2000, 2100, 3000 })] - [TestCase(new[] { "[0,start]:", "[0,start]:", "[0,end]:2000", "[0,start]:1100" }, new double[] { 1100, 2000 })] + [TestCase(new[] { "[0,start]", "[0,start]", "[0,end]:2000", "[0,start]:1100" }, new double[] { 1100, 2000 })] [TestCase(new[] { "[0,start]:1000", "[0,start]:1100", "[0,end]:2000", "[0,start]:1100" }, new double[] { 1000, 2000 })] - [TestCase(new[] { "[0,start]:", "[0,end]:", "[0,start]:", "[1,start]:", "[1,end]:" }, new double[] { })] + [TestCase(new[] { "[0,start]", "[0,end]", "[0,start]", "[1,start]", "[1,end]" }, new double[] { })] [TestCase(new[] { "[0,start]:2000", "[0,end]:1000" }, new double[] { 2000, 2000 })] [TestCase(new[] { "[0,start]:1100", "[0,end]:2100", "[1,start]:2000", "[1,end]:3000" }, new double[] { 1100, 2100, 2100, 3000 })] [TestCase(new[] { "[0,start]:1000", "[0,end]:5000", "[1,start]:2000", "[1,end]:3000" }, new double[] { 1000, 5000, 5000, 5000 })] diff --git a/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckLyricTimeTagTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckLyricTimeTagTest.cs index af8298bf3..040a4b1a0 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckLyricTimeTagTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckLyricTimeTagTest.cs @@ -87,9 +87,9 @@ public void TestCheckOverlapping(string text, string[] timeTags) AssertNotOk(lyric); } - [TestCase("カラオケ", new[] { "[0,start]:", "[3,end]:1000" })] // empty start time-tag time. - [TestCase("カラオケ", new[] { "[0,start]:1000", "[3,end]:" })] // empty end time-tag time. - [TestCase("カラオケ", new[] { "[0,start]:1000", "[1,start]:", "[3,end]:2000" })] // empty center time-tag time. + [TestCase("カラオケ", new[] { "[0,start]", "[3,end]:1000" })] // empty start time-tag time. + [TestCase("カラオケ", new[] { "[0,start]:1000", "[3,end]" })] // empty end time-tag time. + [TestCase("カラオケ", new[] { "[0,start]:1000", "[1,start]", "[3,end]:2000" })] // empty center time-tag time. public void TestCheckEmptyTime(string text, string[] timeTags) { var lyric = new Lyric diff --git a/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckNoteReferenceLyricTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckNoteReferenceLyricTest.cs index 4621bdede..de9714a59 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckNoteReferenceLyricTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckNoteReferenceLyricTest.cs @@ -107,9 +107,9 @@ public void TestCheckMissingStartReferenceTimeTag(int referenceTimeTagIndex, str AssertNotOk(new HitObject[] { referencedLyric, note }); } - [TestCase(0, new[] { "[0,start]:", "[3,end]:5000" })] - [TestCase(0, new[] { "[0,start]:", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]:5000" })] - [TestCase(1, new[] { "[0,start]:1000", "[1,start]:", "[2,start]:3000", "[3,start]:4000", "[3,end]:5000" })] + [TestCase(0, new[] { "[0,start]", "[3,end]:5000" })] + [TestCase(0, new[] { "[0,start]", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]:5000" })] + [TestCase(1, new[] { "[0,start]:1000", "[1,start]", "[2,start]:3000", "[3,start]:4000", "[3,end]:5000" })] public void TestCheckStartReferenceTimeTagMissingTime(int referenceTimeTagIndex, string[] timeTags) { var referencedLyric = new Lyric @@ -146,9 +146,9 @@ public void TestCheckMissingEndReferenceTimeTag(int referenceTimeTagIndex, strin AssertNotOk(new HitObject[] { referencedLyric, note }); } - [TestCase(0, new[] { "[0,start]:1000", "[3,end]:" })] - [TestCase(3, new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]:" })] - [TestCase(2, new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:", "[3,end]:5000" })] + [TestCase(0, new[] { "[0,start]:1000", "[3,end]" })] + [TestCase(3, new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]" })] + [TestCase(2, new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]", "[3,end]:5000" })] public void TestCheckEndReferenceTimeTagMissingTime(int referenceTimeTagIndex, string[] timeTags) { var referencedLyric = new Lyric diff --git a/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckNoteTimeTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckNoteTimeTest.cs index 9e90824b9..194019855 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckNoteTimeTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Editor/Checks/CheckNoteTimeTest.cs @@ -46,8 +46,8 @@ public void TestCheckWithNoReferenceLyric() AssertOk(new HitObject[] { note }); } - [TestCase(3, new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]:" })] // will missing start time-tag. - [TestCase(2, new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:", "[3,end]:5000" })] // will missing end time-tag. + [TestCase(3, new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]" })] // will missing start time-tag. + [TestCase(2, new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]", "[3,end]:5000" })] // will missing end time-tag. public void TestCheckMissingStartOrEndTimeTag(int referenceTimeTagIndex, string[] timeTags) { var referencedLyric = new Lyric @@ -88,17 +88,17 @@ public void TestCheckInvalidReferenceTimeTagTime(string startTimeTag, string end AssertNotOk(new HitObject[] { referencedLyric, note }); } - [TestCase("[0,start]:", "[1,start]:")] - [TestCase("[0,end]:", "[1,end]:")] - [TestCase("[0,start]:", "[1,end]:")] - [TestCase("[0,end]:", "[1,start]:")] - [TestCase("[1,start]:", "[0,start]:")] // should have error even if time-tag index is not sorted. we did not care about the time-tag index in here. + [TestCase("[0,start]", "[1,start]")] + [TestCase("[0,end]", "[1,end]")] + [TestCase("[0,start]", "[1,end]")] + [TestCase("[0,end]", "[1,start]")] + [TestCase("[1,start]", "[0,start]")] // should have error even if time-tag index is not sorted. we did not care about the time-tag index in here. public void TestCheckDurationTooShort(string startTimeTag, string endTimeTag) { var referencedLyric = new Lyric { Text = "カラオケ", - TimeTags = TestCaseTagHelper.ParseTimeTags(new[] { $"{startTimeTag}0", $"{endTimeTag}{MIN_DURATION - 1}" }), + TimeTags = TestCaseTagHelper.ParseTimeTags(new[] { $"{startTimeTag}:0", $"{endTimeTag}:{MIN_DURATION - 1}" }), }; var note = new Note { @@ -110,17 +110,17 @@ public void TestCheckDurationTooShort(string startTimeTag, string endTimeTag) AssertNotOk(new HitObject[] { referencedLyric, note }); } - [TestCase("[0,start]:", "[1,start]:")] - [TestCase("[0,end]:", "[1,end]:")] - [TestCase("[0,start]:", "[1,end]:")] - [TestCase("[0,end]:", "[1,start]:")] - [TestCase("[1,start]:", "[0,start]:")] // should have error even if time-tag index is not sorted. we did not care about the time-tag index in here. + [TestCase("[0,start]", "[1,start]")] + [TestCase("[0,end]", "[1,end]")] + [TestCase("[0,start]", "[1,end]")] + [TestCase("[0,end]", "[1,start]")] + [TestCase("[1,start]", "[0,start]")] // should have error even if time-tag index is not sorted. we did not care about the time-tag index in here. public void TestCheckDurationTooLong(string startTimeTag, string endTimeTag) { var referencedLyric = new Lyric { Text = "カラオケ", - TimeTags = TestCaseTagHelper.ParseTimeTags(new[] { $"{startTimeTag}0", $"{endTimeTag}{MAX_DURATION + 1}" }), + TimeTags = TestCaseTagHelper.ParseTimeTags(new[] { $"{startTimeTag}:0", $"{endTimeTag}:{MAX_DURATION + 1}" }), }; var note = new Note { diff --git a/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/Notes/NoteGeneratorTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/Notes/NoteGeneratorTest.cs index da5fddaf4..49772018c 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/Notes/NoteGeneratorTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/Notes/NoteGeneratorTest.cs @@ -14,10 +14,10 @@ public class NoteGeneratorTest : BaseLyricGeneratorTest x.Checkん.Value = applyConfig); CheckGenerateResult(lyric, expectedTimeTags, config); } - [TestCase("買って", new[] { "[0,start]:", "[2,start]:" }, false)] - [TestCase("買って", new[] { "[0,start]:", "[1,start]:", "[2,start]:" }, true)] + [TestCase("買って", new[] { "[0,start]", "[2,start]" }, false)] + [TestCase("買って", new[] { "[0,start]", "[1,start]", "[2,start]" }, true)] public void TestGenerateWithCheckっ(string lyric, string[] expectedTimeTags, bool applyConfig) { var config = GeneratorEmptyConfig(x => x.Checkっ.Value = applyConfig); @@ -38,31 +38,31 @@ public void TestGenerateWithCheckっ(string lyric, string[] expectedTimeTags, bo } [TestCase(" ", new string[] { }, false)] - [TestCase(" ", new[] { "[0,start]:" }, true)] + [TestCase(" ", new[] { "[0,start]" }, true)] public void TestGenerateWithCheckBlankLine(string lyric, string[] expectedTimeTags, bool applyConfig) { var config = GeneratorEmptyConfig(x => x.CheckBlankLine.Value = applyConfig); CheckGenerateResult(lyric, expectedTimeTags, config); } - [TestCase("か", new[] { "[0,start]:" }, false)] - [TestCase("か", new[] { "[0,start]:", "[0,end]:" }, true)] + [TestCase("か", new[] { "[0,start]" }, false)] + [TestCase("か", new[] { "[0,start]", "[0,end]" }, true)] public void TestGenerateWithCheckLineEndKeyUp(string lyric, string[] expectedTimeTags, bool applyConfig) { var config = GeneratorEmptyConfig(x => x.CheckLineEndKeyUp.Value = applyConfig); CheckGenerateResult(lyric, expectedTimeTags, config); } - [TestCase("か ", new[] { "[0,start]:", "[1,start]:", "[2,start]:", "[3,start]:", "[4,start]:", "[5,start]:" }, false)] - [TestCase("か ", new[] { "[0,start]:", "[1,start]:" }, true)] + [TestCase("か ", new[] { "[0,start]", "[1,start]", "[2,start]", "[3,start]", "[4,start]", "[5,start]" }, false)] + [TestCase("か ", new[] { "[0,start]", "[1,start]" }, true)] public void TestGenerateWithCheckWhiteSpace(string lyric, string[] expectedTimeTags, bool applyConfig) { var config = GeneratorEmptyConfig(x => x.CheckWhiteSpace.Value = applyConfig); CheckGenerateResult(lyric, expectedTimeTags, config); } - [TestCase("か ", new[] { "[0,start]:", "[1,start]:" }, false)] - [TestCase("か ", new[] { "[0,start]:", "[0,end]:" }, true)] + [TestCase("か ", new[] { "[0,start]", "[1,start]" }, false)] + [TestCase("か ", new[] { "[0,start]", "[0,end]" }, true)] public void TestGenerateWithCheckWhiteSpaceKeyUp(string lyric, string[] expectedTimeTags, bool applyConfig) { var config = GeneratorEmptyConfig(x => @@ -73,12 +73,12 @@ public void TestGenerateWithCheckWhiteSpaceKeyUp(string lyric, string[] expected CheckGenerateResult(lyric, expectedTimeTags, config); } - [TestCase("a b c", new[] { "[0,start]:", "[2,start]:", "[4,start]:" }, false, false)] - [TestCase("a b c", new[] { "[0,start]:", "[1,start]:", "[2,start]:", "[3,start]:", "[4,start]:" }, true, false)] - [TestCase("a b c", new[] { "[0,start]:", "[0,end]:", "[2,start]:", "[2,end]:", "[4,start]:" }, true, true)] - [TestCase("A B C", new[] { "[0,start]:", "[2,start]:", "[4,start]:" }, false, false)] - [TestCase("A B C", new[] { "[0,start]:", "[1,start]:", "[2,start]:", "[3,start]:", "[4,start]:" }, true, false)] - [TestCase("A B C", new[] { "[0,start]:", "[0,end]:", "[2,start]:", "[2,end]:", "[4,start]:" }, true, true)] + [TestCase("a b c", new[] { "[0,start]", "[2,start]", "[4,start]" }, false, false)] + [TestCase("a b c", new[] { "[0,start]", "[1,start]", "[2,start]", "[3,start]", "[4,start]" }, true, false)] + [TestCase("a b c", new[] { "[0,start]", "[0,end]", "[2,start]", "[2,end]", "[4,start]" }, true, true)] + [TestCase("A B C", new[] { "[0,start]", "[2,start]", "[4,start]" }, false, false)] + [TestCase("A B C", new[] { "[0,start]", "[1,start]", "[2,start]", "[3,start]", "[4,start]" }, true, false)] + [TestCase("A B C", new[] { "[0,start]", "[0,end]", "[2,start]", "[2,end]", "[4,start]" }, true, true)] public void TestGenerateWithCheckWhiteSpaceAlphabet(string lyric, string[] expectedTimeTags, bool applyConfig, bool keyUp) { var config = GeneratorEmptyConfig(x => @@ -90,12 +90,12 @@ public void TestGenerateWithCheckWhiteSpaceAlphabet(string lyric, string[] expec CheckGenerateResult(lyric, expectedTimeTags, config); } - [TestCase("0 1 2", new[] { "[0,start]:", "[2,start]:", "[4,start]:" }, false, false)] - [TestCase("0 1 2", new[] { "[0,start]:", "[1,start]:", "[2,start]:", "[3,start]:", "[4,start]:" }, true, false)] - [TestCase("0 1 2", new[] { "[0,start]:", "[0,end]:", "[2,start]:", "[2,end]:", "[4,start]:" }, true, true)] - [TestCase("0 1 2", new[] { "[0,start]:", "[2,start]:", "[4,start]:" }, false, false)] - [TestCase("0 1 2", new[] { "[0,start]:", "[1,start]:", "[2,start]:", "[3,start]:", "[4,start]:" }, true, false)] - [TestCase("0 1 2", new[] { "[0,start]:", "[0,end]:", "[2,start]:", "[2,end]:", "[4,start]:" }, true, true)] + [TestCase("0 1 2", new[] { "[0,start]", "[2,start]", "[4,start]" }, false, false)] + [TestCase("0 1 2", new[] { "[0,start]", "[1,start]", "[2,start]", "[3,start]", "[4,start]" }, true, false)] + [TestCase("0 1 2", new[] { "[0,start]", "[0,end]", "[2,start]", "[2,end]", "[4,start]" }, true, true)] + [TestCase("0 1 2", new[] { "[0,start]", "[2,start]", "[4,start]" }, false, false)] + [TestCase("0 1 2", new[] { "[0,start]", "[1,start]", "[2,start]", "[3,start]", "[4,start]" }, true, false)] + [TestCase("0 1 2", new[] { "[0,start]", "[0,end]", "[2,start]", "[2,end]", "[4,start]" }, true, true)] public void TestGenerateWithCheckWhiteSpaceDigit(string lyric, string[] expectedTimeTags, bool applyConfig, bool keyUp) { var config = GeneratorEmptyConfig(x => @@ -107,9 +107,9 @@ public void TestGenerateWithCheckWhiteSpaceDigit(string lyric, string[] expected CheckGenerateResult(lyric, expectedTimeTags, config); } - [TestCase("! ! !", new[] { "[0,start]:", "[2,start]:", "[4,start]:" }, false, false)] - [TestCase("! ! !", new[] { "[0,start]:", "[1,start]:", "[2,start]:", "[3,start]:", "[4,start]:" }, true, false)] - [TestCase("! ! !", new[] { "[0,start]:", "[0,end]:", "[2,start]:", "[2,end]:", "[4,start]:" }, true, true)] + [TestCase("! ! !", new[] { "[0,start]", "[2,start]", "[4,start]" }, false, false)] + [TestCase("! ! !", new[] { "[0,start]", "[1,start]", "[2,start]", "[3,start]", "[4,start]" }, true, false)] + [TestCase("! ! !", new[] { "[0,start]", "[0,end]", "[2,start]", "[2,end]", "[4,start]" }, true, true)] public void TestGenerateWitCheckWhiteSpaceAsciiSymbol(string lyric, string[] expectedTimeTags, bool applyConfig, bool keyUp) { var config = GeneratorEmptyConfig(x => @@ -147,19 +147,19 @@ public void TestGenerateWithRubyLyric() string[] expectedTimeTags = { - "[0,start]:", - "[0,start]:", - "[0,start]:", - "[2,start]:", - "[4,start]:", - "[6,start]:", - "[7,start]:", - "[7,start]:", - "[8,start]:", - "[9,start]:", - "[10,start]:", - "[12,start]:", - "[13,start]:", + "[0,start]", + "[0,start]", + "[0,start]", + "[2,start]", + "[4,start]", + "[6,start]", + "[7,start]", + "[7,start]", + "[8,start]", + "[9,start]", + "[10,start]", + "[12,start]", + "[13,start]", }; CheckGenerateResult(lyric, expectedTimeTags, config); } diff --git a/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/TimeTags/TimeTagGeneratorSelectorTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/TimeTags/TimeTagGeneratorSelectorTest.cs index 21be31de5..4976d77c5 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/TimeTags/TimeTagGeneratorSelectorTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/TimeTags/TimeTagGeneratorSelectorTest.cs @@ -30,9 +30,9 @@ public void TestCanGenerate(int lcid, string text, bool canGenerate) CheckCanGenerate(lyric, canGenerate, selector); } - [TestCase(17, "か", new[] { "[0,start]:", "[0,end]:" })] // Japanese - [TestCase(1041, "か", new[] { "[0,start]:", "[0,end]:" })] // Japanese - [TestCase(1028, "喵", new[] { "[0,start]:" })] // Chinese + [TestCase(17, "か", new[] { "[0,start]", "[0,end]" })] // Japanese + [TestCase(1041, "か", new[] { "[0,start]", "[0,end]" })] // Japanese + [TestCase(1028, "喵", new[] { "[0,start]" })] // Chinese public void TestGenerate(int lcid, string text, string[] expectedTimeTags) { var selector = CreateSelector(); diff --git a/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/TimeTags/Zh/ZhTimeTagGeneratorTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/TimeTags/Zh/ZhTimeTagGeneratorTest.cs index 3d2356610..2fd9ce870 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/TimeTags/Zh/ZhTimeTagGeneratorTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Editor/Generator/Lyrics/TimeTags/Zh/ZhTimeTagGeneratorTest.cs @@ -20,9 +20,9 @@ public void TestCanGenerate(string text, bool canGenerate) CheckCanGenerate(text, canGenerate, config); } - [TestCase("測試一些歌詞", new[] { "[0,start]:", "[1,start]:", "[2,start]:", "[3,start]:", "[4,start]:", "[5,start]:", "[5,end]:" })] - [TestCase("拉拉拉~~~", new[] { "[0,start]:", "[1,start]:", "[2,start]:", "[5,end]:" })] - [TestCase("拉~拉~拉~", new[] { "[0,start]:", "[2,start]:", "[4,start]:", "[5,end]:" })] + [TestCase("測試一些歌詞", new[] { "[0,start]", "[1,start]", "[2,start]", "[3,start]", "[4,start]", "[5,start]", "[5,end]" })] + [TestCase("拉拉拉~~~", new[] { "[0,start]", "[1,start]", "[2,start]", "[5,end]" })] + [TestCase("拉~拉~拉~", new[] { "[0,start]", "[2,start]", "[4,start]", "[5,end]" })] public void TestGenerateWithCheckLineEndKeyUp(string lyric, string[] expectedTimeTags) { var config = GeneratorEmptyConfig(x => x.CheckLineEndKeyUp.Value = true); diff --git a/osu.Game.Rulesets.Karaoke.Tests/Helper/TestCaseTagHelper.cs b/osu.Game.Rulesets.Karaoke.Tests/Helper/TestCaseTagHelper.cs index 5f67d8080..ec29dea40 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Helper/TestCaseTagHelper.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Helper/TestCaseTagHelper.cs @@ -1,6 +1,7 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -18,157 +19,203 @@ namespace osu.Game.Rulesets.Karaoke.Tests.Helper; public static class TestCaseTagHelper { + private const string char_index_range_str = @"\[(?[-0-9]+)(?:,(?[-0-9]+))?\]"; + private const string time_index_regex = @"\[(?[-0-9]+),(?start|end)]"; + private const string time_range_str = @"\[(?[-0-9]+)(?:,(?[-0-9]+))?\]"; + private const string id_str = "(?[-0-9]+)]"; + + private static string getStringPropertyRegex(char prefix, string propertyName) + => @$"(?:{prefix}(?<{propertyName}>[^\s]+))?"; + + private static string getNumberPropertyRegex(char prefix, string propertyName) + => $"(?:{prefix}(?<{propertyName}>[-0-9]+|s*|))?"; + + private static string generateRegex(string regexPrefix, IEnumerable regexProperties) + => regexPrefix + string.Join("", regexProperties); + + private static TObject getMatchByStatement(string? str, string regexStr, Func returnValue) + { + if (string.IsNullOrEmpty(str)) + return returnValue(null); + + var regex = new Regex(regexStr); + var result = regex.Match(str); + if (!result.Success) + throw new RegexMatchTimeoutException(nameof(str)); + + return returnValue(result); + } + /// /// Process test case ruby string format into /// /// - /// [0]:ruby - /// [0,3]:ruby + /// [0]:ruby -> has range index with text.
+ /// [0,3]:ruby -> has range index with text.
+ /// [0]: -> has range index with empty text.
+ /// [0,3]: -> has range index with empty text.
+ /// [0] -> has range index with empty text.
+ /// [0,3] -> has range index with empty text.
///
/// Ruby tag string format /// Ruby tag object public static RubyTag ParseRubyTag(string? str) { - if (string.IsNullOrEmpty(str)) - return new RubyTag(); - - var regex = new Regex("\\[(?[-0-9]+)(?:,(?[-0-9]+))?\\]:(?.*$)"); - var result = regex.Match(str); - if (!result.Success) - throw new RegexMatchTimeoutException(nameof(str)); + string regex = generateRegex(char_index_range_str, new[] + { + getStringPropertyRegex(':', "ruby"), + }); - string startValue = result.Groups["start"].Value; - string endValue = result.Groups["end"].Value; + return getMatchByStatement(str, regex, result => + { + if (result == null) + return new RubyTag(); - int startIndex = int.Parse(startValue); - int endIndex = int.Parse(string.IsNullOrEmpty(endValue) ? startValue : endValue); - string text = result.Groups["ruby"].Value; + int startIndex = result.GetGroupValue("start"); + int? endIndex = result.GetGroupValue("end"); + string text = result.GetGroupValue("ruby"); - return new RubyTag - { - StartIndex = startIndex, - EndIndex = endIndex, - Text = text, - }; + return new RubyTag + { + StartIndex = startIndex, + EndIndex = endIndex ?? startIndex, + Text = text, + }; + }); } /// /// Process test case romaji string format into /// /// - /// [0]:romaji - /// [0,3]:romaji + /// [0]:romaji -> has range index with text.
+ /// [0,3]:romaji -> has range index with text.
+ /// [0]: -> has range index with empty text.
+ /// [0,3]: -> has range index with empty text.
+ /// [0] -> has range index with empty text.
+ /// [0,3] -> has range index with empty text.
///
/// Romaji tag string format /// Romaji tag object public static RomajiTag ParseRomajiTag(string? str) { - if (string.IsNullOrEmpty(str)) - return new RomajiTag(); - - var regex = new Regex("\\[(?[-0-9]+)(?:,(?[-0-9]+))?\\]:(?.*$)"); - var result = regex.Match(str); - if (!result.Success) - throw new RegexMatchTimeoutException(nameof(str)); + string regex = generateRegex(char_index_range_str, new[] + { + getStringPropertyRegex(':', "romaji"), + }); - string startValue = result.Groups["start"].Value; - string endValue = result.Groups["end"].Value; + return getMatchByStatement(str, regex, result => + { + if (result == null) + return new RomajiTag(); - int startIndex = int.Parse(startValue); - int endIndex = int.Parse(string.IsNullOrEmpty(endValue) ? startValue : endValue); - string text = result.Groups["romaji"].Value; + int startIndex = result.GetGroupValue("start"); + int? endIndex = result.GetGroupValue("end"); + string text = result.GetGroupValue("romaji"); - return new RomajiTag - { - StartIndex = startIndex, - EndIndex = endIndex, - Text = text, - }; + return new RomajiTag + { + StartIndex = startIndex, + EndIndex = endIndex ?? startIndex, + Text = text, + }; + }); } /// /// Process test case time tag string format into /// /// - /// [0,start]:1000 + /// [0,start]:1000 -> has time-tag index with time.
+ /// [0,start] -> has time-tag index with no time.
+ /// [0,start]: -> has time-tag index with no time.
///
/// Time tag string format /// Time tag object public static TimeTag ParseTimeTag(string? str) { - if (string.IsNullOrEmpty(str)) - return new TimeTag(new TextIndex()); + string regex = generateRegex(time_index_regex, new[] + { + getNumberPropertyRegex(':', "time"), + }); - var regex = new Regex("(?[-0-9]+),(?start|end)]:(?