Skip to content

Commit 0e00510

Browse files
Add proper XML-based tests for XMP transformations
This gives me a lot more confidence in the output :)
1 parent a2e8b5a commit 0e00510

7 files changed

Lines changed: 193 additions & 120 deletions

File tree

src/Xmp.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ private Xmp(XElement xml)
5858
/// </summary>
5959
public Xmp() : this(XElement.Parse(s_xmlTemplate.Trim())) { }
6060

61+
/// <summary>
62+
/// Loads an XMP document from a string.
63+
/// </summary>
64+
/// <param name="xml">The XML to load.</param>
65+
/// <returns>The loaded XMP document.</returns>
66+
public static Xmp FromString(string xml)
67+
{
68+
return new Xmp(XElement.Parse(xml));
69+
}
70+
6171
/// <summary>
6272
/// Loads an XMP document from the filesystem.
6373
/// </summary>

test/TestData/Empty.xml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.6.0">
3+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
4+
<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/">
5+
<dc:format>application/vnd.ableton.folder</dc:format>
6+
<ablFR:resource>folder</ablFR:resource>
7+
<ablFR:items>
8+
<rdf:Bag>
9+
<rdf:li rdf:parseType="Resource">
10+
<ablFR:filePath>bd1.wav</ablFR:filePath>
11+
<ablFR:keywords>
12+
<rdf:Bag>
13+
<rdf:li>Drums|Kick</rdf:li>
14+
<rdf:li>Creator|17cupsofcoffee</rdf:li>
15+
</rdf:Bag>
16+
</ablFR:keywords>
17+
</rdf:li>
18+
<rdf:li rdf:parseType="Resource">
19+
<ablFR:filePath>bd2.wav</ablFR:filePath>
20+
<ablFR:keywords>
21+
<rdf:Bag>
22+
<rdf:li>Drums|Kick</rdf:li>
23+
<rdf:li>Creator|17cupsofcoffee</rdf:li>
24+
</rdf:Bag>
25+
</ablFR:keywords>
26+
</rdf:li>
27+
<rdf:li rdf:parseType="Resource">
28+
<ablFR:filePath>ch.wav</ablFR:filePath>
29+
<ablFR:keywords>
30+
<rdf:Bag>
31+
<rdf:li>Drums|Hihat</rdf:li>
32+
<rdf:li>Drums|Hihat|Closed Hihat</rdf:li>
33+
<rdf:li>Creator|17cupsofcoffee</rdf:li>
34+
</rdf:Bag>
35+
</ablFR:keywords>
36+
</rdf:li>
37+
</rdf:Bag>
38+
</ablFR:items>
39+
</rdf:Description>
40+
</rdf:RDF>
41+
</x:xmpmeta>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.6.0">
3+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
4+
<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/">
5+
<dc:format>application/vnd.ableton.folder</dc:format>
6+
<ablFR:resource>folder</ablFR:resource>
7+
<ablFR:items>
8+
<rdf:Bag>
9+
<rdf:li rdf:parseType="Resource">
10+
<ablFR:filePath>bd1.wav</ablFR:filePath>
11+
<ablFR:keywords>
12+
<rdf:Bag>
13+
<rdf:li>Drums|Kick</rdf:li>
14+
<rdf:li>Creator|17cupsofcoffee</rdf:li>
15+
</rdf:Bag>
16+
</ablFR:keywords>
17+
</rdf:li>
18+
<rdf:li rdf:parseType="Resource">
19+
<ablFR:filePath>bd2.wav</ablFR:filePath>
20+
<ablFR:keywords>
21+
<rdf:Bag>
22+
<rdf:li>Drums|Kick</rdf:li>
23+
<rdf:li>Creator|17cupsofcoffee</rdf:li>
24+
</rdf:Bag>
25+
</ablFR:keywords>
26+
</rdf:li>
27+
<rdf:li rdf:parseType="Resource">
28+
<ablFR:filePath>ch.wav</ablFR:filePath>
29+
</rdf:li>
30+
</rdf:Bag>
31+
</ablFR:items>
32+
</rdf:Description>
33+
</rdf:RDF>
34+
</x:xmpmeta>

