Skip to content

Commit

Permalink
Add proper XML-based tests for XMP transformations
Browse files Browse the repository at this point in the history
This gives me a lot more confidence in the output :)
  • Loading branch information
17cupsofcoffee committed May 6, 2024
1 parent a2e8b5a commit 0e00510
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 120 deletions.
10 changes: 10 additions & 0 deletions src/Xmp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ private Xmp(XElement xml)
/// </summary>
public Xmp() : this(XElement.Parse(s_xmlTemplate.Trim())) { }

/// <summary>
/// Loads an XMP document from a string.
/// </summary>
/// <param name="xml">The XML to load.</param>
/// <returns>The loaded XMP document.</returns>
public static Xmp FromString(string xml)
{
return new Xmp(XElement.Parse(xml));
}

/// <summary>
/// Loads an XMP document from the filesystem.
/// </summary>
Expand Down
41 changes: 41 additions & 0 deletions test/TestData/Empty.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.6.0">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ablFR="https://ns.ableton.com/xmp/fs-resources/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
<dc:format>application/vnd.ableton.folder</dc:format>
<ablFR:resource>folder</ablFR:resource>
<ablFR:items>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>bd1.wav</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
<rdf:li>Drums|Kick</rdf:li>
<rdf:li>Creator|17cupsofcoffee</rdf:li>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>bd2.wav</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
<rdf:li>Drums|Kick</rdf:li>
<rdf:li>Creator|17cupsofcoffee</rdf:li>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>ch.wav</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
<rdf:li>Drums|Hihat</rdf:li>
<rdf:li>Drums|Hihat|Closed Hihat</rdf:li>
<rdf:li>Creator|17cupsofcoffee</rdf:li>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
</rdf:Bag>
</ablFR:items>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
34 changes: 34 additions & 0 deletions test/TestData/WithAllTagsRemoved.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.6.0">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ablFR="https://ns.ableton.com/xmp/fs-resources/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
<dc:format>application/vnd.ableton.folder</dc:format>
<ablFR:resource>folder</ablFR:resource>
<ablFR:items>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>bd1.wav</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
<rdf:li>Drums|Kick</rdf:li>
<rdf:li>Creator|17cupsofcoffee</rdf:li>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>bd2.wav</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
<rdf:li>Drums|Kick</rdf:li>
<rdf:li>Creator|17cupsofcoffee</rdf:li>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>ch.wav</ablFR:filePath>
</rdf:li>
</rdf:Bag>
</ablFR:items>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
41 changes: 41 additions & 0 deletions test/TestData/WithTagsAdded.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.6.0">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ablFR="https://ns.ableton.com/xmp/fs-resources/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
<dc:format>application/vnd.ableton.folder</dc:format>
<ablFR:resource>folder</ablFR:resource>
<ablFR:items>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>bd1.wav</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
<rdf:li>Drums|Kick</rdf:li>
<rdf:li>Creator|17cupsofcoffee</rdf:li>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>bd2.wav</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
<rdf:li>Drums|Kick</rdf:li>
<rdf:li>Creator|17cupsofcoffee</rdf:li>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>ch.wav</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
<rdf:li>Drums|Hihat</rdf:li>
<rdf:li>Drums|Hihat|Closed Hihat</rdf:li>
<rdf:li>Creator|17cupsofcoffee</rdf:li>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
</rdf:Bag>
</ablFR:items>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
32 changes: 32 additions & 0 deletions test/TestData/WithTagsRemoved.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.6.0">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ablFR="https://ns.ableton.com/xmp/fs-resources/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
<dc:format>application/vnd.ableton.folder</dc:format>
<ablFR:resource>folder</ablFR:resource>
<ablFR:items>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>bd1.wav</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
<rdf:li>Drums|Kick</rdf:li>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>bd2.wav</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
<rdf:li>Drums|Kick</rdf:li>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<ablFR:filePath>ch.wav</ablFR:filePath>
</rdf:li>
</rdf:Bag>
</ablFR:items>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
18 changes: 18 additions & 0 deletions test/TestUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Runtime.CompilerServices;

namespace LiveTagger.Tests;

/// <summary>
/// Utilities for testing.
///
/// Based on https://www.honlsoft.com/blog/2022-03-26-unit-testing-reading-reference-data/.
/// </summary>
public static class TestUtils
{
public static string ReadFileAsString(string file, [CallerFilePath] string filePath = "")
{
var directoryPath = Path.GetDirectoryName(filePath);
var fullPath = Path.Join(directoryPath, file);
return File.ReadAllText(fullPath);
}
}
137 changes: 17 additions & 120 deletions test/XmpTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,38 +26,10 @@ public void ShouldAddTags()
["Creator|17cupsofcoffee"]
);

var bd1 = getItem(xmp.Xml, "bd1.wav");
var bd2 = getItem(xmp.Xml, "bd2.wav");
var ch = getItem(xmp.Xml, "ch.wav");

