2022-05-15 01:39:54 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2022-03-27 05:43:17 +08:00
// See the LICENCE file in the repository root for full licence text.
2022-04-22 05:49:40 +08:00
using System ;
2022-04-18 02:26:16 +08:00
using System.Collections.Generic ;
2022-03-27 06:21:17 +08:00
using System.Linq ;
2022-03-27 05:43:17 +08:00
using NUnit.Framework ;
2022-04-04 02:24:32 +08:00
using osu.Framework.Allocation ;
2022-04-18 02:26:16 +08:00
using osu.Framework.Bindables ;
2022-08-15 03:35:09 +08:00
using osu.Framework.Extensions.ObjectExtensions ;
2022-03-27 05:43:17 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2022-08-11 04:09:11 +08:00
using osu.Framework.Localisation ;
2022-03-27 06:21:17 +08:00
using osu.Framework.Testing ;
2022-04-03 23:43:13 +08:00
using osu.Framework.Utils ;
2022-03-27 06:21:17 +08:00
using osu.Game.Graphics.UserInterface ;
2023-04-30 23:23:45 +08:00
using osu.Game.Overlays ;
2022-03-27 05:43:17 +08:00
using osu.Game.Overlays.Mods ;
2022-04-18 02:26:16 +08:00
using osu.Game.Overlays.Settings ;
2022-04-04 02:24:32 +08:00
using osu.Game.Rulesets ;
2022-10-14 06:59:55 +08:00
using osu.Game.Rulesets.Catch.Mods ;
2022-04-03 23:43:13 +08:00
using osu.Game.Rulesets.Mods ;
2022-04-18 02:26:16 +08:00
using osu.Game.Rulesets.Osu ;
2022-03-27 06:21:17 +08:00
using osu.Game.Rulesets.Osu.Mods ;
2022-05-12 01:59:30 +08:00
using osu.Game.Tests.Mods ;
2022-05-06 17:37:50 +08:00
using osuTK ;
2022-03-27 06:21:17 +08:00
using osuTK.Input ;
2022-03-27 05:43:17 +08:00
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
2022-05-12 00:04:39 +08:00
public partial class TestSceneModSelectOverlay : OsuManualInputManagerTestScene
2022-03-27 05:43:17 +08:00
{
2022-08-15 03:35:09 +08:00
protected override bool UseFreshStoragePerRun = > true ;
private RulesetStore rulesetStore = null ! ;
private TestModSelectOverlay modSelectOverlay = null ! ;
2022-04-04 02:24:32 +08:00
2022-08-15 03:35:09 +08:00
[BackgroundDependencyLoader]
private void load ( )
{
Dependencies . Cache ( rulesetStore = new RealmRulesetStore ( Realm ) ) ;
Dependencies . Cache ( Realm ) ;
}
2022-03-27 05:43:17 +08:00
2022-03-27 06:21:17 +08:00
[SetUpSteps]
public void SetUpSteps ( )
{
2022-04-04 02:24:32 +08:00
AddStep ( "clear contents" , Clear ) ;
AddStep ( "reset ruleset" , ( ) = > Ruleset . Value = rulesetStore . GetRuleset ( 0 ) ) ;
2022-04-03 23:43:13 +08:00
AddStep ( "reset mods" , ( ) = > SelectedMods . SetDefault ( ) ) ;
2022-08-16 00:44:51 +08:00
AddStep ( "set up presets" , ( ) = >
{
Realm . Write ( r = >
{
r . RemoveAll < ModPreset > ( ) ;
r . Add ( new ModPreset
{
Name = "AR0" ,
Description = "Too... many... circles..." ,
Ruleset = r . Find < RulesetInfo > ( OsuRuleset . SHORT_NAME ) ,
Mods = new [ ]
{
new OsuModDifficultyAdjust
{
ApproachRate = { Value = 0 }
}
}
} ) ;
2023-04-30 23:23:45 +08:00
r . Add ( new ModPreset
{
Name = "Half Time 0.5x" ,
Description = "Very slow" ,
Ruleset = r . Find < RulesetInfo > ( OsuRuleset . SHORT_NAME ) ,
Mods = new [ ]
{
new OsuModHalfTime
{
SpeedChange = { Value = 0.5 }
}
}
} ) ;
2022-08-16 00:44:51 +08:00
} ) ;
} ) ;
2022-03-27 06:21:17 +08:00
}
2022-03-27 05:43:17 +08:00
2022-04-04 02:24:32 +08:00
private void createScreen ( )
2022-04-03 23:43:13 +08:00
{
2022-08-15 03:35:09 +08:00
AddStep ( "create screen" , ( ) = > Child = modSelectOverlay = new TestModSelectOverlay
2022-04-04 02:24:32 +08:00
{
RelativeSizeAxes = Axes . Both ,
State = { Value = Visibility . Visible } ,
SelectedMods = { BindTarget = SelectedMods }
} ) ;
waitForColumnLoad ( ) ;
}
2022-04-03 23:43:13 +08:00
2022-03-27 06:21:17 +08:00
[Test]
public void TestStateChange ( )
{
2022-04-03 23:43:13 +08:00
createScreen ( ) ;
2022-05-11 04:29:57 +08:00
AddStep ( "toggle state" , ( ) = > modSelectOverlay . ToggleVisibility ( ) ) ;
2022-03-27 05:43:17 +08:00
}
2022-03-27 06:21:17 +08:00
2022-04-03 23:43:13 +08:00
[Test]
public void TestPreexistingSelection ( )
{
AddStep ( "set mods" , ( ) = > SelectedMods . Value = new Mod [ ] { new OsuModAlternate ( ) , new OsuModDaycore ( ) } ) ;
createScreen ( ) ;
2022-05-11 04:29:57 +08:00
AddUntilStep ( "two panels active" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Count ( panel = > panel . Active . Value ) = = 2 ) ;
2022-04-03 23:43:13 +08:00
AddAssert ( "mod multiplier correct" , ( ) = >
{
2022-04-04 14:29:03 +08:00
double multiplier = SelectedMods . Value . Aggregate ( 1d , ( m , mod ) = > m * mod . ScoreMultiplier ) ;
2022-05-11 04:29:57 +08:00
return Precision . AlmostEquals ( multiplier , modSelectOverlay . ChildrenOfType < DifficultyMultiplierDisplay > ( ) . Single ( ) . Current . Value ) ;
2022-04-03 23:43:13 +08:00
} ) ;
assertCustomisationToggleState ( disabled : false , active : false ) ;
2022-05-11 04:29:57 +08:00
AddAssert ( "setting items created" , ( ) = > modSelectOverlay . ChildrenOfType < ISettingsItem > ( ) . Any ( ) ) ;
2022-04-03 23:43:13 +08:00
}
[Test]
public void TestExternalSelection ( )
{
createScreen ( ) ;
AddStep ( "set mods" , ( ) = > SelectedMods . Value = new Mod [ ] { new OsuModAlternate ( ) , new OsuModDaycore ( ) } ) ;
2022-05-11 04:29:57 +08:00
AddUntilStep ( "two panels active" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Count ( panel = > panel . Active . Value ) = = 2 ) ;
2022-04-03 23:43:13 +08:00
AddAssert ( "mod multiplier correct" , ( ) = >
{
2022-04-04 14:29:03 +08:00
double multiplier = SelectedMods . Value . Aggregate ( 1d , ( m , mod ) = > m * mod . ScoreMultiplier ) ;
2022-05-11 04:29:57 +08:00
return Precision . AlmostEquals ( multiplier , modSelectOverlay . ChildrenOfType < DifficultyMultiplierDisplay > ( ) . Single ( ) . Current . Value ) ;
2022-04-03 23:43:13 +08:00
} ) ;
assertCustomisationToggleState ( disabled : false , active : false ) ;
2022-05-11 04:29:57 +08:00
AddAssert ( "setting items created" , ( ) = > modSelectOverlay . ChildrenOfType < ISettingsItem > ( ) . Any ( ) ) ;
2022-04-03 23:43:13 +08:00
}
2022-04-04 00:38:23 +08:00
[Test]
public void TestRulesetChange ( )
{
createScreen ( ) ;
2022-04-04 02:24:32 +08:00
changeRuleset ( 0 ) ;
changeRuleset ( 1 ) ;
changeRuleset ( 2 ) ;
changeRuleset ( 3 ) ;
2022-04-04 00:38:23 +08:00
}
2022-04-22 05:49:40 +08:00
[Test]
public void TestIncompatibilityToggling ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddStep ( "activate DT" , ( ) = > getPanelForMod ( typeof ( OsuModDoubleTime ) ) . TriggerClick ( ) ) ;
AddAssert ( "DT active" , ( ) = > SelectedMods . Value . Single ( ) . GetType ( ) = = typeof ( OsuModDoubleTime ) ) ;
2022-05-03 05:29:30 +08:00
AddAssert ( "DT panel active" , ( ) = > getPanelForMod ( typeof ( OsuModDoubleTime ) ) . Active . Value ) ;
2022-04-22 05:49:40 +08:00
AddStep ( "activate NC" , ( ) = > getPanelForMod ( typeof ( OsuModNightcore ) ) . TriggerClick ( ) ) ;
AddAssert ( "only NC active" , ( ) = > SelectedMods . Value . Single ( ) . GetType ( ) = = typeof ( OsuModNightcore ) ) ;
2022-05-03 05:29:30 +08:00
AddAssert ( "DT panel not active" , ( ) = > ! getPanelForMod ( typeof ( OsuModDoubleTime ) ) . Active . Value ) ;
AddAssert ( "NC panel active" , ( ) = > getPanelForMod ( typeof ( OsuModNightcore ) ) . Active . Value ) ;
2022-04-22 05:49:40 +08:00
AddStep ( "activate HR" , ( ) = > getPanelForMod ( typeof ( OsuModHardRock ) ) . TriggerClick ( ) ) ;
AddAssert ( "NC+HR active" , ( ) = > SelectedMods . Value . Any ( mod = > mod . GetType ( ) = = typeof ( OsuModNightcore ) )
& & SelectedMods . Value . Any ( mod = > mod . GetType ( ) = = typeof ( OsuModHardRock ) ) ) ;
2022-05-03 05:29:30 +08:00
AddAssert ( "NC panel active" , ( ) = > getPanelForMod ( typeof ( OsuModNightcore ) ) . Active . Value ) ;
AddAssert ( "HR panel active" , ( ) = > getPanelForMod ( typeof ( OsuModHardRock ) ) . Active . Value ) ;
2022-04-22 05:49:40 +08:00
AddStep ( "activate MR" , ( ) = > getPanelForMod ( typeof ( OsuModMirror ) ) . TriggerClick ( ) ) ;
AddAssert ( "NC+MR active" , ( ) = > SelectedMods . Value . Any ( mod = > mod . GetType ( ) = = typeof ( OsuModNightcore ) )
& & SelectedMods . Value . Any ( mod = > mod . GetType ( ) = = typeof ( OsuModMirror ) ) ) ;
2022-05-03 05:29:30 +08:00
AddAssert ( "NC panel active" , ( ) = > getPanelForMod ( typeof ( OsuModNightcore ) ) . Active . Value ) ;
AddAssert ( "HR panel not active" , ( ) = > ! getPanelForMod ( typeof ( OsuModHardRock ) ) . Active . Value ) ;
AddAssert ( "MR panel active" , ( ) = > getPanelForMod ( typeof ( OsuModMirror ) ) . Active . Value ) ;
2022-04-22 05:49:40 +08:00
}
2022-04-26 05:41:40 +08:00
[Test]
public void TestDimmedState ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddUntilStep ( "any column dimmed" , ( ) = > this . ChildrenOfType < ModColumn > ( ) . Any ( column = > ! column . Active . Value ) ) ;
2022-08-15 03:35:09 +08:00
ModSelectColumn lastColumn = null ! ;
2022-04-26 05:41:40 +08:00
2022-04-27 05:11:38 +08:00
AddAssert ( "last column dimmed" , ( ) = > ! this . ChildrenOfType < ModColumn > ( ) . Last ( ) . Active . Value ) ;
AddStep ( "request scroll to last column" , ( ) = >
2022-04-26 05:41:40 +08:00
{
2022-05-11 04:29:57 +08:00
var lastDimContainer = this . ChildrenOfType < ModSelectOverlay . ColumnDimContainer > ( ) . Last ( ) ;
2022-04-27 05:11:38 +08:00
lastColumn = lastDimContainer . Column ;
lastDimContainer . RequestScroll ? . Invoke ( lastDimContainer ) ;
2022-04-26 05:41:40 +08:00
} ) ;
2022-04-27 05:11:38 +08:00
AddUntilStep ( "column undimmed" , ( ) = > lastColumn . Active . Value ) ;
2022-04-26 05:41:40 +08:00
2022-04-27 05:11:38 +08:00
AddStep ( "click panel" , ( ) = >
2022-04-26 05:41:40 +08:00
{
2022-04-27 05:11:38 +08:00
InputManager . MoveMouseTo ( lastColumn . ChildrenOfType < ModPanel > ( ) . First ( ) ) ;
2022-04-26 05:41:40 +08:00
InputManager . Click ( MouseButton . Left ) ;
} ) ;
2022-04-27 05:11:38 +08:00
AddUntilStep ( "panel selected" , ( ) = > lastColumn . ChildrenOfType < ModPanel > ( ) . First ( ) . Active . Value ) ;
2022-04-26 05:41:40 +08:00
}
2022-03-27 06:21:17 +08:00
[Test]
public void TestCustomisationToggleState ( )
{
2022-04-03 23:43:13 +08:00
createScreen ( ) ;
2022-03-27 06:21:17 +08:00
assertCustomisationToggleState ( disabled : true , active : false ) ;
AddStep ( "select customisable mod" , ( ) = > SelectedMods . Value = new [ ] { new OsuModDoubleTime ( ) } ) ;
assertCustomisationToggleState ( disabled : false , active : false ) ;
2022-08-16 01:46:17 +08:00
AddStep ( "select mod requiring configuration externally" , ( ) = > SelectedMods . Value = new [ ] { new OsuModDifficultyAdjust ( ) } ) ;
assertCustomisationToggleState ( disabled : false , active : false ) ;
AddStep ( "reset mods" , ( ) = > SelectedMods . SetDefault ( ) ) ;
AddStep ( "select difficulty adjust via panel" , ( ) = > getPanelForMod ( typeof ( OsuModDifficultyAdjust ) ) . TriggerClick ( ) ) ;
2022-03-27 06:21:17 +08:00
assertCustomisationToggleState ( disabled : false , active : true ) ;
2022-05-06 17:37:50 +08:00
AddStep ( "dismiss mod customisation via toggle" , ( ) = >
2022-03-27 06:21:17 +08:00
{
2022-08-15 03:35:09 +08:00
InputManager . MoveMouseTo ( modSelectOverlay . CustomisationButton ) ;
2022-03-27 06:21:17 +08:00
InputManager . Click ( MouseButton . Left ) ;
} ) ;
2022-05-06 01:08:38 +08:00
assertCustomisationToggleState ( disabled : false , active : false ) ;
AddStep ( "reset mods" , ( ) = > SelectedMods . SetDefault ( ) ) ;
2022-08-16 01:46:17 +08:00
AddStep ( "select difficulty adjust via panel" , ( ) = > getPanelForMod ( typeof ( OsuModDifficultyAdjust ) ) . TriggerClick ( ) ) ;
2022-05-06 01:08:38 +08:00
assertCustomisationToggleState ( disabled : false , active : true ) ;
AddStep ( "dismiss mod customisation via keyboard" , ( ) = > InputManager . Key ( Key . Escape ) ) ;
assertCustomisationToggleState ( disabled : false , active : false ) ;
2022-03-27 06:21:17 +08:00
AddStep ( "append another mod not requiring config" , ( ) = > SelectedMods . Value = SelectedMods . Value . Append ( new OsuModFlashlight ( ) ) . ToArray ( ) ) ;
assertCustomisationToggleState ( disabled : false , active : false ) ;
AddStep ( "select mod without configuration" , ( ) = > SelectedMods . Value = new [ ] { new OsuModAutoplay ( ) } ) ;
assertCustomisationToggleState ( disabled : true , active : false ) ;
2022-08-16 01:46:17 +08:00
AddStep ( "select difficulty adjust via panel" , ( ) = > getPanelForMod ( typeof ( OsuModDifficultyAdjust ) ) . TriggerClick ( ) ) ;
2022-03-27 06:21:17 +08:00
assertCustomisationToggleState ( disabled : false , active : true ) ;
AddStep ( "select mod without configuration" , ( ) = > SelectedMods . Value = new [ ] { new OsuModAutoplay ( ) } ) ;
assertCustomisationToggleState ( disabled : true , active : false ) ; // config was dismissed without explicit user action.
2022-08-16 00:44:51 +08:00
AddStep ( "select mod preset with mod requiring configuration" , ( ) = >
{
InputManager . MoveMouseTo ( this . ChildrenOfType < ModPresetPanel > ( ) . First ( ) ) ;
InputManager . Click ( MouseButton . Left ) ;
} ) ;
assertCustomisationToggleState ( disabled : false , active : false ) ;
2022-03-27 06:21:17 +08:00
}
2022-05-06 17:37:50 +08:00
[Test]
public void TestDismissCustomisationViaDimmedArea ( )
{
createScreen ( ) ;
assertCustomisationToggleState ( disabled : true , active : false ) ;
2022-08-16 01:46:17 +08:00
AddStep ( "select difficulty adjust via panel" , ( ) = > getPanelForMod ( typeof ( OsuModDifficultyAdjust ) ) . TriggerClick ( ) ) ;
2022-05-06 17:37:50 +08:00
assertCustomisationToggleState ( disabled : false , active : true ) ;
AddStep ( "move mouse to settings area" , ( ) = > InputManager . MoveMouseTo ( this . ChildrenOfType < ModSettingsArea > ( ) . Single ( ) ) ) ;
AddStep ( "move mouse to dimmed area" , ( ) = >
{
InputManager . MoveMouseTo ( new Vector2 (
2022-05-11 04:29:57 +08:00
modSelectOverlay . ScreenSpaceDrawQuad . TopLeft . X ,
( modSelectOverlay . ScreenSpaceDrawQuad . TopLeft . Y + modSelectOverlay . ScreenSpaceDrawQuad . BottomLeft . Y ) / 2 ) ) ;
2022-05-06 17:37:50 +08:00
} ) ;
AddStep ( "click" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
assertCustomisationToggleState ( disabled : false , active : false ) ;
2022-05-11 04:29:57 +08:00
AddStep ( "move mouse to first mod panel" , ( ) = > InputManager . MoveMouseTo ( modSelectOverlay . ChildrenOfType < ModPanel > ( ) . First ( ) ) ) ;
AddAssert ( "first mod panel is hovered" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . First ( ) . IsHovered ) ;
2022-05-06 17:37:50 +08:00
}
2022-04-18 02:26:16 +08:00
/// <summary>
/// Ensure that two mod overlays are not cross polluting via central settings instances.
/// </summary>
[Test]
public void TestSettingsNotCrossPolluting ( )
{
2022-08-15 03:35:09 +08:00
Bindable < IReadOnlyList < Mod > > selectedMods2 = null ! ;
ModSelectOverlay modSelectOverlay2 = null ! ;
2022-04-18 02:26:16 +08:00
createScreen ( ) ;
2022-08-16 01:46:17 +08:00
AddStep ( "select difficulty adjust via panel" , ( ) = > getPanelForMod ( typeof ( OsuModDifficultyAdjust ) ) . TriggerClick ( ) ) ;
2022-04-18 02:26:16 +08:00
2023-02-03 00:24:45 +08:00
AddStep ( "set setting" , ( ) = > modSelectOverlay . ChildrenOfType < RoundedSliderBar < float > > ( ) . First ( ) . Current . Value = 8 ) ;
2022-04-18 02:26:16 +08:00
AddAssert ( "ensure setting is propagated" , ( ) = > SelectedMods . Value . OfType < OsuModDifficultyAdjust > ( ) . Single ( ) . CircleSize . Value = = 8 ) ;
AddStep ( "create second bindable" , ( ) = > selectedMods2 = new Bindable < IReadOnlyList < Mod > > ( new Mod [ ] { new OsuModDifficultyAdjust ( ) } ) ) ;
AddStep ( "create second overlay" , ( ) = >
{
2022-05-12 00:05:09 +08:00
Add ( modSelectOverlay2 = new UserModSelectOverlay ( ) . With ( d = >
2022-04-18 02:26:16 +08:00
{
d . Origin = Anchor . TopCentre ;
d . Anchor = Anchor . TopCentre ;
d . SelectedMods . BindTarget = selectedMods2 ;
} ) ) ;
} ) ;
2022-05-12 00:05:09 +08:00
AddStep ( "show" , ( ) = > modSelectOverlay2 . Show ( ) ) ;
2022-04-18 02:26:16 +08:00
AddAssert ( "ensure first is unchanged" , ( ) = > SelectedMods . Value . OfType < OsuModDifficultyAdjust > ( ) . Single ( ) . CircleSize . Value = = 8 ) ;
AddAssert ( "ensure second is default" , ( ) = > selectedMods2 . Value . OfType < OsuModDifficultyAdjust > ( ) . Single ( ) . CircleSize . Value = = null ) ;
}
[Test]
public void TestSettingsResetOnDeselection ( )
{
var osuModDoubleTime = new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } ;
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddStep ( "set dt mod with custom rate" , ( ) = > { SelectedMods . Value = new [ ] { osuModDoubleTime } ; } ) ;
AddAssert ( "selected mod matches" , ( ) = > ( SelectedMods . Value . Single ( ) as OsuModDoubleTime ) ? . SpeedChange . Value = = 1.2 ) ;
AddStep ( "deselect" , ( ) = > getPanelForMod ( typeof ( OsuModDoubleTime ) ) . TriggerClick ( ) ) ;
AddAssert ( "selected mods empty" , ( ) = > SelectedMods . Value . Count = = 0 ) ;
AddStep ( "reselect" , ( ) = > getPanelForMod ( typeof ( OsuModDoubleTime ) ) . TriggerClick ( ) ) ;
AddAssert ( "selected mod has default value" , ( ) = > ( SelectedMods . Value . Single ( ) as OsuModDoubleTime ) ? . SpeedChange . IsDefault = = true ) ;
}
[Test]
public void TestAnimationFlushOnClose ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddStep ( "Select all fun mods" , ( ) = >
{
2022-05-11 04:29:57 +08:00
modSelectOverlay . ChildrenOfType < ModColumn > ( )
. Single ( c = > c . ModType = = ModType . DifficultyIncrease )
. SelectAll ( ) ;
2022-04-18 02:26:16 +08:00
} ) ;
AddUntilStep ( "many mods selected" , ( ) = > SelectedMods . Value . Count > = 5 ) ;
AddStep ( "trigger deselect and close overlay" , ( ) = >
{
2022-05-11 04:29:57 +08:00
modSelectOverlay . ChildrenOfType < ModColumn > ( )
. Single ( c = > c . ModType = = ModType . DifficultyIncrease )
. DeselectAll ( ) ;
2022-04-18 02:26:16 +08:00
2022-05-11 04:29:57 +08:00
modSelectOverlay . Hide ( ) ;
2022-04-18 02:26:16 +08:00
} ) ;
AddAssert ( "all mods deselected" , ( ) = > SelectedMods . Value . Count = = 0 ) ;
}
[Test]
2022-10-14 06:59:55 +08:00
public void TestCommonModsMaintainedOnRulesetChange ( )
2022-04-18 02:26:16 +08:00
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
2022-10-14 06:59:55 +08:00
AddStep ( "select relax mod" , ( ) = > SelectedMods . Value = new [ ] { Ruleset . Value . CreateInstance ( ) . CreateMod < ModRelax > ( ) } ) ;
2022-04-18 02:26:16 +08:00
changeRuleset ( 0 ) ;
2022-10-14 06:59:55 +08:00
AddAssert ( "ensure mod still selected" , ( ) = > SelectedMods . Value . SingleOrDefault ( ) is OsuModRelax ) ;
2022-04-18 02:26:16 +08:00
2022-10-14 06:59:55 +08:00
changeRuleset ( 2 ) ;
AddAssert ( "catch variant selected" , ( ) = > SelectedMods . Value . SingleOrDefault ( ) is CatchModRelax ) ;
2022-04-18 02:26:16 +08:00
changeRuleset ( 3 ) ;
2022-10-14 06:59:55 +08:00
AddAssert ( "no mod selected" , ( ) = > SelectedMods . Value . Count = = 0 ) ;
}
2022-04-18 02:26:16 +08:00
2022-10-14 06:59:55 +08:00
[Test]
public void TestUncommonModsDiscardedOnRulesetChange ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddStep ( "select single tap mod" , ( ) = > SelectedMods . Value = new [ ] { new OsuModSingleTap ( ) } ) ;
2022-04-18 02:26:16 +08:00
changeRuleset ( 0 ) ;
2022-10-14 06:59:55 +08:00
AddAssert ( "ensure mod still selected" , ( ) = > SelectedMods . Value . SingleOrDefault ( ) is OsuModSingleTap ) ;
2022-04-18 02:26:16 +08:00
2022-10-14 06:59:55 +08:00
changeRuleset ( 3 ) ;
AddAssert ( "no mod selected" , ( ) = > SelectedMods . Value . Count = = 0 ) ;
2022-04-18 02:26:16 +08:00
}
[Test]
public void TestExternallySetCustomizedMod ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddStep ( "set customized mod externally" , ( ) = > SelectedMods . Value = new [ ] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } } ) ;
AddAssert ( "ensure button is selected and customized accordingly" , ( ) = >
{
var button = getPanelForMod ( SelectedMods . Value . Single ( ) . GetType ( ) ) ;
return ( ( OsuModDoubleTime ) button . Mod ) . SpeedChange . Value = = 1.01 ;
} ) ;
}
[Test]
public void TestSettingsAreRetainedOnReload ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddStep ( "set customized mod externally" , ( ) = > SelectedMods . Value = new [ ] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } } ) ;
AddAssert ( "setting remains" , ( ) = > ( SelectedMods . Value . SingleOrDefault ( ) as OsuModDoubleTime ) ? . SpeedChange . Value = = 1.01 ) ;
createScreen ( ) ;
AddAssert ( "setting remains" , ( ) = > ( SelectedMods . Value . SingleOrDefault ( ) as OsuModDoubleTime ) ? . SpeedChange . Value = = 1.01 ) ;
}
[Test]
public void TestExternallySetModIsReplacedByOverlayInstance ( )
{
Mod external = new OsuModDoubleTime ( ) ;
2022-08-15 03:35:09 +08:00
Mod overlayButtonMod = null ! ;
2022-04-18 02:26:16 +08:00
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddStep ( "set mod externally" , ( ) = > { SelectedMods . Value = new [ ] { external } ; } ) ;
AddAssert ( "ensure button is selected" , ( ) = >
{
var button = getPanelForMod ( SelectedMods . Value . Single ( ) . GetType ( ) ) ;
overlayButtonMod = button . Mod ;
return button . Active . Value ;
} ) ;
// Right now, when an external change occurs, the ModSelectOverlay will replace the global instance with its own
AddAssert ( "mod instance doesn't match" , ( ) = > external ! = overlayButtonMod ) ;
AddAssert ( "one mod present in global selected" , ( ) = > SelectedMods . Value . Count = = 1 ) ;
2022-05-04 03:44:44 +08:00
AddAssert ( "globally selected matches button's mod instance" , ( ) = > SelectedMods . Value . Any ( mod = > ReferenceEquals ( mod , overlayButtonMod ) ) ) ;
AddAssert ( "globally selected doesn't contain original external change" , ( ) = > ! SelectedMods . Value . Any ( mod = > ReferenceEquals ( mod , external ) ) ) ;
2022-04-18 02:26:16 +08:00
}
[Test]
public void TestChangeIsValidChangesButtonVisibility ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
2022-05-11 04:29:57 +08:00
AddAssert ( "double time visible" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Where ( panel = > panel . Mod is OsuModDoubleTime ) . Any ( panel = > ! panel . Filtered . Value ) ) ;
2022-04-18 02:26:16 +08:00
2022-05-11 04:29:57 +08:00
AddStep ( "make double time invalid" , ( ) = > modSelectOverlay . IsValidMod = m = > ! ( m is OsuModDoubleTime ) ) ;
AddUntilStep ( "double time not visible" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Where ( panel = > panel . Mod is OsuModDoubleTime ) . All ( panel = > panel . Filtered . Value ) ) ;
AddAssert ( "nightcore still visible" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Where ( panel = > panel . Mod is OsuModNightcore ) . Any ( panel = > ! panel . Filtered . Value ) ) ;
2022-04-18 02:26:16 +08:00
2022-06-24 20:25:23 +08:00
AddStep ( "make double time valid again" , ( ) = > modSelectOverlay . IsValidMod = _ = > true ) ;
2022-05-11 04:29:57 +08:00
AddUntilStep ( "double time visible" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Where ( panel = > panel . Mod is OsuModDoubleTime ) . Any ( panel = > ! panel . Filtered . Value ) ) ;
AddAssert ( "nightcore still visible" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Where ( b = > b . Mod is OsuModNightcore ) . Any ( panel = > ! panel . Filtered . Value ) ) ;
2022-04-18 02:26:16 +08:00
}
[Test]
public void TestChangeIsValidPreservesSelection ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddStep ( "select DT + HD" , ( ) = > SelectedMods . Value = new Mod [ ] { new OsuModDoubleTime ( ) , new OsuModHidden ( ) } ) ;
2022-05-11 04:29:57 +08:00
AddAssert ( "DT + HD selected" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Count ( panel = > panel . Active . Value ) = = 2 ) ;
2022-04-18 02:26:16 +08:00
2022-05-11 04:29:57 +08:00
AddStep ( "make NF invalid" , ( ) = > modSelectOverlay . IsValidMod = m = > ! ( m is ModNoFail ) ) ;
AddAssert ( "DT + HD still selected" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Count ( panel = > panel . Active . Value ) = = 2 ) ;
2022-04-18 02:26:16 +08:00
}
[Test]
public void TestUnimplementedModIsUnselectable ( )
{
var testRuleset = new TestUnimplementedModOsuRuleset ( ) ;
createScreen ( ) ;
AddStep ( "set ruleset" , ( ) = > Ruleset . Value = testRuleset . RulesetInfo ) ;
waitForColumnLoad ( ) ;
AddAssert ( "unimplemented mod panel is filtered" , ( ) = > getPanelForMod ( typeof ( TestUnimplementedMod ) ) . Filtered . Value ) ;
}
2022-05-15 01:39:54 +08:00
[Test]
public void TestDeselectAllViaKey ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddStep ( "select DT + HD" , ( ) = > SelectedMods . Value = new Mod [ ] { new OsuModDoubleTime ( ) , new OsuModHidden ( ) } ) ;
AddAssert ( "DT + HD selected" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Count ( panel = > panel . Active . Value ) = = 2 ) ;
2022-05-15 02:18:44 +08:00
AddStep ( "press backspace" , ( ) = > InputManager . Key ( Key . BackSpace ) ) ;
2022-05-15 01:39:54 +08:00
AddUntilStep ( "all mods deselected" , ( ) = > ! SelectedMods . Value . Any ( ) ) ;
}
2022-05-07 02:54:36 +08:00
[Test]
public void TestDeselectAllViaButton ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
2022-05-26 04:27:54 +08:00
AddAssert ( "deselect all button disabled" , ( ) = > ! this . ChildrenOfType < DeselectAllModsButton > ( ) . Single ( ) . Enabled . Value ) ;
2022-05-26 03:49:31 +08:00
2022-05-07 02:54:36 +08:00
AddStep ( "select DT + HD" , ( ) = > SelectedMods . Value = new Mod [ ] { new OsuModDoubleTime ( ) , new OsuModHidden ( ) } ) ;
2022-05-11 04:29:57 +08:00
AddAssert ( "DT + HD selected" , ( ) = > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Count ( panel = > panel . Active . Value ) = = 2 ) ;
2022-05-26 04:27:54 +08:00
AddAssert ( "deselect all button enabled" , ( ) = > this . ChildrenOfType < DeselectAllModsButton > ( ) . Single ( ) . Enabled . Value ) ;
2022-05-07 02:54:36 +08:00
AddStep ( "click deselect all button" , ( ) = >
{
2022-05-26 04:27:54 +08:00
InputManager . MoveMouseTo ( this . ChildrenOfType < DeselectAllModsButton > ( ) . Single ( ) ) ;
2022-05-07 02:54:36 +08:00
InputManager . Click ( MouseButton . Left ) ;
} ) ;
AddUntilStep ( "all mods deselected" , ( ) = > ! SelectedMods . Value . Any ( ) ) ;
2022-05-26 04:27:54 +08:00
AddAssert ( "deselect all button disabled" , ( ) = > ! this . ChildrenOfType < DeselectAllModsButton > ( ) . Single ( ) . Enabled . Value ) ;
2022-05-07 02:54:36 +08:00
}
2022-05-07 16:23:49 +08:00
[Test]
public void TestCloseViaBackButton ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
2022-08-16 01:46:17 +08:00
AddStep ( "select difficulty adjust via panel" , ( ) = > getPanelForMod ( typeof ( OsuModDifficultyAdjust ) ) . TriggerClick ( ) ) ;
2022-05-07 16:23:49 +08:00
assertCustomisationToggleState ( disabled : false , active : true ) ;
2022-08-15 03:35:09 +08:00
AddAssert ( "back button disabled" , ( ) = > ! modSelectOverlay . BackButton . Enabled . Value ) ;
2022-05-07 16:23:49 +08:00
AddStep ( "dismiss customisation area" , ( ) = > InputManager . Key ( Key . Escape ) ) ;
AddStep ( "click back button" , ( ) = >
{
2022-08-15 03:35:09 +08:00
InputManager . MoveMouseTo ( modSelectOverlay . BackButton ) ;
2022-05-07 16:23:49 +08:00
InputManager . Click ( MouseButton . Left ) ;
} ) ;
2022-05-11 04:29:57 +08:00
AddAssert ( "mod select hidden" , ( ) = > modSelectOverlay . State . Value = = Visibility . Hidden ) ;
2022-05-07 16:23:49 +08:00
}
2022-05-08 20:45:01 +08:00
[Test]
public void TestColumnHiding ( )
{
2022-08-15 03:35:09 +08:00
AddStep ( "create screen" , ( ) = > Child = modSelectOverlay = new TestModSelectOverlay
2022-05-08 20:45:01 +08:00
{
RelativeSizeAxes = Axes . Both ,
State = { Value = Visibility . Visible } ,
SelectedMods = { BindTarget = SelectedMods } ,
IsValidMod = mod = > mod . Type = = ModType . DifficultyIncrease | | mod . Type = = ModType . Conversion
} ) ;
waitForColumnLoad ( ) ;
changeRuleset ( 0 ) ;
AddAssert ( "two columns visible" , ( ) = > this . ChildrenOfType < ModColumn > ( ) . Count ( col = > col . IsPresent ) = = 2 ) ;
2022-05-11 04:29:57 +08:00
AddStep ( "unset filter" , ( ) = > modSelectOverlay . IsValidMod = _ = > true ) ;
2022-05-08 20:45:01 +08:00
AddAssert ( "all columns visible" , ( ) = > this . ChildrenOfType < ModColumn > ( ) . All ( col = > col . IsPresent ) ) ;
2022-05-11 04:29:57 +08:00
AddStep ( "filter out everything" , ( ) = > modSelectOverlay . IsValidMod = _ = > false ) ;
2022-05-08 20:45:01 +08:00
AddAssert ( "no columns visible" , ( ) = > this . ChildrenOfType < ModColumn > ( ) . All ( col = > ! col . IsPresent ) ) ;
2022-05-11 04:29:57 +08:00
AddStep ( "hide" , ( ) = > modSelectOverlay . Hide ( ) ) ;
AddStep ( "set filter for 3 columns" , ( ) = > modSelectOverlay . IsValidMod = mod = > mod . Type = = ModType . DifficultyReduction
| | mod . Type = = ModType . Automation
| | mod . Type = = ModType . Conversion ) ;
2022-05-08 20:45:01 +08:00
2022-05-11 04:29:57 +08:00
AddStep ( "show" , ( ) = > modSelectOverlay . Show ( ) ) ;
2022-05-08 20:45:01 +08:00
AddUntilStep ( "3 columns visible" , ( ) = > this . ChildrenOfType < ModColumn > ( ) . Count ( col = > col . IsPresent ) = = 3 ) ;
}
2022-05-12 01:59:30 +08:00
[Test]
public void TestColumnHidingOnRulesetChange ( )
{
createScreen ( ) ;
changeRuleset ( 0 ) ;
AddAssert ( "5 columns visible" , ( ) = > this . ChildrenOfType < ModColumn > ( ) . Count ( col = > col . IsPresent ) = = 5 ) ;
AddStep ( "change to ruleset without all mod types" , ( ) = > Ruleset . Value = TestCustomisableModRuleset . CreateTestRulesetInfo ( ) ) ;
AddUntilStep ( "1 column visible" , ( ) = > this . ChildrenOfType < ModColumn > ( ) . Count ( col = > col . IsPresent ) = = 1 ) ;
changeRuleset ( 0 ) ;
AddAssert ( "5 columns visible" , ( ) = > this . ChildrenOfType < ModColumn > ( ) . Count ( col = > col . IsPresent ) = = 5 ) ;
}
2023-04-30 23:23:45 +08:00
[Test]
public void TestModMultiplierUpdates ( )
{
createScreen ( ) ;
AddStep ( "select mod preset with half time" , ( ) = >
{
InputManager . MoveMouseTo ( this . ChildrenOfType < ModPresetPanel > ( ) . Single ( preset = > preset . Preset . Value . Name = = "Half Time 0.5x" ) ) ;
InputManager . Click ( MouseButton . Left ) ;
} ) ;
AddAssert ( "difficulty multiplier display shows correct value" , ( ) = > modSelectOverlay . ChildrenOfType < DifficultyMultiplierDisplay > ( ) . Single ( ) . Current . Value , ( ) = > Is . EqualTo ( 0.5 ) ) ;
// this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation,
// it is instrumental in the reproduction of the failure scenario that this test is supposed to cover.
AddStep ( "force collection" , GC . Collect ) ;
AddStep ( "open customisation area" , ( ) = > modSelectOverlay . CustomisationButton ! . TriggerClick ( ) ) ;
AddStep ( "reset half time speed to default" , ( ) = > modSelectOverlay . ChildrenOfType < ModSettingsArea > ( ) . Single ( )
. ChildrenOfType < RestoreDefaultValueButton < double > > ( ) . Single ( ) . TriggerClick ( ) ) ;
AddUntilStep ( "difficulty multiplier display shows correct value" , ( ) = > modSelectOverlay . ChildrenOfType < DifficultyMultiplierDisplay > ( ) . Single ( ) . Current . Value , ( ) = > Is . EqualTo ( 0.7 ) ) ;
}
2022-04-04 02:24:32 +08:00
private void waitForColumnLoad ( ) = > AddUntilStep ( "all column content loaded" ,
2022-05-11 04:29:57 +08:00
( ) = > modSelectOverlay . ChildrenOfType < ModColumn > ( ) . Any ( ) & & modSelectOverlay . ChildrenOfType < ModColumn > ( ) . All ( column = > column . IsLoaded & & column . ItemsLoaded ) ) ;
2022-04-04 02:24:32 +08:00
private void changeRuleset ( int id )
{
AddStep ( $"set ruleset to {id}" , ( ) = > Ruleset . Value = rulesetStore . GetRuleset ( id ) ) ;
waitForColumnLoad ( ) ;
}
2022-03-27 06:21:17 +08:00
private void assertCustomisationToggleState ( bool disabled , bool active )
{
2022-08-15 03:35:09 +08:00
AddAssert ( $"customisation toggle is {(disabled ? "" : " not ")}disabled" , ( ) = > modSelectOverlay . CustomisationButton . AsNonNull ( ) . Active . Disabled = = disabled ) ;
AddAssert ( $"customisation toggle is {(active ? "" : " not ")}active" , ( ) = > modSelectOverlay . CustomisationButton . AsNonNull ( ) . Active . Value = = active ) ;
2022-03-27 06:21:17 +08:00
}
2022-04-22 05:49:40 +08:00
private ModPanel getPanelForMod ( Type modType )
2022-05-11 04:29:57 +08:00
= > modSelectOverlay . ChildrenOfType < ModPanel > ( ) . Single ( panel = > panel . Mod . GetType ( ) = = modType ) ;
2022-04-18 02:26:16 +08:00
2022-08-15 03:35:09 +08:00
private partial class TestModSelectOverlay : UserModSelectOverlay
{
protected override bool ShowPresets = > true ;
public new ShearedButton BackButton = > base . BackButton ;
public new ShearedToggleButton ? CustomisationButton = > base . CustomisationButton ;
}
2022-04-18 02:26:16 +08:00
private class TestUnimplementedMod : Mod
{
public override string Name = > "Unimplemented mod" ;
public override string Acronym = > "UM" ;
2022-08-11 04:09:11 +08:00
public override LocalisableString Description = > "A mod that is not implemented." ;
2022-04-18 02:26:16 +08:00
public override double ScoreMultiplier = > 1 ;
public override ModType Type = > ModType . Conversion ;
}
private class TestUnimplementedModOsuRuleset : OsuRuleset
{
public override string ShortName = > "unimplemented" ;
public override IEnumerable < Mod > GetModsFor ( ModType type )
{
if ( type = = ModType . Conversion ) return base . GetModsFor ( type ) . Concat ( new [ ] { new TestUnimplementedMod ( ) } ) ;
return base . GetModsFor ( type ) ;
}
}
2022-03-27 05:43:17 +08:00
}
}