2021-06-25 12:02:19 +08:00
// 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.
2022-06-17 15:37:17 +08:00
#nullable disable
2021-06-25 12:02:19 +08:00
using System ;
2022-05-30 15:33:07 +08:00
using System.Threading.Tasks ;
2021-06-25 12:02:19 +08:00
using NUnit.Framework ;
using osu.Framework.Allocation ;
using osu.Framework.Bindables ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2022-02-15 20:05:05 +08:00
using osu.Game.Database ;
2022-02-15 21:02:33 +08:00
using osu.Game.Beatmaps ;
2021-10-27 13:49:50 +08:00
using osu.Game.Online.API ;
2021-06-25 12:02:19 +08:00
using osu.Game.Online.Rooms ;
using osu.Game.Screens.OnlinePlay ;
namespace osu.Game.Tests.Visual.OnlinePlay
{
/// <summary>
2021-06-25 17:07:47 +08:00
/// A base test scene for all online play components and screens.
2021-06-25 12:02:19 +08:00
/// </summary>
2021-06-25 19:15:30 +08:00
public abstract class OnlinePlayTestScene : ScreenTestScene , IOnlinePlayTestSceneDependencies
2021-06-25 12:02:19 +08:00
{
2021-06-25 17:07:47 +08:00
public Bindable < Room > SelectedRoom = > OnlinePlayDependencies ? . SelectedRoom ;
public IRoomManager RoomManager = > OnlinePlayDependencies ? . RoomManager ;
public OngoingOperationTracker OngoingOperationTracker = > OnlinePlayDependencies ? . OngoingOperationTracker ;
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker = > OnlinePlayDependencies ? . AvailabilityTracker ;
2022-02-15 19:56:46 +08:00
public TestUserLookupCache UserLookupCache = > OnlinePlayDependencies ? . UserLookupCache ;
2022-02-15 20:05:05 +08:00
public BeatmapLookupCache BeatmapLookupCache = > OnlinePlayDependencies ? . BeatmapLookupCache ;
2021-06-25 12:02:19 +08:00
2021-06-25 17:07:47 +08:00
/// <summary>
/// All dependencies required for online play components and screens.
/// </summary>
2021-06-25 19:11:38 +08:00
protected OnlinePlayTestSceneDependencies OnlinePlayDependencies = > dependencies ? . OnlinePlayDependencies ;
2021-06-25 17:07:47 +08:00
2021-06-25 12:02:19 +08:00
protected override Container < Drawable > Content = > content ;
2021-10-27 13:49:50 +08:00
2021-06-25 12:02:19 +08:00
private readonly Container content ;
private readonly Container drawableDependenciesContainer ;
2021-10-27 13:49:50 +08:00
private DelegatedDependencyContainer dependencies ;
2021-06-25 12:02:19 +08:00
protected OnlinePlayTestScene ( )
{
base . Content . AddRange ( new Drawable [ ]
{
drawableDependenciesContainer = new Container { RelativeSizeAxes = Axes . Both } ,
content = new Container { RelativeSizeAxes = Axes . Both } ,
} ) ;
}
2022-02-15 19:55:56 +08:00
protected sealed override IReadOnlyDependencyContainer CreateChildDependencies ( IReadOnlyDependencyContainer parent )
2021-06-25 12:02:19 +08:00
{
2021-06-25 17:07:47 +08:00
dependencies = new DelegatedDependencyContainer ( base . CreateChildDependencies ( parent ) ) ;
return dependencies ;
2021-06-25 12:02:19 +08:00
}
[SetUp]
public void Setup ( ) = > Schedule ( ( ) = >
{
// Reset the room dependencies to a fresh state.
drawableDependenciesContainer . Clear ( ) ;
2021-06-25 17:07:47 +08:00
dependencies . OnlinePlayDependencies = CreateOnlinePlayDependencies ( ) ;
drawableDependenciesContainer . AddRange ( OnlinePlayDependencies . DrawableComponents ) ;
2021-10-28 15:48:17 +08:00
2022-02-15 22:42:37 +08:00
var handler = OnlinePlayDependencies . RequestsHandler ;
2022-02-15 21:02:33 +08:00
2022-02-15 22:42:37 +08:00
// Resolving the BeatmapManager in the test scene will inject the game-wide BeatmapManager, while many test scenes cache their own BeatmapManager 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 > ( ) ;
2022-02-15 21:02:33 +08:00
2022-05-30 15:33:07 +08:00
( ( 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.
2022-05-31 11:24:44 +08:00
// Importantly, this doesn't deadlock because of the scheduler call above running inline where feasible (see the `false` argument).
2022-05-30 15:33:07 +08:00
return tcs . Task . Result ;
#pragma warning restore RS0030
} ;
2022-02-15 22:42:37 +08:00
} ) ;
2021-10-27 13:49:50 +08:00
2021-06-25 12:02:19 +08:00
/// <summary>
/// Creates the room dependencies. Called every <see cref="Setup"/>.
/// </summary>
/// <remarks>
2021-06-25 17:07:47 +08:00
/// Any custom dependencies required for online play sub-classes should be added here.
2021-06-25 12:02:19 +08:00
/// </remarks>
2021-06-25 19:11:38 +08:00
protected virtual OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies ( ) = > new OnlinePlayTestSceneDependencies ( ) ;
2021-06-25 12:02:19 +08:00
/// <summary>
2021-06-25 17:07:47 +08:00
/// A <see cref="IReadOnlyDependencyContainer"/> providing a mutable lookup source for online play dependencies.
2021-06-25 12:02:19 +08:00
/// </summary>
2021-06-25 17:07:47 +08:00
private class DelegatedDependencyContainer : IReadOnlyDependencyContainer
2021-06-25 12:02:19 +08:00
{
/// <summary>
2021-06-25 17:07:47 +08:00
/// The online play dependencies.
2021-06-25 12:02:19 +08:00
/// </summary>
2021-06-25 19:11:38 +08:00
public OnlinePlayTestSceneDependencies OnlinePlayDependencies { get ; set ; }
2021-06-25 12:02:19 +08:00
private readonly IReadOnlyDependencyContainer parent ;
private readonly DependencyContainer injectableDependencies ;
/// <summary>
2021-06-25 17:07:47 +08:00
/// Creates a new <see cref="DelegatedDependencyContainer"/>.
2021-06-25 12:02:19 +08:00
/// </summary>
2021-06-25 17:07:47 +08:00
/// <param name="parent">The fallback <see cref="IReadOnlyDependencyContainer"/> to use when <see cref="OnlinePlayDependencies"/> cannot satisfy a dependency.</param>
public DelegatedDependencyContainer ( IReadOnlyDependencyContainer parent )
2021-06-25 12:02:19 +08:00
{
this . parent = parent ;
injectableDependencies = new DependencyContainer ( this ) ;
}
public object Get ( Type type )
2021-06-25 17:07:47 +08:00
= > OnlinePlayDependencies ? . Get ( type ) ? ? parent . Get ( type ) ;
2021-06-25 12:02:19 +08:00
public object Get ( Type type , CacheInfo info )
2021-06-25 17:07:47 +08:00
= > OnlinePlayDependencies ? . Get ( type , info ) ? ? parent . Get ( type , info ) ;
2021-06-25 12:02:19 +08:00
public void Inject < T > ( T instance )
where T : class
= > injectableDependencies . Inject ( instance ) ;
}
}
}