using SongTidy.Services;
namespace SongTidy.Tests;
///
/// Contains tests for .
///
[TestClass]
public sealed class FilenameParserTests {
#region Successful parsing
///
/// Verifies the documented example with one English and one Chinese bracket group.
///
[TestMethod]
public void ParsesSpecificationExample() {
AssertParse("A - B (Live - Tokyo)(修正版)", "A", "B", "Live - Tokyo 修正版");
}
///
/// Verifies that multiple comment groups are joined with a single half-width space.
///
[TestMethod]
public void ParsesMultipleCommentGroups() {
AssertParse("A - B (Live)(Remix)", "A", "B", "Live Remix");
}
///
/// Verifies that Chinese bracket groups are extracted like English bracket groups.
///
[TestMethod]
public void ParsesChineseBracketComment() {
AssertParse("歌手 - 歌名(现场)", "歌手", "歌名", "现场");
}
///
/// Verifies that all comment groups are removed from the text used for artist and title.
///
[TestMethod]
public void RemovesAllCommentGroupsFromSource() {
AssertParse("A - B (Live) (Tokyo)", "A", "B", "Live Tokyo");
}
///
/// Verifies that nested brackets stay as literal text inside the comment.
///
[TestMethod]
public void KeepsNestedBracketsInsideComment() {
AssertParse("A - B (Live (Tokyo) Mix)", "A", "B", "Live (Tokyo) Mix");
}
///
/// Verifies that nested full-width brackets stay as literal text inside an English comment.
///
[TestMethod]
public void PreservesFullWidthNestedBrackets() {
AssertParse("A - B (Live(东京))", "A", "B", "Live(东京)");
}
///
/// Verifies that only the outermost pair of a nested group is treated as the comment.
///
[TestMethod]
public void TreatsOnlyOutermostBracketGroupAsComment() {
AssertParse("A - B ((Live))", "A", "B", "(Live)");
}
///
/// Verifies that a comment can be extracted from a name without a hyphen.
///
[TestMethod]
public void ParsesCommentWithoutArtistSeparator() {
AssertParse("Song (Live)", string.Empty, "Song", "Live");
}
#endregion
#region Invalid bracket structures
///
/// Verifies that an unmatched opening bracket disables comment extraction entirely.
///
[TestMethod]
public void AbandonsExtractionForUnmatchedOpeningBracket() {
AssertParse("A - B (Live", "A", "B (Live", string.Empty);
}
///
/// Verifies that an unmatched closing bracket disables comment extraction entirely.
///
[TestMethod]
public void AbandonsExtractionForUnmatchedClosingBracket() {
AssertParse("A - B Live)", "A", "B Live)", string.Empty);
}
///
/// Verifies that opening and closing brackets of different types cannot pair.
///
[TestMethod]
public void AbandonsExtractionForMismatchedBracketTypes() {
AssertParse("A - B (Live)", "A", "B (Live)", string.Empty);
}
///
/// Verifies that an extra closing bracket after a valid pair disables extraction entirely.
///
[TestMethod]
public void AbandonsExtractionForExtraClosingBracket() {
AssertParse("A - B (Live))", "A", "B (Live))", string.Empty);
}
///
/// Verifies that an extra opening bracket after a valid pair disables extraction entirely.
///
[TestMethod]
public void AbandonsExtractionForExtraOpeningBracket() {
AssertParse("A - B (Live) (", "A", "B (Live) (", string.Empty);
}
///
/// Verifies that an unclosed outer bracket invalidates otherwise complete nested brackets.
///
[TestMethod]
public void AbandonsExtractionForUnclosedOuterBracket() {
AssertParse("A - B (Live (Tokyo)", "A", "B (Live (Tokyo)", string.Empty);
}
///
/// Verifies that crossing bracket groups are treated as an invalid structure.
///
[TestMethod]
public void AbandonsExtractionForCrossingBracketGroups() {
AssertParse("(a(b))", string.Empty, "(a(b))", string.Empty);
}
#endregion
#region Hyphen handling
///
/// Verifies that a single half-width hyphen separates artist from title.
///
[TestMethod]
public void SplitsSingleHalfWidthHyphen() {
AssertParse("A - B", "A", "B", string.Empty);
}
///
/// Verifies that multiple half-width hyphens make the whole remaining text the title.
///
[TestMethod]
public void TreatsMultipleHyphensAsTitleOnly() {
AssertParse("A - B - C", string.Empty, "A - B - C", string.Empty);
}
///
/// Verifies that hyphens inside a removed comment do not count when applying the multiple-hyphen rule.
///
[TestMethod]
public void IgnoresHyphensInsideCommentsWhenSplitting() {
AssertParse("A - B (Live - Tokyo - Mix)", "A", "B", "Live - Tokyo - Mix");
}
///
/// Verifies that a full-width hyphen is not treated as a separator.
///
[TestMethod]
public void DoesNotSplitOnFullWidthHyphen() {
AssertParse("A - B", string.Empty, "A - B", string.Empty);
}
///
/// Verifies that an em dash is not treated as a separator.
///
[TestMethod]
public void DoesNotSplitOnEmDash() {
AssertParse("A — B", string.Empty, "A — B", string.Empty);
}
///
/// Verifies that a name without a hyphen produces an empty artist and a full title.
///
[TestMethod]
public void ReturnsEmptyArtistWhenNoHyphenExists() {
AssertParse("Song Title", string.Empty, "Song Title", string.Empty);
}
///
/// Verifies that an empty input produces three empty fields.
///
[TestMethod]
public void ReturnsEmptyFieldsForEmptyInput() {
AssertParse(string.Empty, string.Empty, string.Empty, string.Empty);
}
#endregion
#region Whitespace handling
///
/// Verifies that standard whitespace around the artist and title is removed.
///
[TestMethod]
public void TrimsStandardWhitespaceAroundArtistAndTitle() {
AssertParse(" A - B ", "A", "B", string.Empty);
}
///
/// Verifies that standard whitespace around extracted comment text is removed.
///
[TestMethod]
public void TrimsStandardWhitespaceInsideComments() {
AssertParse("A - B ( Live )", "A", "B", "Live");
}
///
/// Verifies that full-width spaces inside comment text are preserved.
///
[TestMethod]
public void PreservesFullWidthSpacesInsideComments() {
AssertParse("A - B (\u3000Live\u3000)", "A", "B", "\u3000Live\u3000");
}
///
/// Verifies that full-width spaces around artist and title are preserved.
///
[TestMethod]
public void PreservesFullWidthSpacesAroundFields() {
AssertParse("A\u3000-\u3000B", "A\u3000", "\u3000B", string.Empty);
}
#endregion
///
/// Parses the input and asserts all three resulting fields.
///
/// The file name to parse.
/// The expected artist value.
/// The expected title value.
/// The expected comment value.
private static void AssertParse(
string input,
string expectedArtist,
string expectedTitle,
string expectedComment) {
FilenameParseResult result = FilenameParser.Parse(input);
Assert.AreEqual(expectedArtist, result.Artist, "Artist mismatch.");
Assert.AreEqual(expectedTitle, result.Title, "Title mismatch.");
Assert.AreEqual(expectedComment, result.Comment, "Comment mismatch.");
}
}