// We should only ever have a single item per file.
Assert.Single(bd1);
Assert.Single(bd2);
Assert.Single(ch);

var bd1Tags = getTags(bd1.First());
var bd2Tags = getTags(bd2.First());
var chTags = getTags(ch.First());

Assert.Contains("Drums|Kick", bd1Tags);
Assert.Contains("Creator|17cupsofcoffee", bd1Tags);

Assert.Contains("Drums|Kick", bd2Tags);
Assert.Contains("Creator|17cupsofcoffee", bd2Tags);

Assert.Contains("Drums|Hihat", chTags);
Assert.Contains("Drums|Hihat|Closed Hihat", chTags);
Assert.Contains("Creator|17cupsofcoffee", chTags);
}

[Fact]
public void ShouldMarkDirtyAfterAddTags()
{
var xmp = new Xmp();

xmp.AddTags(
["bd1.wav"],
["Drums|Kick"]
Assert.Equal(
XElement.Parse(TestUtils.ReadFileAsString("TestData/WithTagsAdded.xml")),
xmp.Xml,
XNode.EqualityComparer
);

Assert.True(xmp.IsDirty);
Expand All @@ -66,51 +38,22 @@ public void ShouldMarkDirtyAfterAddTags()
[Fact]
public void ShouldRemoveTags()
{
var xmp = new Xmp();

xmp.AddTags(
["bd.wav", "ch.wav"],
["Drums|Kick", "Drums|Snare"]
);
var xmp = Xmp.FromString(TestUtils.ReadFileAsString("TestData/WithTagsAdded.xml"));

xmp.RemoveTags(
["bd.wav", "ch.wav"],
["Drums|Snare"]
["bd1.wav", "bd2.wav", "ch.wav"],
["Creator|17cupsofcoffee"]
);

xmp.RemoveTags(
["ch.wav"],
["Drums|Kick"]
);

var bd = getItem(xmp.Xml, "bd.wav");
var ch = getItem(xmp.Xml, "ch.wav");

Assert.Single(bd);
Assert.Single(ch);

var bdTags = getTags(bd.First());

Assert.Contains("Drums|Kick", bdTags);
Assert.Null(ch.First().Element(Ableton.Keywords));
}

[Fact]
public void ShouldMarkDirtyAfterRemoveTags()
{
// TODO: This test isn't great, as it's not clear whether AddTags or RemoveTags
// is triggering the flag.

var xmp = new Xmp();

xmp.AddTags(
["bd1.wav"],
["Drums|Kick"]
["Drums|Hihat", "Drums|Hihat|Closed Hihat"]
);

xmp.RemoveTags(
["bd1.wav"],
["Drums|Kick"]
Assert.Equal(
XElement.Parse(TestUtils.ReadFileAsString("TestData/WithTagsRemoved.xml")),
xmp.Xml,
XNode.EqualityComparer
);

Assert.True(xmp.IsDirty);
Expand All @@ -119,64 +62,18 @@ public void ShouldMarkDirtyAfterRemoveTags()
[Fact]
public void ShouldRemoveAllTags()
{
var xmp = new Xmp();

xmp.AddTags(
["bd.wav", "ch.wav"],
["Drums|Kick"]
);
var xmp = Xmp.FromString(TestUtils.ReadFileAsString("TestData/WithTagsAdded.xml"));

xmp.RemoveTags(
["ch.wav"]
);

var bd = getItem(xmp.Xml, "bd.wav");
var ch = getItem(xmp.Xml, "ch.wav");

Assert.Single(bd);
Assert.Single(ch);

var bdTags = getTags(bd.First());

Assert.Contains("Drums|Kick", bdTags);
Assert.Null(ch.First().Element(Ableton.Keywords));
}

[Fact]
public void ShouldMarkDirtyAfterRemoveAllTags()
{
// TODO: This test isn't great, as it's not clear whether AddTags or RemoveTags
// is triggering the flag.

var xmp = new Xmp();

xmp.AddTags(
["bd1.wav"],
["Drums|Kick"]
);

xmp.RemoveTags(
["bd1.wav"]
Assert.Equal(
XElement.Parse(TestUtils.ReadFileAsString("TestData/WithAllTagsRemoved.xml")),
xmp.Xml,
XNode.EqualityComparer
);

Assert.True(xmp.IsDirty);
}

private IEnumerable<XElement> getItem(XElement xml, string file)
{
return xml.Descendants(Ableton.Items)!
.First()!
.Element(Rdf.Bag)!
.Elements(Rdf.Li)!
.Where(e => e.Element(Ableton.FilePath)!.Value == file);
}

private IEnumerable<string> getTags(XElement item)
{
return item
.Element(Ableton.Keywords)!
.Element(Rdf.Bag)!
.Elements(Rdf.Li)
.Select(e => e.Value);
}
}

0 comments on commit 0e00510

Please sign in to comment.