diff --git a/src/NzbDrone.Common.Test/ExtensionTests/FuzzyContainsFixture.cs b/src/NzbDrone.Common.Test/ExtensionTests/FuzzyContainsFixture.cs
new file mode 100644
index 000000000..76e87888f
--- /dev/null
+++ b/src/NzbDrone.Common.Test/ExtensionTests/FuzzyContainsFixture.cs
@@ -0,0 +1,61 @@
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Common.Extensions;
+using NzbDrone.Test.Common;
+
+namespace NzbDrone.Common.Test
+{
+ [TestFixture]
+ public class FuzzyContainsFixture : TestBase
+ {
+ [TestCase("abcdef", "abcdef", 0.5, 0)]
+ [TestCase("", "abcdef", 0.5, -1)]
+ [TestCase("abcdef", "", 0.5, -1)]
+ [TestCase("", "", 0.5, -1)]
+ [TestCase("abcdef", "de", 0.5, 3)]
+ [TestCase("abcdef", "defy", 0.5, 3)]
+ [TestCase("abcdef", "abcdefy", 0.5, 0)]
+ [TestCase("I am the very model of a modern major general.", " that berry ", 0.3, 4)]
+ [TestCase("abcdefghijk", "fgh", 0.5, 5)]
+ [TestCase("abcdefghijk", "fgh", 0.5, 5)]
+ [TestCase("abcdefghijk", "efxhi", 0.5, 4)]
+ [TestCase("abcdefghijk", "cdefxyhijk", 0.5, 2)]
+ [TestCase("abcdefghijk", "bxy", 0.5, -1)]
+ [TestCase("123456789xx0", "3456789x0", 0.5, 2)]
+ [TestCase("abcdef", "xxabc", 0.5, 0)]
+ [TestCase("abcdef", "defyy", 0.5, 3)]
+ [TestCase("abcdef", "xabcdefy", 0.5, 0)]
+ [TestCase("abcdefghijk", "efxyhi", 0.6, 4)]
+ [TestCase("abcdefghijk", "efxyhi", 0.7, -1)]
+ [TestCase("abcdefghijk", "bcdef", 0.0, 1)]
+ [TestCase("abcdexyzabcde", "abccde", 0.5, 0)]
+ [TestCase("abcdefghijklmnopqrstuvwxyz", "abcdxxefg", 0.5, 0)]
+ [TestCase("abcdefghijklmnopqrstuvwxyz", "abcdefg", 0.5, 0)]
+ [TestCase("The quick brown fox jumps over the lazy dog", "The quick brown fox jumps over the lazy d", 0.5, 0)]
+ [TestCase("The quick brown fox jumps over the lazy dog", "The quick brown fox jumps over the lazy g", 0.5, 0)]
+ [TestCase("The quick brown fox jumps over the lazy dog", "quikc brown fox jumps over the lazy dog", 0.5, 4)]
+ [TestCase("The quick brown fox jumps over the lazy dog", "qui jumps over the lazy dog", 0.5, 16)]
+ [TestCase("The quick brown fox jumps over the lazy dog", "quikc brown fox jumps over the lazy dog", 0.5, 4)]
+ [TestCase("u6IEytQiYpzAccsbjQ5ISuE4smDQ1ZiU42cFBrTeKB2XrVLEqAvgIiKlDP75iApy07jzmK", "xEytQiYpzAccsbjQ5ISuE4smDQ1ZiU42cFBrTeKB2XrVLEqAvgIiKlDP75iApy07jzmK", 0.5, 2)]
+ [TestCase("plusifeelneedforredundantinformationintitlefield", "anthology", 0.5, -1)]
+ public void FuzzyFind(string text, string pattern, double threshold, int expected)
+ {
+ text.FuzzyFind(pattern, threshold).Should().Be(expected);
+ }
+
+ [TestCase("abcdef", "abcdef", 1)]
+ [TestCase("", "abcdef", 0)]
+ [TestCase("abcdef", "", 0)]
+ [TestCase("", "", 0)]
+ [TestCase("abcdef", "de", 1)]
+ [TestCase("abcdef", "defy", 0.75)]
+ [TestCase("abcdef", "abcdefghk", 6.0/9)]
+ [TestCase("abcdef", "zabcdefz", 6.0/8)]
+ [TestCase("plusifeelneedforredundantinformationintitlefield", "anthology", 4.0/9)]
+ [TestCase("+ (Plus) - I feel the need for redundant information in the title field", "+", 1)]
+ public void FuzzyContains(string text, string pattern, double expectedScore)
+ {
+ text.FuzzyContains(pattern).Should().BeApproximately(expectedScore, 1e-9);
+ }
+ }
+}
diff --git a/src/NzbDrone.Common.Test/LevenshteinDistanceFixture.cs b/src/NzbDrone.Common.Test/LevenshteinDistanceFixture.cs
index d8711191b..aff7e9738 100644
--- a/src/NzbDrone.Common.Test/LevenshteinDistanceFixture.cs
+++ b/src/NzbDrone.Common.Test/LevenshteinDistanceFixture.cs
@@ -42,5 +42,21 @@ namespace NzbDrone.Common.Test
{
text.ToLower().LevenshteinDistanceClean(other.ToLower()).Should().Be(expected);
}
+
+ [TestCase("hello", "hello")]
+ [TestCase("hello", "bye")]
+ [TestCase("a longer string", "a different long string")]
+ public void FuzzyMatchSymmetric(string a, string b)
+ {
+ a.FuzzyMatch(b).Should().Be(b.FuzzyMatch(a));
+ }
+
+ [TestCase("", "", 0)]
+ [TestCase("a", "", 0)]
+ [TestCase("", "a", 0)]
+ public void FuzzyMatchEmptyValuesReturnZero(string a, string b, double expected)
+ {
+ a.FuzzyMatch(b).Should().Be(expected);
+ }
}
}
diff --git a/src/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj b/src/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj
index e49b6b937..69459d7b1 100644
--- a/src/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj
+++ b/src/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj
@@ -84,6 +84,7 @@
+
diff --git a/src/NzbDrone.Common/Extensions/FuzzyContains.cs b/src/NzbDrone.Common/Extensions/FuzzyContains.cs
new file mode 100644
index 000000000..6a372c44f
--- /dev/null
+++ b/src/NzbDrone.Common/Extensions/FuzzyContains.cs
@@ -0,0 +1,167 @@
+/*
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+
+namespace NzbDrone.Common.Extensions
+{
+
+ public static class FuzzyContainsExtension {
+
+ public static int FuzzyFind(this string text, string pattern, double matchProb)
+ {
+ return match(text, pattern, matchProb).Item1;
+ }
+
+ // return the accuracy of the best match of pattern within text
+ public static double FuzzyContains(this string text, string pattern)
+ {
+ return match(text, pattern, 0.25).Item2;
+ }
+
+ /**
+ * Locate the best instance of 'pattern' in 'text'.
+ * Returns (-1, 1) if no match found.
+ * @param text The text to search.
+ * @param pattern The pattern to search for.
+ * @return Best match index or -1.
+ */
+ private static Tuple match(string text, string pattern, double matchThreshold = 0.5) {
+ // Check for null inputs not needed since null can't be passed in C#.
+ if (text.Length == 0 || pattern.Length == 0) {
+ // Nothing to match.
+ return new Tuple (-1, 0);
+ }
+
+ if (pattern.Length <= text.Length)
+ {
+ var loc = text.IndexOf(pattern, StringComparison.Ordinal);
+ if (loc != -1)
+ {
+ // Perfect match!
+ return new Tuple (loc, 1);
+ }
+ }
+
+ // Do a fuzzy compare.
+ return match_bitap(text, pattern, matchThreshold);
+ }
+
+ /**
+ * Locate the best instance of 'pattern' in 'text' near 'loc' using the
+ * Bitap algorithm. Returns -1 if no match found.
+ * @param text The text to search.
+ * @param pattern The pattern to search for.
+ * @return Best match index or -1.
+ */
+ private static Tuple match_bitap(string text, string pattern, double matchThreshold) {
+
+ // Initialise the alphabet.
+ Dictionary s = alphabet(pattern);
+ // don't keep creating new BigInteger(1)
+ var big1 = new BigInteger(1);
+
+ // Lowest score belowe which we give up.
+ var score_threshold = matchThreshold;
+
+ // Initialise the bit arrays.
+ var matchmask = big1 << (pattern.Length - 1);
+ int best_loc = -1;
+
+ // Empty initialization added to appease C# compiler.
+ var last_rd = new BigInteger[0];
+ for (int d = 0; d < pattern.Length; d++) {
+ // Scan for the best match; each iteration allows for one more error.
+ int start = 1;
+ int finish = text.Length + pattern.Length;
+
+ var rd = new BigInteger[finish + 2];
+ rd[finish + 1] = (big1 << d) - big1;
+ for (int j = finish; j >= start; j--) {
+ BigInteger charMatch;
+ if (text.Length <= j - 1 || !s.ContainsKey(text[j - 1])) {
+ // Out of range.
+ charMatch = 0;
+ } else {
+ charMatch = s[text[j - 1]];
+ }
+ if (d == 0) {
+ // First pass: exact match.
+ rd[j] = ((rd[j + 1] << 1) | big1) & charMatch;
+ } else {
+ // Subsequent passes: fuzzy match.
+ rd[j] = ((rd[j + 1] << 1) | big1) & charMatch
+ | (((last_rd[j + 1] | last_rd[j]) << 1) | big1) | last_rd[j + 1];
+ }
+ if ((rd[j] & matchmask) != 0) {
+ var score = bitapScore(d, pattern);
+ // This match will almost certainly be better than any existing
+ // match. But check anyway.
+ if (score >= score_threshold) {
+ // Told you so.
+ score_threshold = score;
+ best_loc = j - 1;
+ }
+ }
+ }
+ if (bitapScore(d + 1, pattern) < score_threshold) {
+ // No hope for a (better) match at greater error levels.
+ break;
+ }
+ last_rd = rd;
+ }
+ return new Tuple (best_loc, score_threshold);
+ }
+
+ /**
+ * Compute and return the score for a match with e errors and x location.
+ * @param e Number of errors in match.
+ * @param pattern Pattern being sought.
+ * @return Overall score for match (1.0 = good, 0.0 = bad).
+ */
+ private static double bitapScore(int e, string pattern) {
+ return 1.0 - (double)e / pattern.Length;
+ }
+
+ /**
+ * Initialise the alphabet for the Bitap algorithm.
+ * @param pattern The text to encode.
+ * @return Hash of character locations.
+ */
+ private static Dictionary alphabet(string pattern) {
+ var s = new Dictionary();
+ char[] char_pattern = pattern.ToCharArray();
+ foreach (char c in char_pattern) {
+ if (!s.ContainsKey(c)) {
+ s.Add(c, 0);
+ }
+ }
+ int i = 0;
+ foreach (char c in char_pattern) {
+ s[c] = s[c] | (new BigInteger(1) << (pattern.Length - i - 1));
+ i++;
+ }
+ return s;
+ }
+ }
+}
diff --git a/src/NzbDrone.Common/Extensions/StringExtensions.cs b/src/NzbDrone.Common/Extensions/StringExtensions.cs
index 40c7886f6..1fe5b90f2 100644
--- a/src/NzbDrone.Common/Extensions/StringExtensions.cs
+++ b/src/NzbDrone.Common/Extensions/StringExtensions.cs
@@ -143,29 +143,17 @@ namespace NzbDrone.Common.Extensions
public static double FuzzyMatch(this string a, string b)
{
- if (a.Contains(" ") && b.Contains(" "))
+ if (a.IsNullOrWhiteSpace() || b.IsNullOrWhiteSpace())
+ {
+ return 0;
+ }
+ else if (a.Contains(" ") && b.Contains(" "))
{
var partsA = a.Split(' ');
var partsB = b.Split(' ');
- var weightedHighCoefficients = new double[partsA.Length];
- var distanceRatios = new double[partsA.Length];
- for (int i = 0; i < partsA.Length; i++)
- {
- double high = 0.0;
- int indexDistance = 0;
- for (int x = 0; x < partsB.Length; x++)
- {
- var coef = LevenshteinCoefficient(partsA[i], partsB[x]);
- if (coef > high)
- {
- high = coef;
- indexDistance = Math.Abs(i - x);
- }
- }
- double distanceWeight = 1.0 - (double)indexDistance / (double)partsA.Length;
- weightedHighCoefficients[i] = high * distanceWeight;
- }
- return weightedHighCoefficients.Sum() / (double)partsA.Length;
+
+ var coef = (FuzzyMatchComponents(partsA, partsB) + FuzzyMatchComponents(partsB, partsA)) / (partsA.Length + partsB.Length);
+ return Math.Max(coef, LevenshteinCoefficient(a, b));
}
else
{
@@ -173,6 +161,28 @@ namespace NzbDrone.Common.Extensions
}
}
+ private static double FuzzyMatchComponents(string[] a, string[] b)
+ {
+ double weightDenom = Math.Max(a.Length, b.Length);
+ double sum = 0;
+ for (int i = 0; i < a.Length; i++)
+ {
+ double high = 0.0;
+ int indexDistance = 0;
+ for (int x = 0; x < b.Length; x++)
+ {
+ var coef = LevenshteinCoefficient(a[i], b[x]);
+ if (coef > high)
+ {
+ high = coef;
+ indexDistance = Math.Abs(i - x);
+ }
+ }
+ sum += (1.0 - (double)indexDistance / weightDenom) * high;
+ }
+ return sum;
+ }
+
public static double LevenshteinCoefficient(this string a, string b)
{
return 1.0 - (double)a.LevenshteinDistance(b) / Math.Max(a.Length, b.Length);
diff --git a/src/NzbDrone.Common/NzbDrone.Common.csproj b/src/NzbDrone.Common/NzbDrone.Common.csproj
index 08fd5c764..bd08eacb1 100644
--- a/src/NzbDrone.Common/NzbDrone.Common.csproj
+++ b/src/NzbDrone.Common/NzbDrone.Common.csproj
@@ -71,6 +71,7 @@
+
@@ -198,6 +199,7 @@
+
diff --git a/src/NzbDrone.Core.Test/MusicTests/AlbumRepositoryTests/AlbumRepositoryFixture.cs b/src/NzbDrone.Core.Test/MusicTests/AlbumRepositoryTests/AlbumRepositoryFixture.cs
index d924bd3ab..d7245756d 100644
--- a/src/NzbDrone.Core.Test/MusicTests/AlbumRepositoryTests/AlbumRepositoryFixture.cs
+++ b/src/NzbDrone.Core.Test/MusicTests/AlbumRepositoryTests/AlbumRepositoryFixture.cs
@@ -94,7 +94,6 @@ namespace NzbDrone.Core.Test.MusicTests.AlbumRepositoryTests
}
-
[Test]
public void should_find_album_in_db_by_releaseid()
{
@@ -129,6 +128,7 @@ namespace NzbDrone.Core.Test.MusicTests.AlbumRepositoryTests
[TestCase("ANTholog")]
[TestCase("nthology")]
[TestCase("antholoyg")]
+ [TestCase("÷")]
public void should_not_find_album_in_db_by_incorrect_title(string title)
{
var album = _albumRepo.FindByTitle(_artist.Id, title);
@@ -136,28 +136,6 @@ namespace NzbDrone.Core.Test.MusicTests.AlbumRepositoryTests
album.Should().BeNull();
}
- [TestCase("ANTholog")]
- [TestCase("antholoyg")]
- [TestCase("ANThology CD")]
- public void should_find_album_in_db_by_inexact_title(string title)
- {
- var album = _albumRepo.FindByTitleInexact(_artist.Id, title);
-
- album.Should().NotBeNull();
- album.Title.Should().Be(_album.Title);
- }
-
- [TestCase("ANTholog")]
- [TestCase("antholoyg")]
- [TestCase("ANThology CD")]
- public void should_not_find_album_in_db_by_inexact_title_when_two_similar_matches(string title)
- {
- _albumRepo.Insert(_albumSimilar);
- var album = _albumRepo.FindByTitleInexact(_artist.Id, title);
-
- album.Should().BeNull();
- }
-
[Test]
public void should_not_find_album_in_db_by_partial_releaseid()
{
diff --git a/src/NzbDrone.Core.Test/MusicTests/AlbumServiceFixture.cs b/src/NzbDrone.Core.Test/MusicTests/AlbumServiceFixture.cs
new file mode 100644
index 000000000..1a44e97ec
--- /dev/null
+++ b/src/NzbDrone.Core.Test/MusicTests/AlbumServiceFixture.cs
@@ -0,0 +1,77 @@
+using FizzWare.NBuilder;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Core.Music;
+using NzbDrone.Core.Test.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NLog;
+using Moq;
+
+namespace NzbDrone.Core.Test.MusicTests.AlbumRepositoryTests
+{
+ [TestFixture]
+ public class AlbumServiceFixture : CoreTest
+ {
+ private List _albums;
+
+ [SetUp]
+ public void Setup()
+ {
+ _albums = new List();
+ _albums.Add(new Album
+ {
+ Title = "ANThology",
+ CleanTitle = "anthology",
+ });
+
+ _albums.Add(new Album
+ {
+ Title = "+",
+ CleanTitle = "",
+ });
+
+ Mocker.GetMock()
+ .Setup(s => s.GetAlbums(It.IsAny()))
+ .Returns(_albums);
+ }
+
+ private void GivenSimilarAlbum()
+ {
+ _albums.Add(new Album
+ {
+ Title = "ANThology2",
+ CleanTitle = "anthology2",
+ });
+ }
+
+ [TestCase("ANTholog", "ANThology")]
+ [TestCase("antholoyg", "ANThology")]
+ [TestCase("ANThology CD", "ANThology")]
+ [TestCase("ANThology CD xxxx (Remastered) - [Oh please why do they do this?]", "ANThology")]
+ [TestCase("+ (Plus) - I feel the need for redundant information in the title field", "+")]
+ public void should_find_album_in_db_by_inexact_title(string title, string expected)
+ {
+ var album = Subject.FindByTitleInexact(0, title);
+
+ album.Should().NotBeNull();
+ album.Title.Should().Be(expected);
+ }
+
+ [TestCase("ANTholog")]
+ [TestCase("antholoyg")]
+ [TestCase("ANThology CD")]
+ [TestCase("÷")]
+ [TestCase("÷ (Divide)")]
+ public void should_not_find_album_in_db_by_inexact_title_when_two_similar_matches(string title)
+ {
+ GivenSimilarAlbum();
+ var album = Subject.FindByTitleInexact(0, title);
+
+ album.Should().BeNull();
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/MusicTests/ArtistRepositoryTests/ArtistRepositoryFixture.cs b/src/NzbDrone.Core.Test/MusicTests/ArtistRepositoryTests/ArtistRepositoryFixture.cs
index 814a20ab0..0142842a3 100644
--- a/src/NzbDrone.Core.Test/MusicTests/ArtistRepositoryTests/ArtistRepositoryFixture.cs
+++ b/src/NzbDrone.Core.Test/MusicTests/ArtistRepositoryTests/ArtistRepositoryFixture.cs
@@ -17,6 +17,24 @@ namespace NzbDrone.Core.Test.MusicTests.ArtistRepositoryTests
public class ArtistRepositoryFixture : DbTest
{
+ private ArtistRepository _artistRepo;
+
+ private Artist CreateArtist(string name)
+ {
+ return Builder.CreateNew()
+ .With(a => a.Name = name)
+ .With(a => a.CleanName = Parser.Parser.CleanArtistName(name))
+ .With(a => a.ForeignArtistId = name)
+ .BuildNew();
+ }
+
+ private void GivenArtists()
+ {
+ _artistRepo = Mocker.Resolve();
+ _artistRepo.Insert(CreateArtist("The Black Eyed Peas"));
+ _artistRepo.Insert(CreateArtist("The Black Keys"));
+ }
+
[Test]
public void should_lazyload_profiles()
{
@@ -61,5 +79,16 @@ namespace NzbDrone.Core.Test.MusicTests.ArtistRepositoryTests
StoredModel.MetadataProfile.Should().NotBeNull();
}
+
+ [TestCase("The Black Eyed Peas")]
+ [TestCase("The Black Keys")]
+ public void should_find_artist_in_db_by_name(string name)
+ {
+ GivenArtists();
+ var artist = _artistRepo.FindByName(Parser.Parser.CleanArtistName(name));
+
+ artist.Should().NotBeNull();
+ artist.Name.Should().Be(name);
+ }
}
}
diff --git a/src/NzbDrone.Core.Test/MusicTests/ArtistServiceTests/FindByNameInexactFixture.cs b/src/NzbDrone.Core.Test/MusicTests/ArtistServiceTests/FindByNameInexactFixture.cs
new file mode 100644
index 000000000..2c11b65d4
--- /dev/null
+++ b/src/NzbDrone.Core.Test/MusicTests/ArtistServiceTests/FindByNameInexactFixture.cs
@@ -0,0 +1,58 @@
+using System.Collections.Generic;
+using FizzWare.NBuilder;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Core.Music;
+
+namespace NzbDrone.Core.Test.MusicTests.ArtistServiceTests
+{
+ [TestFixture]
+
+ public class FindByNameInexactFixture : CoreTest
+ {
+ private List _artists;
+
+ private Artist CreateArtist(string name)
+ {
+ return Builder.CreateNew()
+ .With(a => a.Name = name)
+ .With(a => a.CleanName = Parser.Parser.CleanArtistName(name))
+ .With(a => a.ForeignArtistId = name)
+ .BuildNew();
+ }
+
+ [SetUp]
+ public void Setup()
+ {
+ _artists = new List();
+ _artists.Add(CreateArtist("The Black Eyed Peas"));
+ _artists.Add(CreateArtist("The Black Keys"));
+
+ Mocker.GetMock()
+ .Setup(s => s.All())
+ .Returns(_artists);
+ }
+
+ [TestCase("The Black Eyde Peas", "The Black Eyed Peas")]
+ [TestCase("Black Eyed Peas", "The Black Eyed Peas")]
+ [TestCase("The Black eys", "The Black Keys")]
+ public void should_find_artist_in_db_by_name_inexact(string name, string expected)
+ {
+ var artist = Subject.FindByNameInexact(name);
+
+ artist.Should().NotBeNull();
+ artist.Name.Should().Be(expected);
+ }
+
+ [TestCase("The Black Peas")]
+ public void should_not_find_artist_in_db_by_ambiguous_name(string name)
+ {
+
+ var artist = Subject.FindByNameInexact(name);
+
+ artist.Should().BeNull();
+ }
+
+ }
+}
diff --git a/src/NzbDrone.Core.Test/MusicTests/TitleMatchingTests/TitleMatchingFixture.cs b/src/NzbDrone.Core.Test/MusicTests/TitleMatchingTests/TitleMatchingFixture.cs
index f9d10b1da..0594fdcf2 100644
--- a/src/NzbDrone.Core.Test/MusicTests/TitleMatchingTests/TitleMatchingFixture.cs
+++ b/src/NzbDrone.Core.Test/MusicTests/TitleMatchingTests/TitleMatchingFixture.cs
@@ -1,27 +1,21 @@
using System.Linq;
using FluentAssertions;
-using NLog;
using NUnit.Framework;
-using NzbDrone.Core.Configuration;
using NzbDrone.Core.Music;
using NzbDrone.Core.Test.Framework;
using System.Collections.Generic;
+using Moq;
namespace NzbDrone.Core.Test.MusicTests.TitleMatchingTests
{
[TestFixture]
- public class TitleMatchingFixture : DbTest
+ public class TitleMatchingFixture : CoreTest
{
- private TrackRepository _trackRepository;
- private TrackService _trackService;
-
+ private List