diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index bdd1b92c8d..1d3dd35e1d 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -5,8 +5,11 @@ using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics.Textures; using osu.Framework.Screens; using osu.Framework.Testing; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Graphics.Backgrounds; @@ -15,6 +18,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens; using osu.Game.Screens.Backgrounds; using osu.Game.Skinning; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual.Background { @@ -22,8 +26,7 @@ namespace osu.Game.Tests.Visual.Background public class TestSceneBackgroundScreenDefault : OsuTestScene { private BackgroundScreenStack stack; - private BackgroundScreenDefault screen; - + private TestBackgroundScreenDefault screen; private Graphics.Backgrounds.Background getCurrentBackground() => screen.ChildrenOfType().FirstOrDefault(); [Resolved] @@ -36,10 +39,95 @@ namespace osu.Game.Tests.Visual.Background public void SetUpSteps() { AddStep("create background stack", () => Child = stack = new BackgroundScreenStack()); - AddStep("push default screen", () => stack.Push(screen = new BackgroundScreenDefault(false))); + AddStep("push default screen", () => stack.Push(screen = new TestBackgroundScreenDefault())); AddUntilStep("wait for screen to load", () => screen.IsCurrentScreen()); } + [Test] + public void TestBeatmapBackgroundTracksBeatmap() + { + setSupporter(true); + setSourceMode(BackgroundSource.Beatmap); + + AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithUniqueBackground()); + AddAssert("background changed", () => screen.CheckLastLoadChange() == true); + + Graphics.Backgrounds.Background last = null; + + AddUntilStep("wait for beatmap background to be loaded", () => getCurrentBackground()?.GetType() == typeof(BeatmapBackground)); + AddStep("store background", () => last = getCurrentBackground()); + + AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithUniqueBackground()); + + AddUntilStep("wait for beatmap background to change", () => screen.CheckLastLoadChange() == true); + + AddUntilStep("background is new beatmap background", () => last != getCurrentBackground()); + AddStep("store background", () => last = getCurrentBackground()); + + AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithUniqueBackground()); + + AddUntilStep("wait for beatmap background to change", () => screen.CheckLastLoadChange() == true); + AddUntilStep("background is new beatmap background", () => last != getCurrentBackground()); + } + + [Test] + public void TestBeatmapBackgroundTracksBeatmapWhenSuspended() + { + setSupporter(true); + setSourceMode(BackgroundSource.Beatmap); + + AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithUniqueBackground()); + AddAssert("background changed", () => screen.CheckLastLoadChange() == true); + AddUntilStep("wait for beatmap background to be loaded", () => getCurrentBackground()?.GetType() == typeof(BeatmapBackground)); + + BackgroundScreenBeatmap nestedScreen = null; + + // of note, this needs to be a type that doesn't match BackgroundScreenDefault else it is silently not pushed by the background stack. + AddStep("push new background to stack", () => stack.Push(nestedScreen = new BackgroundScreenBeatmap(Beatmap.Value))); + AddUntilStep("wait for screen to load", () => nestedScreen.IsLoaded && nestedScreen.IsCurrentScreen()); + + AddAssert("top level background hasn't changed yet", () => screen.CheckLastLoadChange() == null); + + AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithUniqueBackground()); + + AddAssert("top level background hasn't changed yet", () => screen.CheckLastLoadChange() == null); + + AddStep("pop screen back to top level", () => screen.MakeCurrent()); + + AddAssert("top level background changed", () => screen.CheckLastLoadChange() == true); + } + + [Test] + public void TestBeatmapBackgroundIgnoresNoChangeWhenSuspended() + { + BackgroundScreenBeatmap nestedScreen = null; + WorkingBeatmap originalWorking = null; + + setSupporter(true); + setSourceMode(BackgroundSource.Beatmap); + + AddStep("change beatmap", () => originalWorking = Beatmap.Value = createTestWorkingBeatmapWithUniqueBackground()); + AddAssert("background changed", () => screen.CheckLastLoadChange() == true); + AddUntilStep("wait for beatmap background to be loaded", () => getCurrentBackground()?.GetType() == typeof(BeatmapBackground)); + + // of note, this needs to be a type that doesn't match BackgroundScreenDefault else it is silently not pushed by the background stack. + AddStep("push new background to stack", () => stack.Push(nestedScreen = new BackgroundScreenBeatmap(Beatmap.Value))); + AddUntilStep("wait for screen to load", () => nestedScreen.IsLoaded && nestedScreen.IsCurrentScreen()); + + // we're testing a case where scheduling may be used to avoid issues, so ensure the scheduler is no longer running. + AddUntilStep("wait for top level not alive", () => !screen.IsAlive); + + AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithUniqueBackground()); + AddStep("change beatmap back", () => Beatmap.Value = originalWorking); + + AddAssert("top level background hasn't changed yet", () => screen.CheckLastLoadChange() == null); + + AddStep("pop screen back to top level", () => screen.MakeCurrent()); + + AddStep("top level screen is current", () => screen.IsCurrentScreen()); + AddAssert("top level background reused existing", () => screen.CheckLastLoadChange() == false); + } + [Test] public void TestBackgroundTypeSwitch() { @@ -78,36 +166,24 @@ namespace osu.Game.Tests.Visual.Background [TestCase(BackgroundSource.Skin, typeof(SkinBackground))] public void TestBackgroundDoesntReloadOnNoChange(BackgroundSource source, Type backgroundType) { - Graphics.Backgrounds.Background last = null; - setSourceMode(source); setSupporter(true); if (source == BackgroundSource.Skin) setCustomSkin(); - AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == backgroundType); + AddUntilStep("wait for beatmap background to be loaded", () => (getCurrentBackground())?.GetType() == backgroundType); AddAssert("next doesn't load new background", () => screen.Next() == false); - - // doesn't really need to be checked but might as well. - AddWaitStep("wait a bit", 5); - AddUntilStep("ensure same background instance", () => last == getCurrentBackground()); } [Test] public void TestBackgroundCyclingOnDefaultSkin([Values] bool supporter) { - Graphics.Backgrounds.Background last = null; - setSourceMode(BackgroundSource.Skin); setSupporter(supporter); setDefaultSkin(); - AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == typeof(Graphics.Backgrounds.Background)); + AddUntilStep("wait for beatmap background to be loaded", () => (getCurrentBackground())?.GetType() == typeof(Graphics.Backgrounds.Background)); AddAssert("next cycles background", () => screen.Next()); - - // doesn't really need to be checked but might as well. - AddWaitStep("wait a bit", 5); - AddUntilStep("ensure different background instance", () => last != getCurrentBackground()); } private void setSourceMode(BackgroundSource source) => @@ -120,6 +196,42 @@ namespace osu.Game.Tests.Visual.Background Id = API.LocalUser.Value.Id + 1, }); + private WorkingBeatmap createTestWorkingBeatmapWithUniqueBackground() => new UniqueBackgroundTestWorkingBeatmap(Audio); + + private class TestBackgroundScreenDefault : BackgroundScreenDefault + { + private bool? lastLoadTriggerCausedChange; + + public TestBackgroundScreenDefault() + : base(false) + { + } + + public override bool Next() + { + bool didChange = base.Next(); + lastLoadTriggerCausedChange = didChange; + return didChange; + } + + public bool? CheckLastLoadChange() + { + bool? lastChange = lastLoadTriggerCausedChange; + lastLoadTriggerCausedChange = null; + return lastChange; + } + } + + private class UniqueBackgroundTestWorkingBeatmap : TestWorkingBeatmap + { + public UniqueBackgroundTestWorkingBeatmap(AudioManager audioManager) + : base(new Beatmap(), null, audioManager) + { + } + + protected override Texture GetBackground() => new Texture(1, 1); + } + private void setCustomSkin() { // feign a skin switch. this doesn't do anything except force CurrentSkin to become a LegacySkin. diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 4a922c45b9..452f033dcc 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -48,16 +48,19 @@ namespace osu.Game.Screens.Backgrounds AddInternal(seasonalBackgroundLoader); - user.ValueChanged += _ => Next(); - skin.ValueChanged += _ => Next(); - mode.ValueChanged += _ => Next(); - beatmap.ValueChanged += _ => Next(); - introSequence.ValueChanged += _ => Next(); - seasonalBackgroundLoader.SeasonalBackgroundChanged += () => Next(); + user.ValueChanged += _ => Scheduler.AddOnce(loadNextIfRequired); + skin.ValueChanged += _ => Scheduler.AddOnce(loadNextIfRequired); + mode.ValueChanged += _ => Scheduler.AddOnce(loadNextIfRequired); + beatmap.ValueChanged += _ => Scheduler.AddOnce(loadNextIfRequired); + introSequence.ValueChanged += _ => Scheduler.AddOnce(loadNextIfRequired); + seasonalBackgroundLoader.SeasonalBackgroundChanged += () => Scheduler.AddOnce(loadNextIfRequired); currentDisplay = RNG.Next(0, background_count); Next(); + + // helper function required for AddOnce usage. + void loadNextIfRequired() => Next(); } private ScheduledDelegate nextTask; @@ -67,7 +70,7 @@ namespace osu.Game.Screens.Backgrounds /// Request loading the next background. /// /// Whether a new background was queued for load. May return false if the current background is still valid. - public bool Next() + public virtual bool Next() { var nextBackground = createBackground();