test/TestData/WithTagsAdded.xml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.6.0">
3+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
4+
<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/">
5+
<dc:format>application/vnd.ableton.folder</dc:format>
6+
<ablFR:resource>folder</ablFR:resource>
7+
<ablFR:items>
8+
<rdf:Bag>
9+
<rdf:li rdf:parseType="Resource">
10+
<ablFR:filePath>bd1.wav</ablFR:filePath>
11+
<ablFR:keywords>
12+
<rdf:Bag>
13+
<rdf:li>Drums|Kick</rdf:li>
14+
<rdf:li>Creator|17cupsofcoffee</rdf:li>
15+
</rdf:Bag>
16+
</ablFR:keywords>
17+
</rdf:li>
18+
<rdf:li rdf:parseType="Resource">
19+
<ablFR:filePath>bd2.wav</ablFR:filePath>
20+
<ablFR:keywords>
21+
<rdf:Bag>
22+
<rdf:li>Drums|Kick</rdf:li>
23+
<rdf:li>Creator|17cupsofcoffee</rdf:li>
24+
</rdf:Bag>
25+
</ablFR:keywords>
26+
</rdf:li>
27+
<rdf:li rdf:parseType="Resource">
28+
<ablFR:filePath>ch.wav</ablFR:filePath>
29+
<ablFR:keywords>
30+
<rdf:Bag>
31+
<rdf:li>Drums|Hihat</rdf:li>
32+
<rdf:li>Drums|Hihat|Closed Hihat</rdf:li>
33+
<rdf:li>Creator|17cupsofcoffee</rdf:li>
34+
</rdf:Bag>
35+
</ablFR:keywords>
36+
</rdf:li>
37+
</rdf:Bag>
38+
</ablFR:items>
39+
</rdf:Description>
40+
</rdf:RDF>
41+
</x:xmpmeta>

test/TestData/WithTagsRemoved.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.6.0">
3+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
4+
<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/">
5+
<dc:format>application/vnd.ableton.folder</dc:format>
6+
<ablFR:resource>folder</ablFR:resource>
7+
<ablFR:items>
8+
<rdf:Bag>
9+
<rdf:li rdf:parseType="Resource">
10+
<ablFR:filePath>bd1.wav</ablFR:filePath>
11+
<ablFR:keywords>
12+
<rdf:Bag>
13+
<rdf:li>Drums|Kick</rdf:li>
14+
</rdf:Bag>
15+
</ablFR:keywords>
16+
</rdf:li>
17+
<rdf:li rdf:parseType="Resource">
18+
<ablFR:filePath>bd2.wav</ablFR:filePath>
19+
<ablFR:keywords>
20+
<rdf:Bag>
21+
<rdf:li>Drums|Kick</rdf:li>
22+
</rdf:Bag>
23+
</ablFR:keywords>
24+
</rdf:li>
25+
<rdf:li rdf:parseType="Resource">
26+
<ablFR:filePath>ch.wav</ablFR:filePath>
27+
</rdf:li>
28+
</rdf:Bag>
29+
</ablFR:items>
30+
</rdf:Description>
31+
</rdf:RDF>
32+
</x:xmpmeta>

test/TestUtils.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Runtime.CompilerServices;
2+
3+
namespace LiveTagger.Tests;
4+
5+
/// <summary>
6+
/// Utilities for testing.
7+
///
8+
/// Based on https://www.honlsoft.com/blog/2022-03-26-unit-testing-reading-reference-data/.
9+
/// </summary>
10+
public static class TestUtils
11+
{
12+
public static string ReadFileAsString(string file, [CallerFilePath] string filePath = "")
13+
{
14+
var directoryPath = Path.GetDirectoryName(filePath);
15+
var fullPath = Path.Join(directoryPath, file);
16+
return File.ReadAllText(fullPath);
17+
}
18+
}

test/XmpTests.cs

Lines changed: 17 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -26,38 +26,10 @@ public void ShouldAddTags()
2626
["Creator|17cupsofcoffee"]
2727
);
2828

