Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ Immutable, validated musical value types plus an analysis layer that models harm
```csharp
using ktsu.Semantics.Music;

Progression prog = Progression.Parse("| Dm7 | G7 | Cmaj7 |");
Progression prog = Progression.Parse("4/4 Dm7 / / / | G7 / / / | Cmaj7 / / /");
Key key = prog.InferKey()!; // C major
IReadOnlyList<string> roman = prog.RomanNumerals(key); // ii7, V7, Imaj7

Expand Down
53 changes: 53 additions & 0 deletions Semantics.Test/Music/ReadmeExampleTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) 2023-2026 ktsu-dev contributors

namespace ktsu.Semantics.Test.Music;

using System.Text.RegularExpressions;

using ktsu.Semantics.Music;

/// <summary>
/// Checks the music examples in the READMEs against the library, so that an example a reader copies
/// from the package's front page runs.
/// </summary>
[TestClass]
public partial class ReadmeExampleTests
{
private static readonly string[] Readmes = ["Root.md", "Music.md"];
private static readonly string[] RootExampleNumerals = ["ii7", "V7", "Imaj7"];

private static string ReadmeDirectory => Path.Combine(AppContext.BaseDirectory, "Readmes");

[GeneratedRegex("""Progression\.Parse\("([^"]*)"\)""")]
private static partial Regex ProgressionLiteral();

private static string[] ProgressionLiterals(string readme) =>
[.. ProgressionLiteral().Matches(File.ReadAllText(Path.Combine(ReadmeDirectory, readme))).Select(match => match.Groups[1].Value)];

[TestMethod]
public void EveryProgressionLiteralInTheReadmesParses()
{
foreach (string readme in Readmes)
{
string[] literals = ProgressionLiterals(readme);
Assert.IsNotEmpty(literals, readme);

foreach (string literal in literals)
{
Assert.IsTrue(Progression.TryParse(literal, out _), $"{readme}: \"{literal}\"");
}
}
}

[TestMethod]
public void RootReadmeMusicExample_GivesTheOutputsItsCommentsShow()
{
Progression prog = Progression.Parse(ProgressionLiterals("Root.md")[0]);

Key? key = prog.InferKey();
Assert.IsNotNull(key);
Assert.AreEqual(0, key.Tonic.Value);
Assert.AreEqual(Mode.Major, key.Mode);
Assert.AreSequenceEqual(RootExampleNumerals, prog.RomanNumerals(key));
}
}
7 changes: 7 additions & 0 deletions Semantics.Test/Semantics.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@
CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<!-- The READMEs' code examples are checked against the library, so a parser change that breaks
one fails a test instead of reaching the package's front page. -->
<None Include="..\README.md" Link="Readmes\Root.md" CopyToOutputDirectory="PreserveNewest" />
<None Include="..\Semantics.Music\README.md" Link="Readmes\Music.md" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<!-- The generator bundles its dependencies alongside itself so that they are present when a
compiler loads it as an analyzer. This project loads it as an ordinary assembly instead, so
that bundle arrives as compile references it did not ask for, and the netstandard2.0 builds
Expand Down
Loading