diff --git a/osu.Game.Tests/Database/RulesetStoreTests.cs b/osu.Game.Tests/Database/RulesetStoreTests.cs index ddf207342a..29aec73770 100644 --- a/osu.Game.Tests/Database/RulesetStoreTests.cs +++ b/osu.Game.Tests/Database/RulesetStoreTests.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealm((realm, storage) => { - var rulesets = new RealmRulesetStore(realm, storage); + using var rulesets = new RealmRulesetStore(realm, storage); Assert.AreEqual(4, rulesets.AvailableRulesets.Count()); Assert.AreEqual(4, realm.Realm.All().Count()); @@ -36,8 +36,8 @@ namespace osu.Game.Tests.Database { RunTestWithRealm((realm, storage) => { - var rulesets = new RealmRulesetStore(realm, storage); - var rulesets2 = new RealmRulesetStore(realm, storage); + using var rulesets = new RealmRulesetStore(realm, storage); + using var rulesets2 = new RealmRulesetStore(realm, storage); Assert.AreEqual(4, rulesets.AvailableRulesets.Count()); Assert.AreEqual(4, rulesets2.AvailableRulesets.Count()); @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Database { RunTestWithRealm((realm, storage) => { - var rulesets = new RealmRulesetStore(realm, storage); + using var rulesets = new RealmRulesetStore(realm, storage); Assert.IsFalse(rulesets.AvailableRulesets.First().IsManaged); Assert.IsFalse(rulesets.GetRuleset(0)?.IsManaged); @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Database Assert.That(realm.Run(r => r.Find(rulesetShortName)!.Available), Is.True); // Availability is updated on construction of a RealmRulesetStore - _ = new RealmRulesetStore(realm, storage); + using var _ = new RealmRulesetStore(realm, storage); Assert.That(realm.Run(r => r.Find(rulesetShortName)!.Available), Is.False); }); @@ -104,13 +104,13 @@ namespace osu.Game.Tests.Database Assert.That(realm.Run(r => r.Find(rulesetShortName)!.Available), Is.True); // Availability is updated on construction of a RealmRulesetStore - _ = new RealmRulesetStore(realm, storage); + using var _ = new RealmRulesetStore(realm, storage); Assert.That(realm.Run(r => r.Find(rulesetShortName)!.Available), Is.False); // Simulate the ruleset getting updated LoadTestRuleset.Version = Ruleset.CURRENT_RULESET_API_VERSION; - _ = new RealmRulesetStore(realm, storage); + using var __ = new RealmRulesetStore(realm, storage); Assert.That(realm.Run(r => r.Find(rulesetShortName)!.Available), Is.True); }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs index 046ae6d953..0e6fd8f519 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs @@ -257,6 +257,14 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("score in database", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID) != null)); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } + private class CustomRuleset : OsuRuleset, ILegacyRuleset { public override string Description => "custom"; diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 0e8093f459..184bb33c2f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -36,6 +37,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private BeatmapManager beatmaps = null!; private BeatmapSetInfo importedSet = null!; + private RulesetStore rulesets = null!; private TestMultiplayerComponents multiplayerComponents = null!; @@ -46,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { BeatmapStore beatmapStore; - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default)); Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore()); Dependencies.Cache(Realm); @@ -115,5 +117,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for player", () => multiplayerComponents.CurrentScreen is Player player && player.IsLoaded); AddStep("exit player", () => multiplayerComponents.MultiplayerScreen.MakeCurrent()); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 7e19f45a00..c8216c54be 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -8,6 +8,7 @@ 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; @@ -37,13 +38,14 @@ 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(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); } @@ -436,6 +438,14 @@ namespace osu.Game.Tests.Visual.Multiplayer 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> ItemMap => base.ItemMap; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 083b5b14fb..4c487c8288 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -51,6 +51,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneMultiplayer : ScreenTestScene { + private RulesetStore rulesets = null!; private BeatmapManager beatmaps = null!; private BeatmapSetInfo importedSet = null!; private BeatmapSetInfo importedSet2 = null!; @@ -67,7 +68,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { BeatmapStore beatmapStore; - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, API, audio, Resources, host, Beatmap.Default)); Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore()); Dependencies.Cache(Realm); @@ -1247,5 +1248,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for join", () => multiplayerClient.RoomJoined); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 9c85bdd57a..e6f3d7e5ac 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Platform; using osu.Framework.Screens; @@ -170,6 +171,14 @@ namespace osu.Game.Tests.Visual.Multiplayer .All(b => b.Mod.GetType() != type)); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } + private partial class TestMultiplayerMatchSongSelect : MultiplayerMatchSongSelect { public new Bindable> Mods => base.Mods; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index aa4c4949fb..792bff63d3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Platform; @@ -44,6 +45,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public partial class TestSceneMultiplayerMatchSubScreen : MultiplayerTestScene { private MultiplayerMatchSubScreen screen = null!; + private RulesetStore rulesets = null!; private BeatmapManager beatmaps = null!; private BeatmapSetInfo importedSet = null!; private Room room = null!; @@ -51,7 +53,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); Dependencies.CacheAs(new RealmDetachedBeatmapStore()); @@ -462,6 +464,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("settings still open", () => this.ChildrenOfType().Single().State.Value, () => Is.EqualTo(Visibility.Visible)); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } + private partial class TestMultiplayerMatchSubScreen : MultiplayerMatchSubScreen { [Resolved(canBeNull: true)] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index c6a203c77a..44177c080c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Testing; @@ -28,6 +29,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public partial class TestSceneMultiplayerPlaylist : MultiplayerTestScene { private MultiplayerPlaylist list = null!; + private RulesetStore rulesets = null!; private BeatmapManager beatmaps = null!; private BeatmapSetInfo importedSet = null!; private BeatmapInfo importedBeatmap = null!; @@ -35,7 +37,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); } @@ -290,5 +292,13 @@ namespace osu.Game.Tests.Visual.Multiplayer .Single() .Items.Any(i => i.ID == playlistItemId); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index d7659351bb..2f54551fa8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Platform; @@ -26,6 +27,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public partial class TestSceneMultiplayerQueueList : MultiplayerTestScene { private MultiplayerQueueList playlist = null!; + private RulesetStore rulesets = null!; private BeatmapManager beatmaps = null!; private BeatmapSetInfo importedSet = null!; private BeatmapInfo importedBeatmap = null!; @@ -34,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, API, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); } @@ -168,5 +170,13 @@ namespace osu.Game.Tests.Visual.Multiplayer var button = playlist.ChildrenOfType().ElementAtOrDefault(index); return (button?.Alpha > 0) == visible; }); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs index 12bc3c1418..fe9ea632cf 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -32,12 +33,13 @@ namespace osu.Game.Tests.Visual.Multiplayer private Room room = null!; private BeatmapSetInfo importedSet = null!; + private RulesetStore rulesets = null!; private BeatmapManager beatmaps = null!; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); @@ -162,5 +164,13 @@ namespace osu.Game.Tests.Visual.Multiplayer private void assertReadyButtonEnablement(bool shouldBeEnabled) => AddUntilStep($"ready button {(shouldBeEnabled ? "is" : "is not")} enabled", () => startControl.ChildrenOfType().Single().Enabled.Value == shouldBeEnabled); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs index 066c981cd2..7135ff930d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Platform; @@ -31,6 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestScenePlaylistsSongSelect : OnlinePlayTestScene { + private RulesetStore rulesets = null!; private BeatmapManager manager = null!; private TestPlaylistsSongSelect songSelect = null!; private Room room = null!; @@ -40,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { BeatmapStore beatmapStore; - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default)); Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore()); Dependencies.Cache(Realm); @@ -189,6 +191,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("mod select visible", () => this.ChildrenOfType().Single().State.Value, () => Is.EqualTo(Visibility.Visible)); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } + private partial class TestPlaylistsSongSelect : PlaylistsSongSelect { public new MatchBeatmapDetailArea BeatmapDetails => (MatchBeatmapDetailArea)base.BeatmapDetails; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 05136ebee1..2e08b494bd 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -27,6 +28,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneTeamVersus : ScreenTestScene { + private RulesetStore rulesets = null!; private BeatmapManager beatmaps = null!; private BeatmapSetInfo importedSet = null!; @@ -37,7 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); } @@ -182,5 +184,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for join", () => multiplayerClient.RoomJoined); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/Playlists/TestSceneAddPlaylistToCollectionButton.cs b/osu.Game.Tests/Visual/Playlists/TestSceneAddPlaylistToCollectionButton.cs index abfc5c4d0e..7a11581d27 100644 --- a/osu.Game.Tests/Visual/Playlists/TestSceneAddPlaylistToCollectionButton.cs +++ b/osu.Game.Tests/Visual/Playlists/TestSceneAddPlaylistToCollectionButton.cs @@ -7,6 +7,7 @@ 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.Platform; using osu.Framework.Testing; @@ -25,6 +26,7 @@ namespace osu.Game.Tests.Visual.Playlists { public partial class TestSceneAddPlaylistToCollectionButton : OsuManualInputManagerTestScene { + private RulesetStore rulesets = null!; private BeatmapManager manager = null!; private BeatmapSetInfo importedBeatmap = null!; private Room room = null!; @@ -33,7 +35,7 @@ namespace osu.Game.Tests.Visual.Playlists [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, API, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); @@ -112,5 +114,13 @@ namespace osu.Game.Tests.Visual.Playlists } ]; }); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index 2e90f08d47..44c2e7eb55 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -32,6 +33,7 @@ namespace osu.Game.Tests.Visual.Playlists { public partial class TestScenePlaylistsRoomCreation : OnlinePlayTestScene { + private RulesetStore rulesets = null!; private BeatmapManager manager = null!; private TestPlaylistsRoomSubScreen match = null!; private BeatmapSetInfo importedBeatmap = null!; @@ -40,7 +42,7 @@ namespace osu.Game.Tests.Visual.Playlists [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, API, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); } @@ -220,6 +222,14 @@ namespace osu.Game.Tests.Visual.Playlists }); }); + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } + private partial class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen { public new Bindable SelectedItem => base.SelectedItem; diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index 0eed6c9f5f..87f65111b0 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Platform; @@ -38,6 +39,7 @@ namespace osu.Game.Tests.Visual.Playlists { public partial class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene { + private RulesetStore rulesets = null!; private BeatmapManager beatmaps = null!; private BeatmapSetInfo importedSet = null!; @@ -46,7 +48,7 @@ namespace osu.Game.Tests.Visual.Playlists { BeatmapStore beatmapStore; - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, API, audio, Resources, host, Beatmap.Default)); Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore()); Dependencies.Cache(Realm); @@ -579,6 +581,14 @@ namespace osu.Game.Tests.Visual.Playlists AddUntilStep("mods set", () => SelectedMods.Value.Count == 1 && SelectedMods.Value.OfType().Any()); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } + private partial class TestPlaylistsScreen : OsuScreen { public TestPlaylistsScreen(PlaylistsRoomSubScreen screen) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneSoloResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneSoloResultsScreen.cs index cd8f234f04..e86ed8cd89 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneSoloResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneSoloResultsScreen.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Testing; @@ -531,5 +532,13 @@ namespace osu.Game.Tests.Visual.Ranking AddAssert("only one score with ID 12345", () => this.ChildrenOfType().Count(s => s.Score.OnlineID == 12345), () => Is.EqualTo(1)); AddUntilStep("user best position preserved", () => this.ChildrenOfType().Any(p => p.ScorePosition.Value == 133_337)); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesetStore.IsNotNull()) + rulesetStore.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs index b682ec7265..88e381a468 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs @@ -12,6 +12,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -219,8 +220,15 @@ namespace osu.Game.Tests.Visual.Ranking Tags = [ new APITag { Id = 1, Name = "song representation/simple", Description = "Accessible and straightforward map design.", }, - new APITag { Id = 2, Name = "style/clean", Description = "Visually uncluttered and organised patterns, often involving few overlaps and equal visual spacing between objects.", }, - new APITag { Id = 3, Name = "aim/aim control", Description = "Patterns with velocity or direction changes which strongly go against a player's natural movement pattern.", }, + new APITag + { + Id = 2, Name = "style/clean", + Description = "Visually uncluttered and organised patterns, often involving few overlaps and equal visual spacing between objects.", + }, + new APITag + { + Id = 3, Name = "aim/aim control", Description = "Patterns with velocity or direction changes which strongly go against a player's natural movement pattern.", + }, new APITag { Id = 4, Name = "tap/bursts", Description = "Patterns requiring continuous movement and alternating, typically 9 notes or less.", }, ] }), 500); @@ -403,6 +411,14 @@ namespace osu.Game.Tests.Visual.Ranking return hitEvents; } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesetStore.IsNotNull()) + rulesetStore?.Dispose(); + } + private class TestRuleset : Ruleset { public override IEnumerable GetModsFor(ModType type) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneCollectionDropdown.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneCollectionDropdown.cs index 8fcbcb2fbc..8525e33a33 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneCollectionDropdown.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneCollectionDropdown.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -28,6 +29,7 @@ namespace osu.Game.Tests.Visual.SongSelect { public partial class TestSceneCollectionDropdown : OsuManualInputManagerTestScene { + private RulesetStore rulesets = null!; private BeatmapManager beatmapManager = null!; private CollectionDropdown dropdown = null!; @@ -37,7 +39,7 @@ namespace osu.Game.Tests.Visual.SongSelect [BackgroundDependencyLoader] private void load(GameHost host) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, Audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); @@ -269,5 +271,13 @@ namespace osu.Game.Tests.Visual.SongSelect CollectionFilterMenuItem item = dropdown.ChildrenOfType().Single().ItemSource.ElementAt(index); return dropdown.ChildrenOfType().Single(i => i.Item.Text.Value == item.CollectionName); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneManageCollectionsDialog.cs index 475d8ec461..b690bb2708 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneManageCollectionsDialog.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; @@ -27,13 +28,14 @@ namespace osu.Game.Tests.Visual.SongSelect protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; private DialogOverlay dialogOverlay = null!; + private RulesetStore rulesets = null!; private BeatmapManager beatmapManager = null!; private ManageCollectionsDialog dialog = null!; [BackgroundDependencyLoader] private void load(GameHost host) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, Audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); @@ -379,5 +381,13 @@ namespace osu.Game.Tests.Visual.SongSelect private void assertCollectionName(int index, string name) => AddUntilStep($"item {index + 1} has correct name", () => dialog.ChildrenOfType().Single().OrderedItems.ElementAtOrDefault(index)?.ChildrenOfType().First().Text == name); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs index 93b9efed6a..cb0845ede8 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Testing; @@ -211,5 +212,13 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("No rank displayed", () => topLocalRank.DisplayedRank, () => Is.Null); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs b/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs index e3b02e5905..ac8591699a 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs @@ -190,5 +190,13 @@ namespace osu.Game.Tests.Visual.SongSelectV2 } protected void WaitForSuspension() => AddUntilStep("wait for not current", () => !SongSelect.AsNonNull().IsCurrentScreen()); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (Rulesets.IsNotNull()) + Rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardSorting.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardSorting.cs index 6e3fafdd6a..a37700f6be 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardSorting.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardSorting.cs @@ -151,5 +151,13 @@ namespace osu.Game.Tests.Visual.SongSelectV2 }, }); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesetStore.IsNotNull()) + rulesetStore.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardWedge.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardWedge.cs index 8fcb3d7acc..1c3a5e4bab 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardWedge.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapLeaderboardWedge.cs @@ -578,5 +578,13 @@ namespace osu.Game.Tests.Visual.SongSelectV2 }, }; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesetStore.IsNotNull()) + rulesetStore.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneCollectionDropdown.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneCollectionDropdown.cs index 774d4a00ce..8cee78e0b8 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneCollectionDropdown.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneCollectionDropdown.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -29,6 +30,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { public partial class TestSceneCollectionDropdown : OsuManualInputManagerTestScene { + private RulesetStore rulesets = null!; private BeatmapManager beatmapManager = null!; private CollectionDropdown dropdown = null!; @@ -38,7 +40,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 [BackgroundDependencyLoader] private void load(GameHost host) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, Audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); @@ -260,5 +262,13 @@ namespace osu.Game.Tests.Visual.SongSelectV2 CollectionFilterMenuItem item = dropdown.ChildrenOfType().Single().ItemSource.ElementAt(index); return dropdown.ChildrenOfType().Single(i => i.Item.Text.Value == item.CollectionName); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index f7bdda6b57..c2277f2c7c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -9,6 +9,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Platform; @@ -37,6 +38,7 @@ namespace osu.Game.Tests.Visual.UserInterface private readonly ContextMenuContainer contextMenuContainer; private readonly BeatmapLeaderboard leaderboard; + private RulesetStore rulesets = null!; private BeatmapManager beatmapManager; private ScoreManager scoreManager; @@ -71,7 +73,7 @@ namespace osu.Game.Tests.Visual.UserInterface { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.Cache(new RealmRulesetStore(Realm)); + dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); dependencies.Cache(scoreManager = new ScoreManager(dependencies.Get(), () => beatmapManager, LocalStorage, Realm, API)); Dependencies.Cache(Realm); @@ -151,7 +153,8 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("click delete option", () => { - InputManager.MoveMouseTo(contextMenuContainer.ChildrenOfType().First(i => string.Equals(i.Item.Text.Value.ToString(), "delete", System.StringComparison.OrdinalIgnoreCase))); + InputManager.MoveMouseTo(contextMenuContainer.ChildrenOfType() + .First(i => string.Equals(i.Item.Text.Value.ToString(), "delete", System.StringComparison.OrdinalIgnoreCase))); InputManager.Click(MouseButton.Left); }); @@ -178,5 +181,13 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for fetch", () => leaderboard.Scores.Any()); AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineID != importedScores[0].OnlineID)); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs index b7c1428397..c202442f9c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs @@ -469,5 +469,13 @@ namespace osu.Game.Tests.Visual.UserInterface Ruleset = rulesets.GetRuleset(3).AsNonNull() } }; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 017d246461..6127be481c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; @@ -1057,6 +1058,14 @@ namespace osu.Game.Tests.Visual.UserInterface private ModPanel getPanelForMod(Type modType) => modSelectOverlay.ChildrenOfType().Single(panel => panel.Mod.GetType() == modType); + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesetStore.IsNotNull()) + rulesetStore.Dispose(); + } + private partial class TestModSelectOverlay : UserModSelectOverlay { public TestModSelectOverlay() diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs index 2672854e19..b6d9bad5bb 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Platform; @@ -21,6 +22,7 @@ namespace osu.Game.Tests.Visual.UserInterface { protected override bool UseFreshStoragePerRun => true; + private RulesetStore rulesets = null!; private BeatmapManager beatmapManager = null!; private const int item_count = 20; @@ -30,7 +32,7 @@ namespace osu.Game.Tests.Visual.UserInterface [BackgroundDependencyLoader] private void load(GameHost host) { - Dependencies.Cache(new RealmRulesetStore(Realm)); + Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, Audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); } @@ -62,5 +64,13 @@ namespace osu.Game.Tests.Visual.UserInterface // Ensure all the initial imports are present before running any tests. Realm.Run(r => r.Refresh()); }); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesets.IsNotNull()) + rulesets.Dispose(); + } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 75fdc7d7e8..222acbc039 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -280,8 +280,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards } createStatistics(); - - Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(BeatmapSet.OnlineID); } private LocalisableString createArtistText() diff --git a/osu.Game/Database/RealmDetachedBeatmapStore.cs b/osu.Game/Database/RealmDetachedBeatmapStore.cs index 6954bb320a..f9f84c52e5 100644 --- a/osu.Game/Database/RealmDetachedBeatmapStore.cs +++ b/osu.Game/Database/RealmDetachedBeatmapStore.cs @@ -82,6 +82,34 @@ namespace osu.Game.Database return; } + if (changes.InsertedIndices.Length == 1 && changes.DeletedIndices.Length == 1) + { + lock (detachedBeatmapSets) + { + var deletedSet = detachedBeatmapSets[changes.DeletedIndices[0]]; + var insertedSet = sender[changes.InsertedIndices[0]]; + + // this handles beatmap updates using a heuristic that a beatmap update will preserve the online ID. + // it relies on the fact that updates are performed by removing the old set and adding a new one, in a single transaction. + // instead of removing the old set and adding a new one to the collection too, which would trigger consumers' logic related to set removals, + // move the deleted set to the index occupied by the new one and then replace it in-place. + // due to this, the operation can be presented to consumer in a manner that permits them to actually handle this as a replace operation + // and not trigger any set removal logic that may result in selections changing or similar undesirable side effects. + if (deletedSet.OnlineID == insertedSet.OnlineID) + { + pendingOperations.Enqueue(new OperationArgs + { + Type = OperationType.MoveAndReplace, + BeatmapSet = insertedSet.Detach(), + Index = changes.DeletedIndices[0], + NewIndex = changes.InsertedIndices[0], + }); + + return; + } + } + } + foreach (int i in changes.DeletedIndices.OrderDescending()) { pendingOperations.Enqueue(new OperationArgs @@ -138,6 +166,11 @@ namespace osu.Game.Database detachedBeatmapSets.ReplaceRange(op.Index, 1, new[] { op.BeatmapSet! }); break; + case OperationType.MoveAndReplace: + detachedBeatmapSets.Move(op.Index, op.NewIndex!.Value); + detachedBeatmapSets.ReplaceRange(op.NewIndex!.Value, 1, [op.BeatmapSet!]); + break; + case OperationType.Remove: detachedBeatmapSets.RemoveAt(op.Index); break; @@ -160,13 +193,15 @@ namespace osu.Game.Database public OperationType Type; public BeatmapSetInfo? BeatmapSet; public int Index; + public int? NewIndex; } private enum OperationType { Insert, Update, - Remove + Remove, + MoveAndReplace, } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 221282ef13..5b75b8419c 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -77,6 +78,8 @@ namespace osu.Game.Overlays.Toolbar protected readonly Container BackgroundContent; + private IDisposable? realmSubscription; + [Resolved] private RealmAccess realm { get; set; } = null!; @@ -184,7 +187,8 @@ namespace osu.Game.Overlays.Toolbar { if (Hotkey != null) { - realm.SubscribeToPropertyChanged(r => r.All().FirstOrDefault(rkb => rkb.RulesetName == null && rkb.ActionInt == (int)Hotkey.Value), kb => kb.KeyCombinationString, updateKeyBindingTooltip); + realmSubscription = realm.SubscribeToPropertyChanged(r => r.All().FirstOrDefault(rkb => rkb.RulesetName == null && rkb.ActionInt == (int)Hotkey.Value), + kb => kb.KeyCombinationString, updateKeyBindingTooltip); } } @@ -234,6 +238,13 @@ namespace osu.Game.Overlays.Toolbar ? $" ({keyBindingString})" : string.Empty; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + realmSubscription?.Dispose(); + } } public partial class OpaqueBackground : Container diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 9ccb8170f3..0d75ddb0f0 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -237,26 +237,29 @@ namespace osu.Game.Screens.Select private void beatmapSetsChanged(object? beatmaps, NotifyCollectionChangedEventArgs changed) { + IEnumerable? oldBeatmapSets = changed.OldItems?.Cast(); + HashSet oldBeatmapSetIDs = oldBeatmapSets?.Select(s => s.ID).ToHashSet() ?? []; + IEnumerable? newBeatmapSets = changed.NewItems?.Cast(); + HashSet newBeatmapSetIDs = newBeatmapSets?.Select(s => s.ID).ToHashSet() ?? []; switch (changed.Action) { case NotifyCollectionChangedAction.Add: - HashSet newBeatmapSetIDs = newBeatmapSets!.Select(s => s.ID).ToHashSet(); - setsRequiringRemoval.RemoveWhere(s => newBeatmapSetIDs.Contains(s.ID)); setsRequiringUpdate.AddRange(newBeatmapSets!); break; case NotifyCollectionChangedAction.Remove: - IEnumerable oldBeatmapSets = changed.OldItems!.Cast(); - HashSet oldBeatmapSetIDs = oldBeatmapSets.Select(s => s.ID).ToHashSet(); - setsRequiringUpdate.RemoveWhere(s => oldBeatmapSetIDs.Contains(s.ID)); - setsRequiringRemoval.AddRange(oldBeatmapSets); + setsRequiringRemoval.AddRange(oldBeatmapSets!); break; case NotifyCollectionChangedAction.Replace: + setsRequiringUpdate.RemoveWhere(s => oldBeatmapSetIDs.Contains(s.ID)); + setsRequiringRemoval.AddRange(oldBeatmapSets!); + + setsRequiringRemoval.RemoveWhere(s => newBeatmapSetIDs.Contains(s.ID)); setsRequiringUpdate.AddRange(newBeatmapSets!); break; diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs index c687815270..75932bbfef 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs @@ -32,9 +32,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay protected override Container Content => content; - [Resolved] - private RulesetStore rulesets { get; set; } = null!; - private readonly Container content; private readonly Container drawableDependenciesContainer; private DelegatedDependencyContainer dependencies = null!; @@ -100,7 +97,11 @@ namespace osu.Game.Tests.Visual.OnlinePlay Room[] rooms = new Room[count]; // Can't reference Osu ruleset project here. - ruleset ??= rulesets.GetRuleset(0)!; + if (ruleset == null) + { + using var assemblyRulesetStore = new AssemblyRulesetStore(); + ruleset = assemblyRulesetStore.GetRuleset(0)!; + } for (int i = 0; i < count; i++) { diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 1cb7b2c840..9b0b66a18c 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -332,6 +332,9 @@ namespace osu.Game.Tests.Visual if (MusicController?.TrackLoaded == true) MusicController.Stop(); + if (realm?.IsValueCreated == true) + Realm.Dispose(); + RecycleLocalStorage(true); }