1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 20:07:25 +08:00

Merge pull request #18494 from peppy/fix-dummmy-api-request-firing-2

Fix `OnlinePlayTestScene` request handlers potentially running requests post-disposal
This commit is contained in:
Dan Balasescu 2022-06-02 13:20:41 +09:00 committed by GitHub
commit ea48ce1d24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 32 additions and 8 deletions

View File

@ -469,6 +469,8 @@ namespace osu.Game.Tests.Visual.Online
chatOverlay.Show(); chatOverlay.Show();
}); });
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
waitForChannel1Visible(); waitForChannel1Visible();
AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext));
waitForChannel2Visible(); waitForChannel2Visible();

View File

@ -173,6 +173,8 @@ namespace osu.Game.Tests.Visual.Playlists
{ {
AddUntilStep("wait for scores loaded", () => AddUntilStep("wait for scores loaded", () =>
requestComplete requestComplete
// request handler may need to fire more than once to get scores.
&& totalCount > 0
&& resultsScreen.ScorePanelList.GetScorePanels().Count() == totalCount && resultsScreen.ScorePanelList.GetScorePanels().Count() == totalCount
&& resultsScreen.ScorePanelList.AllPanelsVisible); && resultsScreen.ScorePanelList.AllPanelsVisible);
AddWaitStep("wait for display", 5); AddWaitStep("wait for display", 5);

View File

@ -91,7 +91,7 @@ namespace osu.Game.Database
} }
} }
private void performLookup() private async Task performLookup()
{ {
// contains at most 50 unique IDs from tasks, which is used to perform the lookup. // contains at most 50 unique IDs from tasks, which is used to perform the lookup.
var nextTaskBatch = new Dictionary<TLookup, List<TaskCompletionSource<TValue>>>(); var nextTaskBatch = new Dictionary<TLookup, List<TaskCompletionSource<TValue>>>();
@ -127,7 +127,7 @@ namespace osu.Game.Database
// rather than queueing, we maintain our own single-threaded request stream. // rather than queueing, we maintain our own single-threaded request stream.
// todo: we probably want retry logic here. // todo: we probably want retry logic here.
api.Perform(request); await api.PerformAsync(request).ConfigureAwait(false);
finishPendingTask(); finishPendingTask();

View File

@ -62,13 +62,14 @@ namespace osu.Game.Online.API
} }
public virtual void Queue(APIRequest request) public virtual void Queue(APIRequest request)
{
Schedule(() =>
{ {
if (HandleRequest?.Invoke(request) != true) if (HandleRequest?.Invoke(request) != true)
{ {
// this will fail due to not receiving an APIAccess, and trigger a failure on the request. request.Fail(new InvalidOperationException($@"{nameof(DummyAPIAccess)} cannot process this request."));
// this is intended - any request in testing that needs non-failures should use HandleRequest.
request.Perform(this);
} }
});
} }
public void Perform(APIRequest request) => HandleRequest?.Invoke(request); public void Perform(APIRequest request) => HandleRequest?.Invoke(request);

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -67,7 +68,25 @@ namespace osu.Game.Tests.Visual.OnlinePlay
// To get around this, the BeatmapManager is looked up from the dependencies provided to the children of the test scene instead. // To get around this, the BeatmapManager is looked up from the dependencies provided to the children of the test scene instead.
var beatmapManager = dependencies.Get<BeatmapManager>(); var beatmapManager = dependencies.Get<BeatmapManager>();
((DummyAPIAccess)API).HandleRequest = request => handler.HandleRequest(request, API.LocalUser.Value, beatmapManager); ((DummyAPIAccess)API).HandleRequest = request =>
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
// Because some of the handlers use realm, we need to ensure the game is still alive when firing.
// If we don't, a stray `PerformAsync` could hit an `ObjectDisposedException` if running too late.
Scheduler.Add(() =>
{
bool result = handler.HandleRequest(request, API.LocalUser.Value, beatmapManager);
tcs.SetResult(result);
}, false);
#pragma warning disable RS0030
// We can't GetResultSafely() here (will fail with "Can't use GetResultSafely from inside an async operation."), but Wait is safe enough due to
// the task being a TaskCompletionSource.
// Importantly, this doesn't deadlock because of the scheduler call above running inline where feasible (see the `false` argument).
return tcs.Task.Result;
#pragma warning restore RS0030
};
}); });
/// <summary> /// <summary>