1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-28 08:30:00 +08:00

Add tests coverage for filters with not equal

This commit is contained in:
Valerus9
2025-08-12 14:48:43 +02:00
committed by Dean Herbert
Unverified
parent 02f835dbc3
commit 997b8a0bba
3 changed files with 219 additions and 16 deletions
@@ -521,6 +521,199 @@ namespace osu.Game.Tests.NonVisual.Filtering
Assert.That(visibleBeatmaps, Is.EqualTo(expectedBeatmapIndexes));
}
[TestCase("title!=Title", new[] { 2, 4, 6 })]
[TestCase("title!=\"Title1\"", new[] { 2, 3, 4, 5, 6 })]
[TestCase("title!=\"Title1\"!", new[] { 2, 3, 4, 5, 6 })]
[TestCase("artist!=artist", new int[] { })]
[TestCase("artist!=\"artist2\"", new[] { 1, 2, 4, 6 })]
[TestCase("artist!=\"artist2\"!", new[] { 1, 2, 4, 6 })]
[TestCase("diff!=Diff", new[] { 2, 5 })]
[TestCase("diff!=\"Diff1\"", new[] { 1, 2, 3, 4, 5, 6 })]
[TestCase("diff!=\"Diff1\"!", new[] { 1, 2, 3, 4, 5, 6 })]
public void TestNotEqualSearchForTextFilters(string query, int[] expectedBeatmapIndexes)
{
var carouselBeatmaps = (((string title, string difficultyName, string artist)[])new[]
{
("Title1", "Diff1", "artist2"),
("Title1", "Diff2", "artist1"),
("My[Favourite]Song", "Expert", "artist1"),
("Title", "My Favourite Diff", "artist2"),
("Another One", "diff ]with [[ brackets]]]", "artist3"),
("Diff in title", "a", "artist2"),
("a", "Diff in diff", "artist3")
}).Select(info => new CarouselBeatmap(new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
Title = info.title,
Artist = info.artist
},
DifficultyName = info.difficultyName
})).ToList();
var criteria = new FilterCriteria();
FilterQueryParser.ApplyQueries(criteria, query);
carouselBeatmaps.ForEach(b => b.Filter(criteria));
int[] visibleBeatmaps = carouselBeatmaps
.Where(b => !b.Filtered.Value)
.Select(b => carouselBeatmaps.IndexOf(b)).ToArray();
Assert.That(visibleBeatmaps, Is.EqualTo(expectedBeatmapIndexes));
}
[TestCase("ar!=5", new[] { 0, 2, 3, 5 })]
[TestCase("cs!=7", new[] { 0, 2, 3, 6 })]
[TestCase("od!=3", new[] { 0, 2, 4, 6 })]
[TestCase("hp!=6", new[] { 0, 1, 3, 5, 6 })]
[TestCase("star!=1.78", new[] { 0, 2, 3, 5, 6 })]
[TestCase("bpm!=144", new[] { 0, 1, 3, 5 })]
[TestCase("length!=120", new[] { 2, 3, 4, 6 })]
public void TestNotEqualSearchForNumberFilters(string query, int[] expectedBeatmapIndexes)
{
var carouselBeatmaps = (((float ar, float cs, float od, float hp, double star, double bpm, double length)[])new[]
{
(10.0f, 5.0f, 1.0f, 6.5f, 2.78, 100.0, 120000.0),
(5.0f, 7.0f, 3.0f, 8.0f, 1.78, 244.0, 120000.0),
(5.5f, 7.5f, 4.0f, 6.0f, 1.55, 144.0, 60000.0),
(6.0f, 2.0f, 3.0f, 7.0f, 3.78, 774.0, 440000.0),
(5.0f, 7.0f, 4.0f, 6.0f, 1.78, 144.0, 310000.0),
(5.8f, 7.0f, 3.0f, 6.5f, 1.55, 344.0, 120000.0),
(5.0f, 3.0f, 7.0f, 10.0f, 2.78, 144.0, 260000.0)
}).Select(info => new CarouselBeatmap(new BeatmapInfo
{
Difficulty = new BeatmapDifficulty{
ApproachRate = info.ar,
OverallDifficulty = info.od,
DrainRate = info.hp,
CircleSize = info.cs
},
BPM = info.bpm,
StarRating = info.star,
Length = info.length
})).ToList();
var criteria = new FilterCriteria();
FilterQueryParser.ApplyQueries(criteria, query);
carouselBeatmaps.ForEach(b => b.Filter(criteria));
int[] visibleBeatmaps = carouselBeatmaps
.Where(b => !b.Filtered.Value)
.Select(b => carouselBeatmaps.IndexOf(b)).ToArray();
Assert.That(visibleBeatmaps, Is.EqualTo(expectedBeatmapIndexes));
}
[TestCase("status!=ranked", new[] { 1, 2, 4, 5 })]
[TestCase("status!=r", new[] { 1, 2, 4, 5 })]
[TestCase("status!=loved", new[] { 0, 1, 2, 3, 4, 6 })]
[TestCase("status!=l", new[] { 0, 1, 2, 3, 4, 6 })]
public void TestNotEqualSearchForEnumFilter(string query, int[] expectedBeatmapIndexes)
{
var carouselBeatmaps = (new BeatmapOnlineStatus[]
{
BeatmapOnlineStatus.Ranked,
BeatmapOnlineStatus.Qualified,
BeatmapOnlineStatus.Approved,
BeatmapOnlineStatus.Ranked,
BeatmapOnlineStatus.Approved,
BeatmapOnlineStatus.Loved,
BeatmapOnlineStatus.Ranked
}).Select(info => new CarouselBeatmap(new BeatmapInfo
{
Status = info
})).ToList();
var criteria = new FilterCriteria();
FilterQueryParser.ApplyQueries(criteria, query);
carouselBeatmaps.ForEach(b => b.Filter(criteria));
int[] visibleBeatmaps = carouselBeatmaps
.Where(b => !b.Filtered.Value)
.Select(b => carouselBeatmaps.IndexOf(b)).ToArray();
Assert.That(visibleBeatmaps, Is.EqualTo(expectedBeatmapIndexes));
}
//played
[TestCase("played!=1", new[] { 1, 4, 5 })]
[TestCase("played!=0", new[] { 0, 2, 3, 6, 7 })]
public void TestNotEqualSearchForBooleanFilter(string query, int[] expectedBeatmapIndexes)
{
var carouselBeatmaps = (new DateTimeOffset?[]
{
new DateTime(2012, 10, 21),
null,
new DateTime(2012, 11, 12),
new DateTime(2013, 2, 13),
null,
null,
new DateTime(2014, 1, 15),
new DateTime(2014, 11, 16),
}).Select(info => new CarouselBeatmap(new BeatmapInfo
{
LastPlayed = info
})).ToList();
var criteria = new FilterCriteria();
FilterQueryParser.ApplyQueries(criteria, query);
carouselBeatmaps.ForEach(b => b.Filter(criteria));
int[] visibleBeatmaps = carouselBeatmaps
.Where(b => !b.Filtered.Value)
.Select(b => carouselBeatmaps.IndexOf(b)).ToArray();
Assert.That(visibleBeatmaps, Is.EqualTo(expectedBeatmapIndexes));
}
//submitted, ranked
[TestCase("ranked!=2012", new[] { 3, 4, 5, 6, 7 })]
[TestCase("ranked!=2012.11", new[] { 0, 1, 3, 4, 5, 6, 7 })]
[TestCase("ranked!=2012.10.21", new[] { 1, 2, 3, 4, 5, 6, 7 })]
[TestCase("submitted!=2012", new[] { 3, 4, 5, 6, 7 })]
[TestCase("submitted!=2012.11", new[] { 0, 1, 3, 4, 5, 6, 7 })]
[TestCase("submitted!=2012.10.21", new[] { 1, 2, 3, 4, 5, 6, 7 })]
public void TestNotEqualSearchForDateFilter(string query, int[] expectedBeatmapIndexes)
{
var carouselBeatmaps = (new DateTime[]
{
new DateTime(2012, 10, 21),
new DateTime(2012, 10, 11),
new DateTime(2012, 11, 12),
new DateTime(2013, 2, 13),
new DateTime(2013, 2, 13),
new DateTime(2013, 3, 14),
new DateTime(2014, 1, 15),
new DateTime(2014, 11, 16),
}).Select(info => new CarouselBeatmap(new BeatmapInfo
{
BeatmapSet = new BeatmapSetInfo
{
DateRanked = new DateTimeOffset(info),
DateSubmitted = new DateTimeOffset(info),
}
})).ToList();
var criteria = new FilterCriteria();
FilterQueryParser.ApplyQueries(criteria, query);
carouselBeatmaps.ForEach(b => b.Filter(criteria));
int[] visibleBeatmaps = carouselBeatmaps
.Where(b => !b.Filtered.Value)
.Select(b => carouselBeatmaps.IndexOf(b)).ToArray();
Assert.That(visibleBeatmaps, Is.EqualTo(expectedBeatmapIndexes));
}
[Test]
public void TestApplySourceQueries()
{
+4 -4
View File
@@ -212,16 +212,16 @@ namespace osu.Game.Screens.Select
case MatchMode.Substring:
// Note that we are using ordinal here to avoid performance issues caused by globalisation concerns.
// See https://github.com/ppy/osu/issues/11571 / https://github.com/dotnet/docs/issues/18423.
return value.Contains(SearchTerm, StringComparison.OrdinalIgnoreCase);
return InvertSearch != value.Contains(SearchTerm, StringComparison.OrdinalIgnoreCase);
case MatchMode.IsolatedPhrase:
return Regex.IsMatch(value, $@"(^|\s){Regex.Escape(searchTerm)}($|\s)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
return InvertSearch != Regex.IsMatch(value, $@"(^|\s){Regex.Escape(searchTerm)}($|\s)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
case MatchMode.FullPhrase:
return CultureInfo.InvariantCulture.CompareInfo.Compare(value, searchTerm, CompareOptions.OrdinalIgnoreCase) == 0;
return InvertSearch != (CultureInfo.InvariantCulture.CompareInfo.Compare(value, searchTerm, CompareOptions.OrdinalIgnoreCase) == 0);
}
}
public bool InvertSearch;
private string searchTerm;
public string SearchTerm
+22 -12
View File
@@ -76,7 +76,8 @@ namespace osu.Game.Screens.Select
return false;
// Unplayed beatmaps are filtered on DateTimeOffset.MinValue.
if (op == Operator.NotEqual)
played = !played;
if (played)
{
criteria.LastPlayed.Min = DateTimeOffset.MinValue;
@@ -233,6 +234,11 @@ namespace osu.Game.Screens.Select
textFilter.SearchTerm = value;
return true;
case Operator.NotEqual:
textFilter.InvertSearch = true;
textFilter.SearchTerm = value;
return true;
default:
return false;
}
@@ -493,7 +499,6 @@ namespace osu.Game.Screens.Select
private static bool tryUpdateCriteriaRange<T>(ref FilterCriteria.OptionalRange<T> range, Operator op, T value)
where T : struct
{
range.InvertRange = false;
switch (op)
{
@@ -587,13 +592,15 @@ namespace osu.Game.Screens.Select
{
switch (op)
{
case Operator.NotEqual:
case Operator.Equal:
// an equality filter is difficult to define for support here.
// an equality or inequality filter is difficult to define for support here.
// if "3 months 2 days ago" means a single concrete time instant, such a filter is basically useless.
// if it means a range of 24 hours, then that is annoying to write and also comes with its own implications
// (does it mean "time instant 3 months 2 days ago, within 12 hours of tolerance either direction"?
// does it mean "the full calendar day, from midnight to midnight, 3 months 2 days ago"?)
// as such, for simplicity, just refuse to support this.
// same applies to inequality, but instead 24 hours would be need to be left out
return false;
// for the remaining operators, since the value provided to this function is an "ago" type value
@@ -748,6 +755,7 @@ namespace osu.Game.Screens.Select
DateTimeOffset dateTimeOffset;
DateTimeOffset minDateTimeOffset;
DateTimeOffset maxDateTimeOffset;
dateRange.InvertRange = false;
switch (op)
{
@@ -831,14 +839,16 @@ namespace osu.Game.Screens.Select
case Operator.NotEqual:
dateRange.InvertRange = true;
if (month == null)
{
month = 1;
day = 1;
minDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value);
maxDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddYears(1);
return tryUpdateCriteriaRange(ref dateRange, Operator.Less, minDateTimeOffset)
|| tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, maxDateTimeOffset);
return tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, minDateTimeOffset)
&& tryUpdateCriteriaRange(ref dateRange, Operator.Less, maxDateTimeOffset);
}
if (day == null)
@@ -846,14 +856,14 @@ namespace osu.Game.Screens.Select
day = 1;
minDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value);
maxDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddMonths(1);
return tryUpdateCriteriaRange(ref dateRange, Operator.Less, minDateTimeOffset)
|| tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, maxDateTimeOffset);
return tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, minDateTimeOffset)
&& tryUpdateCriteriaRange(ref dateRange, Operator.Less, maxDateTimeOffset);
}
minDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value);
maxDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddDays(1);
return tryUpdateCriteriaRange(ref dateRange, Operator.Less, minDateTimeOffset)
|| tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, maxDateTimeOffset);
minDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddDays(-1);
maxDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value);
return tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, minDateTimeOffset)
&& tryUpdateCriteriaRange(ref dateRange, Operator.Less, maxDateTimeOffset);
default:
return false;
@@ -862,7 +872,7 @@ namespace osu.Game.Screens.Select
catch (ArgumentOutOfRangeException)
{
return false;
}
}
}
}
}