mirror of
https://github.com/ppy/osu.git
synced 2026-05-13 20:33:35 +08:00
090fe44f86
A few quick ones from https://github.com/ppy/osu/issues/37190. ## [Rewrite `TestSceneDeleteLocalScore` to have less context menu containers (and hopefully no longer flake)](https://github.com/ppy/osu/commit/ea0bc5b72374056d1749f8058f4874b7ef66a241) As seen in https://github.com/ppy/osu/actions/runs/23890777748#user-content-r3s3. This is a speculative fix but I'm feeling somewhat confident about this one. `BeatmapLeaderboardWedge` has TWO separate `ContextMenuContainer`s itself, and the test mentioned here was bringing a third. I have a feeling that the test flaking may have something to do with the fact that the test logic would attempt to click a menu item on specifically ONE of the three context menus. My bet is that when it fails, it's because it's trying the wrong one, but I don't have reproduction. ## [Wait for text to appear in flaking `TestSceneDrawableRoomPlaylist.TestSelectableMouseHandling` test](https://github.com/ppy/osu/commit/9d62eea8b56a0eb013e8116db0d38424b500876f) As seen in https://github.com/ppy/osu/actions/runs/23888446400#user-content-r1s1. Speculative. Banking on the fact that it takes time to load the sprite texts. ## [Remove all tests from `TestSceneWikiMarkdownContainer` dependent on existence of wiki on dev](https://github.com/ppy/osu/commit/8f319f91c98510b84e915bb61ec147618a584ce1) Deletes a flaky as seen in https://github.com/ppy/osu/actions/runs/23878899702#user-content-r2s2. The year is 2026 and LLM scrapers hammer [the entire](https://sourcehut.org/blog/2025-04-15-you-cannot-have-our-users-data/) [internet](https://blog.metabrainz.org/2025/12/11/we-cant-have-nice-things-because-of-ai-scrapers/) all over to scrape whatever ounce of Human Content there is left to feed the Moloch so that it can regurgitate it back in the form of The Most Average Speech You've Ever Read. We are not immune to this, and as such the LLM homunculi have hit the dev.ppy.sh wiki instance enough times for it to just completely [be banished to the blagole](https://www.youtube.com/watch?v=AfA_2Ku1aJY). Which means I get to freely delete flaky tests that should never have been running as part of CI because they're completely useless now and it's not like we're ever turning them back on again. ## [Use equality check in `TestSceneBeatmapCarouselScrolling.TestScrollPositionMaintainedOnRemove_SecondSelected` that's less sensitive to floating point](https://github.com/ppy/osu/commit/6f2a1de58f7e816830c430e89117ee4c594f529d) As seen in https://github.com/ppy/osu/actions/runs/23826420794#user-content-r0s1.
457 lines
17 KiB
C#
457 lines
17 KiB
C#
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using NUnit.Framework;
|
|
using osu.Framework.Allocation;
|
|
using osu.Framework.Audio;
|
|
using osu.Framework.Extensions.ObjectExtensions;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Graphics.Containers;
|
|
using osu.Framework.Graphics.Sprites;
|
|
using osu.Framework.Platform;
|
|
using osu.Framework.Testing;
|
|
using osu.Game.Beatmaps;
|
|
using osu.Game.Beatmaps.Drawables;
|
|
using osu.Game.Beatmaps.Drawables.Cards;
|
|
using osu.Game.Database;
|
|
using osu.Game.Graphics.Containers;
|
|
using osu.Game.Graphics.Cursor;
|
|
using osu.Game.Graphics.UserInterface;
|
|
using osu.Game.Models;
|
|
using osu.Game.Online.API;
|
|
using osu.Game.Online.Chat;
|
|
using osu.Game.Online.Rooms;
|
|
using osu.Game.Rulesets;
|
|
using osu.Game.Rulesets.Osu;
|
|
using osu.Game.Rulesets.Osu.Mods;
|
|
using osu.Game.Screens.OnlinePlay;
|
|
using osu.Game.Tests.Beatmaps;
|
|
using osu.Game.Users.Drawables;
|
|
using osuTK;
|
|
using osuTK.Input;
|
|
|
|
namespace osu.Game.Tests.Visual.Multiplayer
|
|
{
|
|
public partial class TestSceneDrawableRoomPlaylist : MultiplayerTestScene
|
|
{
|
|
private RulesetStore rulesets = null!;
|
|
private TestPlaylist playlist = null!;
|
|
private BeatmapManager manager = null!;
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load(GameHost host, AudioManager audio)
|
|
{
|
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
|
|
Dependencies.Cache(Realm);
|
|
}
|
|
|
|
[Test]
|
|
public void TestNonEditableNonSelectable()
|
|
{
|
|
createPlaylist();
|
|
|
|
moveToItem(0);
|
|
assertHandleVisibility(0, false);
|
|
assertDeleteButtonVisibility(0, false);
|
|
|
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
|
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
|
|
|
AddStep("press down", () => InputManager.Key(Key.Down));
|
|
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
|
}
|
|
|
|
[Test]
|
|
public void TestEditable()
|
|
{
|
|
createPlaylist(p =>
|
|
{
|
|
p.AllowReordering = true;
|
|
p.AllowDeletion = true;
|
|
});
|
|
|
|
moveToItem(0);
|
|
assertHandleVisibility(0, true);
|
|
assertDeleteButtonVisibility(0, true);
|
|
|
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
|
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
|
|
|
AddStep("press down", () => InputManager.Key(Key.Down));
|
|
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
|
}
|
|
|
|
[Test]
|
|
public void TestMarkInvalid()
|
|
{
|
|
createPlaylist(p =>
|
|
{
|
|
p.AllowReordering = true;
|
|
p.AllowDeletion = true;
|
|
p.AllowSelection = true;
|
|
});
|
|
|
|
AddStep("mark item 0 as invalid", () => playlist.Items[0].MarkInvalid());
|
|
|
|
moveToItem(0);
|
|
|
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
|
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
|
|
|
AddStep("press down", () => InputManager.Key(Key.Down));
|
|
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
|
}
|
|
|
|
[Test]
|
|
public void TestMarkCompleted()
|
|
{
|
|
createPlaylist();
|
|
AddStep("mark some items as complete", () =>
|
|
{
|
|
playlist.Items[0].MarkCompleted();
|
|
playlist.Items[2].MarkCompleted();
|
|
playlist.Items[3].MarkCompleted();
|
|
playlist.Items[5].MarkCompleted();
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectable()
|
|
{
|
|
createPlaylist(p => p.AllowSelection = true);
|
|
|
|
moveToItem(0);
|
|
assertHandleVisibility(0, false);
|
|
assertDeleteButtonVisibility(0, false);
|
|
|
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
|
|
|
AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
|
|
}
|
|
|
|
[Test]
|
|
public void TestEditableSelectable()
|
|
{
|
|
createPlaylist(p =>
|
|
{
|
|
p.AllowReordering = true;
|
|
p.AllowDeletion = true;
|
|
p.AllowSelection = true;
|
|
});
|
|
|
|
moveToItem(0);
|
|
assertHandleVisibility(0, true);
|
|
assertDeleteButtonVisibility(0, true);
|
|
|
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
|
|
|
AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectionNotLostAfterRearrangement()
|
|
{
|
|
createPlaylist(p =>
|
|
{
|
|
p.AllowReordering = true;
|
|
p.AllowDeletion = true;
|
|
p.AllowSelection = true;
|
|
});
|
|
|
|
moveToItem(0);
|
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
|
|
|
moveToDragger(0);
|
|
AddStep("begin drag", () => InputManager.PressButton(MouseButton.Left));
|
|
moveToDragger(1, new Vector2(0, 5));
|
|
AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left));
|
|
|
|
AddAssert("item 1 is selected", () => playlist.SelectedItem.Value == playlist.Items[1]);
|
|
}
|
|
|
|
[Test]
|
|
public void TestKeyboardSelection()
|
|
{
|
|
createPlaylist(p => p.AllowSelection = true);
|
|
|
|
AddStep("press down", () => InputManager.Key(Key.Down));
|
|
AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
|
|
|
|
AddStep("press down", () => InputManager.Key(Key.Down));
|
|
AddAssert("item 1 is selected", () => playlist.SelectedItem.Value == playlist.Items[1]);
|
|
|
|
AddStep("press up", () => InputManager.Key(Key.Up));
|
|
AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
|
|
|
|
AddUntilStep("navigate to last item via keyboard", () =>
|
|
{
|
|
InputManager.Key(Key.Down);
|
|
return playlist.SelectedItem.Value == playlist.Items.Last();
|
|
});
|
|
AddAssert("last item is selected", () => playlist.SelectedItem.Value == playlist.Items.Last());
|
|
AddUntilStep("last item is scrolled into view", () =>
|
|
{
|
|
var drawableItem = playlist.ItemMap[playlist.Items.Last()];
|
|
return playlist.ScreenSpaceDrawQuad.Contains(drawableItem.ScreenSpaceDrawQuad.TopLeft)
|
|
&& playlist.ScreenSpaceDrawQuad.Contains(drawableItem.ScreenSpaceDrawQuad.BottomRight);
|
|
});
|
|
|
|
AddStep("press down", () => InputManager.Key(Key.Down));
|
|
AddAssert("last item is selected", () => playlist.SelectedItem.Value == playlist.Items.Last());
|
|
|
|
AddStep("press up", () => InputManager.Key(Key.Up));
|
|
AddAssert("second last item is selected", () => playlist.SelectedItem.Value == playlist.Items.Reverse().ElementAt(1));
|
|
}
|
|
|
|
[Test]
|
|
public void TestDownloadButtonHiddenWhenBeatmapExists()
|
|
{
|
|
Live<BeatmapSetInfo> imported = null!;
|
|
|
|
AddStep("import beatmap", () =>
|
|
{
|
|
var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo;
|
|
|
|
Debug.Assert(beatmap.BeatmapSet != null);
|
|
imported = manager.Import(beatmap.BeatmapSet)!;
|
|
});
|
|
|
|
createPlaylistWithBeatmaps(() => imported.PerformRead(s => s.Beatmaps.Detach()));
|
|
|
|
assertDownloadButtonVisible(false);
|
|
|
|
AddStep("delete beatmap set", () => imported.PerformWrite(s => s.DeletePending = true));
|
|
assertDownloadButtonVisible(true);
|
|
|
|
AddStep("undelete beatmap set", () => imported.PerformWrite(s => s.DeletePending = false));
|
|
assertDownloadButtonVisible(false);
|
|
|
|
void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}",
|
|
() => playlist.ChildrenOfType<BeatmapDownloadButton>().SingleOrDefault()?.Alpha == (visible ? 1 : 0));
|
|
}
|
|
|
|
[Test]
|
|
public void TestDownloadButtonVisibleInitiallyWhenBeatmapDoesNotExist()
|
|
{
|
|
var byOnlineId = CreateAPIBeatmap();
|
|
byOnlineId.OnlineID = 1337; // Some random ID that does not exist locally.
|
|
|
|
var byChecksum = CreateAPIBeatmap();
|
|
byChecksum.Checksum = "1337"; // Some random checksum that does not exist locally.
|
|
|
|
createPlaylistWithBeatmaps(() => new[] { byOnlineId, byChecksum });
|
|
|
|
AddAssert("download buttons shown", () => playlist.ChildrenOfType<BeatmapDownloadButton>().All(d => d.IsPresent));
|
|
}
|
|
|
|
[Test]
|
|
public void TestExplicitBeatmapItem()
|
|
{
|
|
var beatmap = CreateAPIBeatmap();
|
|
|
|
Debug.Assert(beatmap.BeatmapSet != null);
|
|
|
|
beatmap.BeatmapSet.HasExplicitContent = true;
|
|
|
|
createPlaylistWithBeatmaps(() => new[] { beatmap });
|
|
}
|
|
|
|
[Test]
|
|
public void TestExpiredItems()
|
|
{
|
|
createPlaylist(p =>
|
|
{
|
|
p.Items.Clear();
|
|
p.Items.AddRange(new[]
|
|
{
|
|
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
|
{
|
|
ID = 0,
|
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
|
Expired = true,
|
|
RequiredMods = new[]
|
|
{
|
|
new APIMod(new OsuModHardRock()),
|
|
new APIMod(new OsuModDoubleTime()),
|
|
new APIMod(new OsuModAutoplay())
|
|
}
|
|
},
|
|
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
|
{
|
|
ID = 1,
|
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
|
RequiredMods = new[]
|
|
{
|
|
new APIMod(new OsuModHardRock()),
|
|
new APIMod(new OsuModDoubleTime()),
|
|
new APIMod(new OsuModAutoplay())
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded));
|
|
}
|
|
|
|
[TestCase(false)]
|
|
[TestCase(true)]
|
|
public void TestWithOwner(bool withOwner)
|
|
{
|
|
createPlaylist(p => p.ShowItemOwners = withOwner);
|
|
|
|
AddAssert("owner visible", () => playlist.ChildrenOfType<UpdateableAvatar>().All(a => a.IsPresent == withOwner));
|
|
}
|
|
|
|
[Test]
|
|
public void TestWithAllButtonsEnabled()
|
|
{
|
|
createPlaylist(p =>
|
|
{
|
|
p.AllowDeletion = true;
|
|
p.AllowShowingResults = true;
|
|
p.AllowEditing = true;
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectableMouseHandling()
|
|
{
|
|
bool resultsRequested = false;
|
|
|
|
AddStep("reset flag", () => resultsRequested = false);
|
|
createPlaylist(p =>
|
|
{
|
|
p.AllowSelection = true;
|
|
p.AllowShowingResults = true;
|
|
p.RequestResults = _ => resultsRequested = true;
|
|
});
|
|
|
|
AddUntilStep("wait for load", () => playlist.ChildrenOfType<DrawableLinkCompiler>().Any()
|
|
&& playlist.ChildrenOfType<LinkFlowContainer>().First().ChildrenOfType<SpriteText>().Any()
|
|
&& playlist.ChildrenOfType<BeatmapCardThumbnail>().First().DrawWidth > 0);
|
|
|
|
AddStep("move mouse to first item title", () => InputManager.MoveMouseTo(playlist.ChildrenOfType<LinkFlowContainer>().First().ChildrenOfType<SpriteText>().First()));
|
|
AddAssert("first item title not hovered", () => playlist.ChildrenOfType<DrawableLinkCompiler>().First().IsHovered, () => Is.False);
|
|
|
|
AddStep("click title", () =>
|
|
{
|
|
InputManager.MoveMouseTo(playlist.ChildrenOfType<LinkFlowContainer>().First().ChildrenOfType<SpriteText>().First());
|
|
InputManager.Click(MouseButton.Left);
|
|
});
|
|
|
|
AddUntilStep("first item selected", () => playlist.ChildrenOfType<DrawableRoomPlaylistItem>().First().IsSelectedItem, () => Is.True);
|
|
AddUntilStep("first item title hovered", () => playlist.ChildrenOfType<DrawableLinkCompiler>().First().IsHovered, () => Is.True);
|
|
|
|
AddStep("move mouse to second item results button", () => InputManager.MoveMouseTo(playlist.ChildrenOfType<GrayButton>().ElementAt(5)));
|
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
|
AddUntilStep("results requested", () => resultsRequested);
|
|
}
|
|
|
|
private void moveToItem(int index, Vector2? offset = null)
|
|
=> AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType<DrawableRoomPlaylistItem>().ElementAt(index), offset));
|
|
|
|
private void moveToDragger(int index, Vector2? offset = null) => AddStep($"move mouse to dragger {index}", () =>
|
|
{
|
|
var item = playlist.ChildrenOfType<OsuRearrangeableListItem<PlaylistItem>>().ElementAt(index);
|
|
InputManager.MoveMouseTo(item.ChildrenOfType<OsuRearrangeableListItem<PlaylistItem>.PlaylistItemHandle>().Single(), offset);
|
|
});
|
|
|
|
private void assertHandleVisibility(int index, bool visible)
|
|
=> AddAssert($"handle {index} {(visible ? "is" : "is not")} visible",
|
|
() => (playlist.ChildrenOfType<OsuRearrangeableListItem<PlaylistItem>.PlaylistItemHandle>().ElementAt(index).Alpha > 0) == visible);
|
|
|
|
private void assertDeleteButtonVisibility(int index, bool visible)
|
|
=> AddAssert($"delete button {index} {(visible ? "is" : "is not")} visible",
|
|
() => (playlist.ChildrenOfType<DrawableRoomPlaylistItem.PlaylistRemoveButton>().ElementAt(2 + index * 2).Alpha > 0) == visible);
|
|
|
|
private void createPlaylistWithBeatmaps(Func<IEnumerable<IBeatmapInfo>> beatmaps) => createPlaylist(p =>
|
|
{
|
|
int index = 0;
|
|
|
|
p.Items.Clear();
|
|
|
|
foreach (var b in beatmaps())
|
|
{
|
|
p.Items.Add(new PlaylistItem(b)
|
|
{
|
|
ID = index++,
|
|
OwnerID = 2,
|
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
|
RequiredMods = new[]
|
|
{
|
|
new APIMod(new OsuModHardRock()),
|
|
new APIMod(new OsuModDoubleTime()),
|
|
new APIMod(new OsuModAutoplay())
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
private void createPlaylist(Action<TestPlaylist>? setupPlaylist = null)
|
|
{
|
|
AddStep("create playlist", () =>
|
|
{
|
|
Child = new OsuContextMenuContainer
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
Child = playlist = new TestPlaylist
|
|
{
|
|
Anchor = Anchor.Centre,
|
|
Origin = Anchor.Centre,
|
|
Size = new Vector2(500, 300)
|
|
}
|
|
};
|
|
|
|
for (int i = 0; i < 20; i++)
|
|
{
|
|
playlist.Items.Add(new PlaylistItem(i % 2 == 1
|
|
? new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo
|
|
: new BeatmapInfo
|
|
{
|
|
Metadata = new BeatmapMetadata
|
|
{
|
|
Artist = "Artist",
|
|
Author = new RealmUser { Username = "Creator name here" },
|
|
Title = "Long title used to check background colour",
|
|
},
|
|
BeatmapSet = new BeatmapSetInfo()
|
|
})
|
|
{
|
|
ID = i,
|
|
OwnerID = 2,
|
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
|
RequiredMods = new[]
|
|
{
|
|
new APIMod(new OsuModHardRock()),
|
|
new APIMod(new OsuModDoubleTime()),
|
|
new APIMod(new OsuModAutoplay())
|
|
}
|
|
});
|
|
}
|
|
|
|
setupPlaylist?.Invoke(playlist);
|
|
});
|
|
|
|
AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded));
|
|
}
|
|
|
|
protected override void Dispose(bool isDisposing)
|
|
{
|
|
base.Dispose(isDisposing);
|
|
|
|
if (rulesets.IsNotNull())
|
|
rulesets.Dispose();
|
|
}
|
|
|
|
private partial class TestPlaylist : DrawableRoomPlaylist
|
|
{
|
|
public new IReadOnlyDictionary<PlaylistItem, RearrangeableListItem<PlaylistItem>> ItemMap => base.ItemMap;
|
|
}
|
|
}
|
|
}
|