29-
var bd1 = getItem(xmp.Xml, "bd1.wav");
30-
var bd2 = getItem(xmp.Xml, "bd2.wav");
31-
var ch = getItem(xmp.Xml, "ch.wav");
32-
33-
// We should only ever have a single item per file.
34-
Assert.Single(bd1);
35-
Assert.Single(bd2);
36-
Assert.Single(ch);
37-
38-
var bd1Tags = getTags(bd1.First());
39-
var bd2Tags = getTags(bd2.First());
40-
var chTags = getTags(ch.First());
41-
42-
Assert.Contains("Drums|Kick", bd1Tags);
43-
Assert.Contains("Creator|17cupsofcoffee", bd1Tags);
44-
45-
Assert.Contains("Drums|Kick", bd2Tags);
46-
Assert.Contains("Creator|17cupsofcoffee", bd2Tags);
47-
48-
Assert.Contains("Drums|Hihat", chTags);
49-
Assert.Contains("Drums|Hihat|Closed Hihat", chTags);
50-
Assert.Contains("Creator|17cupsofcoffee", chTags);
51-
}
52-
53-
[Fact]
54-
public void ShouldMarkDirtyAfterAddTags()
55-
{
56-
var xmp = new Xmp();
57-
58-
xmp.AddTags(
59-
["bd1.wav"],
60-
["Drums|Kick"]
29+
Assert.Equal(
30+
XElement.Parse(TestUtils.ReadFileAsString("TestData/WithTagsAdded.xml")),
31+
xmp.Xml,
32+
XNode.EqualityComparer
6133
);
6234

6335
Assert.True(xmp.IsDirty);
@@ -66,51 +38,22 @@ public void ShouldMarkDirtyAfterAddTags()
6638
[Fact]
6739
public void ShouldRemoveTags()
6840
{
69-
var xmp = new Xmp();
70-
71-
xmp.AddTags(
72-
["bd.wav", "ch.wav"],
73-
["Drums|Kick", "Drums|Snare"]
74-
);
41+
var xmp = Xmp.FromString(TestUtils.ReadFileAsString("TestData/WithTagsAdded.xml"));
7542

7643
xmp.RemoveTags(
77-
["bd.wav", "ch.wav"],
78-
["Drums|Snare"]
44+
["bd1.wav", "bd2.wav", "ch.wav"],
45+
["Creator|17cupsofcoffee"]
7946
);
8047

8148
xmp.RemoveTags(
8249
["ch.wav"],
83-
["Drums|Kick"]
84-
);
85-
86-
var bd = getItem(xmp.Xml, "bd.wav");
87-
var ch = getItem(xmp.Xml, "ch.wav");
88-
89-
Assert.Single(bd);
90-
Assert.Single(ch);
91-
92-
var bdTags = getTags(bd.First());
93-
94-
Assert.Contains("Drums|Kick", bdTags);
95-
Assert.Null(ch.First().Element(Ableton.Keywords));
96-
}
97-
98-
[Fact]
99-
public void ShouldMarkDirtyAfterRemoveTags()
100-
{
101-
// TODO: This test isn't great, as it's not clear whether AddTags or RemoveTags
102-
// is triggering the flag.
103-
104-
var xmp = new Xmp();
105-
106-
xmp.AddTags(
107-
["bd1.wav"],
108-
["Drums|Kick"]
50+
["Drums|Hihat", "Drums|Hihat|Closed Hihat"]
10951
);
11052

111-
xmp.RemoveTags(
112-
["bd1.wav"],
113-
["Drums|Kick"]
53+
Assert.Equal(
54+
XElement.Parse(TestUtils.ReadFileAsString("TestData/WithTagsRemoved.xml")),
55+
xmp.Xml,
56+
XNode.EqualityComparer
11457
);
11558

11659
Assert.True(xmp.IsDirty);
@@ -119,64 +62,18 @@ public void ShouldMarkDirtyAfterRemoveTags()
11962
[Fact]
12063
public void ShouldRemoveAllTags()
12164
{
122-
var xmp = new Xmp();
123-
124-
xmp.AddTags(
125-
["bd.wav", "ch.wav"],
126-
["Drums|Kick"]
127-
);
65+
var xmp = Xmp.FromString(TestUtils.ReadFileAsString("TestData/WithTagsAdded.xml"));
12866

12967
xmp.RemoveTags(
13068
["ch.wav"]
13169
);
13270

133-
var bd = getItem(xmp.Xml, "bd.wav");
134-
var ch = getItem(xmp.Xml, "ch.wav");
135-
136-
Assert.Single(bd);
137-
Assert.Single(ch);
138-
139-
var bdTags = getTags(bd.First());
140-
141-
Assert.Contains("Drums|Kick", bdTags);
142-
Assert.Null(ch.First().Element(Ableton.Keywords));
143-
}
144-
145-
[Fact]
146-
public void ShouldMarkDirtyAfterRemoveAllTags()
147-
{
148-
// TODO: This test isn't great, as it's not clear whether AddTags or RemoveTags
149-
// is triggering the flag.
150-
151-
var xmp = new Xmp();
152-
153-
xmp.AddTags(
154-
["bd1.wav"],
155-
["Drums|Kick"]
156-
);
157-
158-
xmp.RemoveTags(
159-
["bd1.wav"]
71+
Assert.Equal(
72+
XElement.Parse(TestUtils.ReadFileAsString("TestData/WithAllTagsRemoved.xml")),
73+
xmp.Xml,
74+
XNode.EqualityComparer
16075
);
16176

16277
Assert.True(xmp.IsDirty);
16378
}
164-
165-
private IEnumerable<XElement> getItem(XElement xml, string file)
166-
{
167-
return xml.Descendants(Ableton.Items)!
168-
.First()!
169-
.Element(Rdf.Bag)!
170-
.Elements(Rdf.Li)!
171-
.Where(e => e.Element(Ableton.FilePath)!.Value == file);
172-
}
173-
174-
private IEnumerable<string> getTags(XElement item)
175-
{
176-
return item
177-
.Element(Ableton.Keywords)!
178-
.Element(Rdf.Bag)!
179-
.Elements(Rdf.Li)
180-
.Select(e => e.Value);
181-
}
18279
}

0 commit comments

Comments
 (0)