// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osuTK; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface { public partial class TestSceneModDifficultyAdjustSettings : OsuManualInputManagerTestScene { private OsuModDifficultyAdjust modDifficultyAdjust; [SetUpSteps] public void SetUpSteps() { AddStep("create control", () => { modDifficultyAdjust = new OsuModDifficultyAdjust(); Child = new Container { Size = new Vector2(300), Anchor = Anchor.Centre, Origin = Anchor.Centre, Children = new Drawable[] { new Box { Colour = Color4.Black, RelativeSizeAxes = Axes.Both, }, new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, ChildrenEnumerable = modDifficultyAdjust.CreateSettingsControls(), }, } }; }); } [Test] public void TestFollowsBeatmapDefaultsVisually() { setBeatmapWithDifficultyParameters(5); checkSliderAtValue("Circle Size", 5); checkBindableAtValue("Circle Size", null); setBeatmapWithDifficultyParameters(8); checkSliderAtValue("Circle Size", 8); checkBindableAtValue("Circle Size", null); } [Test] public void TestValueAboveRangeStillApplied() { AddStep("set override cs to 11", () => modDifficultyAdjust.CircleSize.Value = 11); checkSliderAtValue("Circle Size", 11); checkBindableAtValue("Circle Size", 11); // this is a no-op, just showing that it won't reset the value during deserialisation. setExtendedLimits(false); checkSliderAtValue("Circle Size", 11); checkBindableAtValue("Circle Size", 11); // setting extended limits will reset the serialisation exception. // this should be fine as the goal is to allow, at most, the value of extended limits. setExtendedLimits(true); checkSliderAtValue("Circle Size", 11); checkBindableAtValue("Circle Size", 11); } [Test] public void TestValueBelowRangeStillApplied() { AddStep("set override cs to -5", () => modDifficultyAdjust.ApproachRate.Value = -5); checkSliderAtValue("Approach Rate", -5); checkBindableAtValue("Approach Rate", -5); // this is a no-op, just showing that it won't reset the value during deserialisation. setExtendedLimits(false); checkSliderAtValue("Approach Rate", -5); checkBindableAtValue("Approach Rate", -5); // setting extended limits will reset the serialisation exception. // this should be fine as the goal is to allow, at most, the value of extended limits. setExtendedLimits(true); checkSliderAtValue("Approach Rate", -5); checkBindableAtValue("Approach Rate", -5); } [Test] public void TestExtendedLimits() { setSliderValue("Circle Size", 99); checkSliderAtValue("Circle Size", 10); checkBindableAtValue("Circle Size", 10); setExtendedLimits(true); checkSliderAtValue("Circle Size", 10); checkBindableAtValue("Circle Size", 10); setSliderValue("Circle Size", 99); checkSliderAtValue("Circle Size", 11); checkBindableAtValue("Circle Size", 11); setSliderValue("Approach Rate", -5); checkSliderAtValue("Approach Rate", -5); checkBindableAtValue("Approach Rate", -5); setExtendedLimits(false); checkSliderAtValue("Circle Size", 10); checkBindableAtValue("Circle Size", 10); } [Test] public void TestUserOverrideMaintainedOnBeatmapChange() { setSliderValue("Circle Size", 9); setBeatmapWithDifficultyParameters(2); checkSliderAtValue("Circle Size", 9); checkBindableAtValue("Circle Size", 9); } [Test] public void TestExtendedLimitsRetainedAfterBoundCopyCreation() { setExtendedLimits(true); setSliderValue("Circle Size", 11); checkSliderAtValue("Circle Size", 11); checkBindableAtValue("Circle Size", 11); AddStep("create bound copy", () => _ = modDifficultyAdjust.CircleSize.GetBoundCopy()); checkSliderAtValue("Circle Size", 11); checkBindableAtValue("Circle Size", 11); } [Test] public void TestResetToDefault() { setBeatmapWithDifficultyParameters(2); setSliderValue("Circle Size", 9); checkSliderAtValue("Circle Size", 9); checkBindableAtValue("Circle Size", 9); resetToDefault("Circle Size"); checkSliderAtValue("Circle Size", 2); checkBindableAtValue("Circle Size", null); } [Test] public void TestUserOverrideMaintainedOnMatchingBeatmapValue() { setBeatmapWithDifficultyParameters(3); checkSliderAtValue("Circle Size", 3); checkBindableAtValue("Circle Size", null); // need to initially change it away from the current beatmap value to trigger an override. setSliderValue("Circle Size", 4); setSliderValue("Circle Size", 3); checkSliderAtValue("Circle Size", 3); checkBindableAtValue("Circle Size", 3); setBeatmapWithDifficultyParameters(4); checkSliderAtValue("Circle Size", 3); checkBindableAtValue("Circle Size", 3); } [Test] public void TestResetToDefaults() { setBeatmapWithDifficultyParameters(5); setSliderValue("Circle Size", 3); setExtendedLimits(true); checkSliderAtValue("Circle Size", 3); checkBindableAtValue("Circle Size", 3); AddStep("reset mod settings", () => modDifficultyAdjust.ResetSettingsToDefaults()); checkSliderAtValue("Circle Size", 5); checkBindableAtValue("Circle Size", null); } [Test] public void TestModSettingChangeTracker() { ModSettingChangeTracker tracker = null; Queue settingsChangedQueue = null; setBeatmapWithDifficultyParameters(5); AddStep("add mod settings change tracker", () => { settingsChangedQueue = new Queue(); tracker = new ModSettingChangeTracker(modDifficultyAdjust.Yield()) { SettingChanged = settingsChangedQueue.Enqueue }; }); AddAssert("no settings changed", () => settingsChangedQueue.Count == 0); setSliderValue("Circle Size", 3); settingsChangedFired(); setSliderValue("Circle Size", 5); checkBindableAtValue("Circle Size", 5); settingsChangedFired(); AddStep("reset mod settings", () => modDifficultyAdjust.CircleSize.SetDefault()); checkBindableAtValue("Circle Size", null); settingsChangedFired(); setExtendedLimits(true); settingsChangedFired(); AddStep("dispose tracker", () => { tracker.Dispose(); tracker = null; }); void settingsChangedFired() { AddAssert("setting changed event fired", () => { settingsChangedQueue.Dequeue(); return settingsChangedQueue.Count == 0; }); } } private void resetToDefault(string name) { AddStep($"Reset {name} to default", () => this.ChildrenOfType().First(c => c.LabelText == name) .Current.SetDefault()); } private void setExtendedLimits(bool status) => AddStep($"Set extended limits {status}", () => modDifficultyAdjust.ExtendedLimits.Value = status); private void setSliderValue(string name, float value) { AddStep($"Set {name} slider to {value}", () => this.ChildrenOfType().First(c => c.LabelText == name) .ChildrenOfType>().First().Current.Value = value); } private void checkBindableAtValue(string name, float? expectedValue) { AddAssert($"Bindable {name} is {(expectedValue?.ToString() ?? "null")}", () => this.ChildrenOfType().First(c => c.LabelText == name) .Current.Value == expectedValue); } private void checkSliderAtValue(string name, float expectedValue) { AddAssert($"Slider {name} at {expectedValue}", () => this.ChildrenOfType().First(c => c.LabelText == name) .ChildrenOfType>().First().Current.Value == expectedValue); } private void setBeatmapWithDifficultyParameters(float value) { AddStep($"set beatmap with all {value}", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap { BeatmapInfo = new BeatmapInfo { Difficulty = new BeatmapDifficulty { OverallDifficulty = value, CircleSize = value, DrainRate = value, ApproachRate = value, } } })); } } }