Merge pull request #16681 from peppy/fix-delete-local-scores

Fix delete local scores via "Clear all scores" button crashing the game
This commit is contained in:
Dan Balasescu 2022-01-28 17:01:31 +09:00 committed by GitHub
commit f021a274d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 121 additions and 20 deletions

View File

@ -1,6 +1,7 @@
// 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.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
@ -10,16 +11,19 @@
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Toolbar;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Menu;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Screens.Select.Options;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
@ -96,35 +100,81 @@ public void TestRetryCountIncrements()
[Test]
public void TestRetryFromResults()
{
Player player = null;
ResultsScreen results = null;
var getOriginalPlayer = playToResults();
IWorkingBeatmap beatmap() => Game.Beatmap.Value;
AddStep("attempt to retry", () => ((ResultsScreen)Game.ScreenStack.CurrentScreen).ChildrenOfType<HotkeyRetryOverlay>().First().Action());
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != getOriginalPlayer() && Game.ScreenStack.CurrentScreen is Player);
}
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
[Test]
public void TestDeleteAllScoresAfterPlaying()
{
playToResults();
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
ScoreInfo score = null;
LeaderboardScore scorePanel = null;
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
AddStep("get score", () => score = ((ResultsScreen)Game.ScreenStack.CurrentScreen).Score);
AddStep("set mods", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModDoubleTime { SpeedChange = { Value = 2 } } });
AddAssert("ensure score is databased", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == false));
AddStep("press enter", () => InputManager.Key(Key.Enter));
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action());
AddUntilStep("wait for player", () =>
AddStep("show local scores", () => Game.ChildrenOfType<BeatmapDetailAreaTabControl>().First().Current.Value = new BeatmapDetailAreaLeaderboardTabItem<BeatmapLeaderboardScope>(BeatmapLeaderboardScope.Local));
AddUntilStep("wait for score displayed", () => (scorePanel = Game.ChildrenOfType<LeaderboardScore>().FirstOrDefault(s => s.Score.Equals(score))) != null);
AddStep("open options", () => InputManager.Key(Key.F3));
AddStep("choose clear all scores", () => InputManager.Key(Key.Number4));
AddStep("confirm deletion", () => InputManager.Key(Key.Number1));
AddAssert("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == true));
AddUntilStep("wait for score panel removal", () => scorePanel.Parent == null);
}
[Test]
public void TestDeleteScoreAfterPlaying()
{
playToResults();
ScoreInfo score = null;
LeaderboardScore scorePanel = null;
AddStep("get score", () => score = ((ResultsScreen)Game.ScreenStack.CurrentScreen).Score);
AddAssert("ensure score is databased", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == false));
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action());
AddStep("show local scores", () => Game.ChildrenOfType<BeatmapDetailAreaTabControl>().First().Current.Value = new BeatmapDetailAreaLeaderboardTabItem<BeatmapLeaderboardScope>(BeatmapLeaderboardScope.Local));
AddUntilStep("wait for score displayed", () => (scorePanel = Game.ChildrenOfType<LeaderboardScore>().FirstOrDefault(s => s.Score.Equals(score))) != null);
AddStep("right click panel", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
InputManager.MoveMouseTo(scorePanel);
InputManager.Click(MouseButton.Right);
});
AddUntilStep("wait for track playing", () => beatmap().Track.IsRunning);
AddStep("seek to near end", () => player.ChildrenOfType<GameplayClockContainer>().First().Seek(beatmap().Beatmap.HitObjects[^1].StartTime - 1000));
AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded);
AddStep("attempt to retry", () => results.ChildrenOfType<HotkeyRetryOverlay>().First().Action());
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != player && Game.ScreenStack.CurrentScreen is Player);
AddStep("click delete", () =>
{
var dropdownItem = Game
.ChildrenOfType<PlayBeatmapDetailArea>().First()
.ChildrenOfType<OsuContextMenu>().First()
.ChildrenOfType<DrawableOsuMenuItem>().First(i => i.Item.Text.ToString() == "Delete");
InputManager.MoveMouseTo(dropdownItem);
InputManager.Click(MouseButton.Left);
});
AddStep("confirm deletion", () => InputManager.Key(Key.Number1));
AddAssert("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == true));
AddUntilStep("wait for score panel removal", () => scorePanel.Parent == null);
}
[TestCase(true)]
@ -432,6 +482,37 @@ public void TestExitGameFromSongSelect()
AddStep("test dispose doesn't crash", () => Game.Dispose());
}
private Func<Player> playToResults()
{
Player player = null;
IWorkingBeatmap beatmap() => Game.Beatmap.Value;
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
AddStep("set mods", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModDoubleTime { SpeedChange = { Value = 2 } } });
AddStep("press enter", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
});
AddUntilStep("wait for track playing", () => beatmap().Track.IsRunning);
AddStep("seek to near end", () => player.ChildrenOfType<GameplayClockContainer>().First().Seek(beatmap().Beatmap.HitObjects[^1].StartTime - 1000));
AddUntilStep("wait for pass", () => (Game.ScreenStack.CurrentScreen as ResultsScreen)?.IsLoaded == true);
return () => player;
}
private void clickMouseInCentre()
{
InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre);

View File

@ -266,6 +266,15 @@ public void Delete([CanBeNull] Expression<Func<ScoreInfo, bool>> filter = null,
});
}
public void Delete(BeatmapInfo beatmap, bool silent = false)
{
realm.Run(r =>
{
var beatmapScores = r.Find<BeatmapInfo>(beatmap.ID).Scores.ToList();
scoreModelManager.Delete(beatmapScores, silent);
});
}
public void Delete(List<ScoreInfo> items, bool silent = false)
{
scoreModelManager.Delete(items, silent);

View File

@ -28,7 +28,7 @@ public BeatmapClearScoresDialog(BeatmapInfo beatmapInfo, Action onCompletion)
Text = @"Yes. Please.",
Action = () =>
{
Task.Run(() => scoreManager.Delete(s => !s.DeletePending && s.BeatmapInfo.ID == beatmapInfo.ID))
Task.Run(() => scoreManager.Delete(beatmapInfo))
.ContinueWith(_ => onCompletion);
}
},

View File

@ -14,6 +14,7 @@
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Overlays;
@ -111,6 +112,8 @@ public class TestOsuGame : OsuGame
public new ScreenStack ScreenStack => base.ScreenStack;
public RealmAccess Realm => Dependencies.Get<RealmAccess>();
public new BackButton BackButton => base.BackButton;
public new BeatmapManager BeatmapManager => base.BeatmapManager;
@ -158,6 +161,14 @@ protected override void LoadComplete()
Dependencies.Get<SessionStatics>().SetValue(Static.MutedAudioNotificationShownOnce, true);
}
protected override void Update()
{
base.Update();
// when running in visual tests and the window loses focus, we generally don't want the game to pause.
((Bindable<bool>)IsActive).Value = true;
}
}
public class TestLoader : Loader