2020-01-30 22:45:15 +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
2020-11-13 13:08:08 +08:00
using System.Linq ;
2022-11-18 22:49:03 +08:00
using System.Threading ;
2020-01-30 22:45:15 +08:00
using NUnit.Framework ;
2020-11-11 14:58:43 +08:00
using osu.Framework.Allocation ;
2022-01-03 16:02:15 +08:00
using osu.Framework.Extensions ;
2022-04-18 17:09:14 +08:00
using osu.Framework.Graphics ;
2020-11-13 13:08:08 +08:00
using osu.Framework.Graphics.Containers ;
2020-11-11 14:58:43 +08:00
using osu.Framework.Screens ;
2020-11-13 13:08:08 +08:00
using osu.Framework.Testing ;
2020-11-11 14:58:43 +08:00
using osu.Game.Overlays ;
2023-07-28 13:49:05 +08:00
using osu.Game.Rulesets.Mods ;
2020-11-11 14:58:43 +08:00
using osu.Game.Screens ;
2020-01-30 22:45:15 +08:00
using osu.Game.Screens.Menu ;
using osu.Game.Screens.Play ;
2021-07-01 14:10:19 +08:00
using osu.Game.Tests.Beatmaps.IO ;
2020-11-11 14:58:43 +08:00
using osuTK.Input ;
2021-05-16 23:14:23 +08:00
using static osu . Game . Tests . Visual . Navigation . TestSceneScreenNavigation ;
2020-01-30 22:45:15 +08:00
namespace osu.Game.Tests.Visual.Navigation
{
2022-11-24 13:32:20 +08:00
public partial class TestScenePerformFromScreen : OsuGameTestScene
2020-01-30 22:45:15 +08:00
{
2020-11-11 14:58:43 +08:00
private bool actionPerformed ;
public override void SetUpSteps ( )
{
AddStep ( "reset status" , ( ) = > actionPerformed = false ) ;
base . SetUpSteps ( ) ;
}
2020-01-30 22:45:15 +08:00
[Test]
public void TestPerformAtMenu ( )
{
2020-11-11 14:58:43 +08:00
AddStep ( "perform immediately" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true ) ) ;
AddAssert ( "did perform" , ( ) = > actionPerformed ) ;
2020-01-30 22:45:15 +08:00
}
[Test]
public void TestPerformAtSongSelect ( )
{
2021-05-16 21:35:44 +08:00
PushAndConfirm ( ( ) = > new TestPlaySongSelect ( ) ) ;
2020-01-30 22:45:15 +08:00
2021-05-16 21:35:44 +08:00
AddStep ( "perform immediately" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true , new [ ] { typeof ( TestPlaySongSelect ) } ) ) ;
2020-11-11 14:58:43 +08:00
AddAssert ( "did perform" , ( ) = > actionPerformed ) ;
2021-05-16 21:35:44 +08:00
AddAssert ( "screen didn't change" , ( ) = > Game . ScreenStack . CurrentScreen is TestPlaySongSelect ) ;
2020-01-30 22:45:15 +08:00
}
[Test]
public void TestPerformAtMenuFromSongSelect ( )
{
2021-05-16 21:35:44 +08:00
PushAndConfirm ( ( ) = > new TestPlaySongSelect ( ) ) ;
2020-01-30 22:45:15 +08:00
AddStep ( "try to perform" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true ) ) ;
AddUntilStep ( "returned to menu" , ( ) = > Game . ScreenStack . CurrentScreen is MainMenu ) ;
AddAssert ( "did perform" , ( ) = > actionPerformed ) ;
}
[Test]
public void TestPerformAtSongSelectFromPlayerLoader ( )
{
2021-07-09 11:36:54 +08:00
importAndWaitForSongSelect ( ) ;
2021-07-01 14:10:19 +08:00
AddStep ( "Press enter" , ( ) = > InputManager . Key ( Key . Enter ) ) ;
AddUntilStep ( "Wait for new screen" , ( ) = > Game . ScreenStack . CurrentScreen is PlayerLoader ) ;
2020-01-30 22:45:15 +08:00
2021-05-16 21:35:44 +08:00
AddStep ( "try to perform" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true , new [ ] { typeof ( TestPlaySongSelect ) } ) ) ;
AddUntilStep ( "returned to song select" , ( ) = > Game . ScreenStack . CurrentScreen is TestPlaySongSelect ) ;
2020-01-30 22:45:15 +08:00
AddAssert ( "did perform" , ( ) = > actionPerformed ) ;
}
[Test]
public void TestPerformAtMenuFromPlayerLoader ( )
{
2021-07-09 11:36:54 +08:00
importAndWaitForSongSelect ( ) ;
2021-07-01 14:10:19 +08:00
AddStep ( "Press enter" , ( ) = > InputManager . Key ( Key . Enter ) ) ;
AddUntilStep ( "Wait for new screen" , ( ) = > Game . ScreenStack . CurrentScreen is PlayerLoader ) ;
2020-01-30 22:45:15 +08:00
AddStep ( "try to perform" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true ) ) ;
AddUntilStep ( "returned to song select" , ( ) = > Game . ScreenStack . CurrentScreen is MainMenu ) ;
AddAssert ( "did perform" , ( ) = > actionPerformed ) ;
}
2020-11-11 14:58:43 +08:00
2023-07-28 13:49:05 +08:00
[Test]
public void TestPerformAtMenuFromPlayerLoaderWithAutoplayShortcut ( )
{
importAndWaitForSongSelect ( ) ;
AddStep ( "press ctrl+enter" , ( ) = >
{
InputManager . PressKey ( Key . ControlLeft ) ;
InputManager . Key ( Key . Enter ) ;
InputManager . ReleaseKey ( Key . ControlLeft ) ;
} ) ;
AddUntilStep ( "Wait for new screen" , ( ) = > Game . ScreenStack . CurrentScreen is PlayerLoader ) ;
AddAssert ( "Mods include autoplay" , ( ) = > Game . SelectedMods . Value . Any ( m = > m is ModAutoplay ) ) ;
AddStep ( "try to perform" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true ) ) ;
2023-07-29 22:12:02 +08:00
AddUntilStep ( "returned to main menu" , ( ) = > Game . ScreenStack . CurrentScreen is MainMenu ) ;
2023-07-28 13:49:05 +08:00
AddAssert ( "did perform" , ( ) = > actionPerformed ) ;
AddAssert ( "Mods don't include autoplay" , ( ) = > ! Game . SelectedMods . Value . Any ( m = > m is ModAutoplay ) ) ;
}
2022-11-18 22:49:03 +08:00
[Test]
public void TestPerformEnsuresScreenIsLoaded ( )
{
TestLoadBlockingScreen screen = null ;
AddStep ( "push blocking screen" , ( ) = > Game . ScreenStack . Push ( screen = new TestLoadBlockingScreen ( ) ) ) ;
AddStep ( "perform" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true , new [ ] { typeof ( TestLoadBlockingScreen ) } ) ) ;
AddAssert ( "action not performed" , ( ) = > ! actionPerformed ) ;
AddStep ( "allow load" , ( ) = > screen . LoadEvent . Set ( ) ) ;
AddUntilStep ( "action performed" , ( ) = > actionPerformed ) ;
}
2020-11-13 13:08:08 +08:00
[Test]
public void TestOverlaysAlwaysClosed ( )
{
2022-05-30 16:54:09 +08:00
ChatOverlay chat = null ;
2020-11-13 13:08:08 +08:00
AddUntilStep ( "is at menu" , ( ) = > Game . ScreenStack . CurrentScreen is MainMenu ) ;
2022-05-30 16:54:09 +08:00
AddUntilStep ( "wait for chat load" , ( ) = > ( chat = Game . ChildrenOfType < ChatOverlay > ( ) . SingleOrDefault ( ) ) ! = null ) ;
2020-11-13 13:08:08 +08:00
AddStep ( "show chat" , ( ) = > InputManager . Key ( Key . F8 ) ) ;
AddStep ( "try to perform" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true ) ) ;
AddUntilStep ( "still at menu" , ( ) = > Game . ScreenStack . CurrentScreen is MainMenu ) ;
AddAssert ( "did perform" , ( ) = > actionPerformed ) ;
AddAssert ( "chat closed" , ( ) = > chat . State . Value = = Visibility . Hidden ) ;
}
2020-11-11 14:58:43 +08:00
[TestCase(true)]
[TestCase(false)]
public void TestPerformBlockedByDialog ( bool confirmed )
{
DialogBlockingScreen blocker = null ;
PushAndConfirm ( ( ) = > blocker = new DialogBlockingScreen ( ) ) ;
AddStep ( "try to perform" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true ) ) ;
AddWaitStep ( "wait a bit" , 10 ) ;
AddAssert ( "screen didn't change" , ( ) = > Game . ScreenStack . CurrentScreen is DialogBlockingScreen ) ;
AddAssert ( "did not perform" , ( ) = > ! actionPerformed ) ;
AddAssert ( "only one exit attempt" , ( ) = > blocker . ExitAttempts = = 1 ) ;
2022-04-18 19:06:08 +08:00
waitForDialogOverlayLoad ( ) ;
2020-11-11 14:58:43 +08:00
if ( confirmed )
{
AddStep ( "accept dialog" , ( ) = > InputManager . Key ( Key . Number1 ) ) ;
2022-04-18 17:09:14 +08:00
AddUntilStep ( "wait for dialog dismissed" , ( ) = > Game . Dependencies . Get < IDialogOverlay > ( ) . CurrentDialog = = null ) ;
2020-11-11 14:58:43 +08:00
AddUntilStep ( "did perform" , ( ) = > actionPerformed ) ;
}
else
{
AddStep ( "cancel dialog" , ( ) = > InputManager . Key ( Key . Number2 ) ) ;
AddAssert ( "screen didn't change" , ( ) = > Game . ScreenStack . CurrentScreen is DialogBlockingScreen ) ;
AddAssert ( "did not perform" , ( ) = > ! actionPerformed ) ;
}
}
[TestCase(true)]
[TestCase(false)]
public void TestPerformBlockedByDialogNested ( bool confirmSecond )
{
DialogBlockingScreen blocker = null ;
DialogBlockingScreen blocker2 = null ;
PushAndConfirm ( ( ) = > blocker = new DialogBlockingScreen ( ) ) ;
PushAndConfirm ( ( ) = > blocker2 = new DialogBlockingScreen ( ) ) ;
AddStep ( "try to perform" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true ) ) ;
AddUntilStep ( "wait for dialog" , ( ) = > blocker2 . ExitAttempts = = 1 ) ;
AddWaitStep ( "wait a bit" , 10 ) ;
2022-04-18 19:06:08 +08:00
waitForDialogOverlayLoad ( ) ;
2020-11-11 14:58:43 +08:00
AddAssert ( "screen didn't change" , ( ) = > Game . ScreenStack . CurrentScreen = = blocker2 ) ;
AddAssert ( "did not perform" , ( ) = > ! actionPerformed ) ;
AddAssert ( "only one exit attempt" , ( ) = > blocker2 . ExitAttempts = = 1 ) ;
AddStep ( "accept dialog" , ( ) = > InputManager . Key ( Key . Number1 ) ) ;
AddUntilStep ( "screen changed" , ( ) = > Game . ScreenStack . CurrentScreen = = blocker ) ;
AddUntilStep ( "wait for second dialog" , ( ) = > blocker . ExitAttempts = = 1 ) ;
AddAssert ( "did not perform" , ( ) = > ! actionPerformed ) ;
AddAssert ( "only one exit attempt" , ( ) = > blocker . ExitAttempts = = 1 ) ;
if ( confirmSecond )
{
AddStep ( "accept dialog" , ( ) = > InputManager . Key ( Key . Number1 ) ) ;
AddUntilStep ( "did perform" , ( ) = > actionPerformed ) ;
}
else
{
AddStep ( "cancel dialog" , ( ) = > InputManager . Key ( Key . Number2 ) ) ;
AddAssert ( "screen didn't change" , ( ) = > Game . ScreenStack . CurrentScreen = = blocker ) ;
AddAssert ( "did not perform" , ( ) = > ! actionPerformed ) ;
}
}
2022-04-14 15:19:32 +08:00
[TestCase(true)]
[TestCase(false)]
public void TestPerformBlockedByDialogSubScreen ( bool confirm )
{
TestScreenWithNestedStack screenWithNestedStack = null ;
PushAndConfirm ( ( ) = > screenWithNestedStack = new TestScreenWithNestedStack ( ) ) ;
AddAssert ( "wait for nested screen" , ( ) = > screenWithNestedStack . SubScreenStack . CurrentScreen = = screenWithNestedStack . Blocker ) ;
AddStep ( "try to perform" , ( ) = > Game . PerformFromScreen ( _ = > actionPerformed = true ) ) ;
AddUntilStep ( "wait for dialog" , ( ) = > screenWithNestedStack . Blocker . ExitAttempts = = 1 ) ;
AddWaitStep ( "wait a bit" , 10 ) ;
2022-04-18 19:06:08 +08:00
waitForDialogOverlayLoad ( ) ;
2022-04-14 15:19:32 +08:00
AddAssert ( "screen didn't change" , ( ) = > Game . ScreenStack . CurrentScreen = = screenWithNestedStack ) ;
AddAssert ( "nested screen didn't change" , ( ) = > screenWithNestedStack . SubScreenStack . CurrentScreen = = screenWithNestedStack . Blocker ) ;
AddAssert ( "did not perform" , ( ) = > ! actionPerformed ) ;
AddAssert ( "only one exit attempt" , ( ) = > screenWithNestedStack . Blocker . ExitAttempts = = 1 ) ;
if ( confirm )
{
AddStep ( "accept dialog" , ( ) = > InputManager . Key ( Key . Number1 ) ) ;
AddAssert ( "nested screen changed" , ( ) = > screenWithNestedStack . SubScreenStack . CurrentScreen ! = screenWithNestedStack . Blocker ) ;
AddUntilStep ( "did perform" , ( ) = > actionPerformed ) ;
}
else
{
AddStep ( "cancel dialog" , ( ) = > InputManager . Key ( Key . Number2 ) ) ;
AddAssert ( "screen didn't change" , ( ) = > Game . ScreenStack . CurrentScreen = = screenWithNestedStack ) ;
AddAssert ( "nested screen didn't change" , ( ) = > screenWithNestedStack . SubScreenStack . CurrentScreen = = screenWithNestedStack . Blocker ) ;
AddAssert ( "did not perform" , ( ) = > ! actionPerformed ) ;
}
}
2022-04-18 19:06:08 +08:00
private void waitForDialogOverlayLoad ( ) = > AddUntilStep ( "wait for dialog overlay loaded" , ( ) = > ( ( Drawable ) Game . Dependencies . Get < IDialogOverlay > ( ) ) . IsLoaded ) ;
2021-07-09 11:36:54 +08:00
private void importAndWaitForSongSelect ( )
{
2021-12-17 17:26:12 +08:00
AddStep ( "import beatmap" , ( ) = > BeatmapImportHelper . LoadQuickOszIntoOsu ( Game ) . WaitSafely ( ) ) ;
2021-07-09 11:36:54 +08:00
PushAndConfirm ( ( ) = > new TestPlaySongSelect ( ) ) ;
2021-11-12 16:50:31 +08:00
AddUntilStep ( "beatmap updated" , ( ) = > Game . Beatmap . Value . BeatmapSetInfo . OnlineID = = 241526 ) ;
2021-07-09 11:36:54 +08:00
}
2022-11-24 13:32:20 +08:00
public partial class DialogBlockingScreen : OsuScreen
2020-11-11 14:58:43 +08:00
{
[Resolved]
2022-04-18 17:09:14 +08:00
private IDialogOverlay dialogOverlay { get ; set ; }
2020-11-11 14:58:43 +08:00
private int dialogDisplayCount ;
public int ExitAttempts { get ; private set ; }
2022-04-21 23:52:44 +08:00
public override bool OnExiting ( ScreenExitEvent e )
2020-11-11 14:58:43 +08:00
{
ExitAttempts + + ;
if ( dialogDisplayCount + + < 1 )
{
dialogOverlay . Push ( new ConfirmExitDialog ( this . Exit , ( ) = > { } ) ) ;
return true ;
}
2022-04-21 23:52:44 +08:00
return base . OnExiting ( e ) ;
2020-11-11 14:58:43 +08:00
}
}
2022-04-14 15:19:32 +08:00
2022-11-24 13:32:20 +08:00
public partial class TestScreenWithNestedStack : OsuScreen , IHasSubScreenStack
2022-04-14 15:19:32 +08:00
{
public DialogBlockingScreen Blocker { get ; private set ; }
public ScreenStack SubScreenStack { get ; } = new ScreenStack ( ) ;
public TestScreenWithNestedStack ( )
{
AddInternal ( SubScreenStack ) ;
SubScreenStack . Push ( Blocker = new DialogBlockingScreen ( ) ) ;
}
2022-04-21 23:52:44 +08:00
public override bool OnExiting ( ScreenExitEvent e )
2022-04-14 15:19:32 +08:00
{
if ( SubScreenStack . CurrentScreen ! = null )
{
SubScreenStack . CurrentScreen . Exit ( ) ;
return true ;
}
2022-04-21 23:52:44 +08:00
return base . OnExiting ( e ) ;
2022-04-14 15:19:32 +08:00
}
}
2022-11-18 22:49:03 +08:00
2022-11-24 13:32:20 +08:00
public partial class TestLoadBlockingScreen : OsuScreen
2022-11-18 22:49:03 +08:00
{
public readonly ManualResetEventSlim LoadEvent = new ManualResetEventSlim ( ) ;
[BackgroundDependencyLoader]
private void load ( )
{
LoadEvent . Wait ( 10000 ) ;
}
}
2020-01-30 22:45:15 +08:00
}
}