mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 07:06:35 +09:00
Merge branch 'master' into update-overlay-headers
This commit is contained in:
@ -4,7 +4,6 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
@ -74,9 +73,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Beatmap.Value = working;
|
||||
SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) };
|
||||
|
||||
Player?.Exit();
|
||||
Player = null;
|
||||
|
||||
Player = CreatePlayer(ruleset);
|
||||
|
||||
LoadScreen(Player);
|
||||
|
@ -3,8 +3,14 @@
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.Break;
|
||||
using osu.Game.Screens.Ranking;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
@ -15,20 +21,38 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
protected override Player CreatePlayer(Ruleset ruleset)
|
||||
{
|
||||
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
|
||||
return new TestPlayer(false, false);
|
||||
SelectedMods.Value = new[] { ruleset.GetAutoplayMod() };
|
||||
return new TestPlayer(false);
|
||||
}
|
||||
|
||||
protected override void AddCheckSteps()
|
||||
{
|
||||
AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
|
||||
AddStep("seek to break time", () => Player.GameplayClockContainer.Seek(Player.BreakOverlay.Breaks.First().StartTime));
|
||||
AddUntilStep("wait for seek to complete", () =>
|
||||
Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= Player.BreakOverlay.Breaks.First().StartTime);
|
||||
AddAssert("test keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
|
||||
seekToBreak(0);
|
||||
AddAssert("keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
|
||||
AddAssert("overlay displays 100% accuracy", () => Player.BreakOverlay.ChildrenOfType<BreakInfo>().Single().AccuracyDisplay.Current.Value == 1);
|
||||
AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
|
||||
AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
|
||||
|
||||
seekToBreak(0);
|
||||
seekToBreak(1);
|
||||
|
||||
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
||||
AddUntilStep("results displayed", () => getResultsScreen() != null);
|
||||
|
||||
AddAssert("score has combo", () => getResultsScreen().Score.Combo > 100);
|
||||
AddAssert("score has no misses", () => getResultsScreen().Score.Statistics[HitResult.Miss] == 0);
|
||||
|
||||
ResultsScreen getResultsScreen() => Stack.CurrentScreen as ResultsScreen;
|
||||
}
|
||||
|
||||
private void seekToBreak(int breakIndex)
|
||||
{
|
||||
AddStep($"seek to break {breakIndex}", () => Player.GameplayClockContainer.Seek(destBreak().StartTime));
|
||||
AddUntilStep("wait for seek to complete", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= destBreak().StartTime);
|
||||
|
||||
BreakPeriod destBreak() => Player.ChildrenOfType<BreakTracker>().First().Breaks.ElementAt(breakIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -12,32 +13,40 @@ using osu.Game.Screens.Play;
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneBreakOverlay : OsuTestScene
|
||||
public class TestSceneBreakTracker : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(BreakOverlay),
|
||||
};
|
||||
|
||||
private readonly TestBreakOverlay breakOverlay;
|
||||
private readonly BreakOverlay breakOverlay;
|
||||
|
||||
private readonly TestBreakTracker breakTracker;
|
||||
|
||||
private readonly IReadOnlyList<BreakPeriod> testBreaks = new List<BreakPeriod>
|
||||
{
|
||||
new BreakPeriod
|
||||
{
|
||||
StartTime = 1000,
|
||||
EndTime = 5000,
|
||||
},
|
||||
new BreakPeriod
|
||||
{
|
||||
StartTime = 6000,
|
||||
EndTime = 13500,
|
||||
},
|
||||
new BreakPeriod(1000, 5000),
|
||||
new BreakPeriod(6000, 13500),
|
||||
};
|
||||
|
||||
public TestSceneBreakOverlay()
|
||||
public TestSceneBreakTracker()
|
||||
{
|
||||
Add(breakOverlay = new TestBreakOverlay(true));
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
breakTracker = new TestBreakTracker(),
|
||||
breakOverlay = new BreakOverlay(true, null)
|
||||
{
|
||||
ProcessCustomClock = false,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
breakOverlay.Clock = breakTracker.Clock;
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -53,7 +62,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestNoEffectsBreak()
|
||||
{
|
||||
var shortBreak = new BreakPeriod { EndTime = 500 };
|
||||
var shortBreak = new BreakPeriod(0, 500);
|
||||
|
||||
setClock(true);
|
||||
loadBreaksStep("short break", new[] { shortBreak });
|
||||
@ -88,7 +97,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
loadBreaksStep("multiple breaks", testBreaks);
|
||||
|
||||
seekAndAssertBreak("seek to break start", testBreaks[1].StartTime, true);
|
||||
AddAssert("is skipped to break #2", () => breakOverlay.CurrentBreakIndex == 1);
|
||||
AddAssert("is skipped to break #2", () => breakTracker.CurrentBreakIndex == 1);
|
||||
|
||||
seekAndAssertBreak("seek to break middle", testBreaks[1].StartTime + testBreaks[1].Duration / 2, true);
|
||||
seekAndAssertBreak("seek to break end", testBreaks[1].EndTime, false);
|
||||
@ -110,24 +119,23 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void addShowBreakStep(double seconds)
|
||||
{
|
||||
AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = new List<BreakPeriod>
|
||||
AddStep($"show '{seconds}s' break", () =>
|
||||
{
|
||||
new BreakPeriod
|
||||
breakOverlay.Breaks = breakTracker.Breaks = new List<BreakPeriod>
|
||||
{
|
||||
StartTime = Clock.CurrentTime,
|
||||
EndTime = Clock.CurrentTime + seconds * 1000,
|
||||
}
|
||||
new BreakPeriod(Clock.CurrentTime, Clock.CurrentTime + seconds * 1000)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void setClock(bool useManual)
|
||||
{
|
||||
AddStep($"set {(useManual ? "manual" : "realtime")} clock", () => breakOverlay.SwitchClock(useManual));
|
||||
AddStep($"set {(useManual ? "manual" : "realtime")} clock", () => breakTracker.SwitchClock(useManual));
|
||||
}
|
||||
|
||||
private void loadBreaksStep(string breakDescription, IReadOnlyList<BreakPeriod> breaks)
|
||||
{
|
||||
AddStep($"load {breakDescription}", () => breakOverlay.Breaks = breaks);
|
||||
AddStep($"load {breakDescription}", () => breakOverlay.Breaks = breakTracker.Breaks = breaks);
|
||||
seekAndAssertBreak("seek back to 0", 0, false);
|
||||
}
|
||||
|
||||
@ -151,17 +159,18 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void seekAndAssertBreak(string seekStepDescription, double time, bool shouldBeBreak)
|
||||
{
|
||||
AddStep(seekStepDescription, () => breakOverlay.ManualClockTime = time);
|
||||
AddStep(seekStepDescription, () => breakTracker.ManualClockTime = time);
|
||||
AddAssert($"is{(!shouldBeBreak ? " not" : string.Empty)} break time", () =>
|
||||
{
|
||||
breakOverlay.ProgressTime();
|
||||
return breakOverlay.IsBreakTime.Value == shouldBeBreak;
|
||||
breakTracker.ProgressTime();
|
||||
return breakTracker.IsBreakTime.Value == shouldBeBreak;
|
||||
});
|
||||
}
|
||||
|
||||
private class TestBreakOverlay : BreakOverlay
|
||||
private class TestBreakTracker : BreakTracker
|
||||
{
|
||||
private readonly FramedClock framedManualClock;
|
||||
public readonly FramedClock FramedManualClock;
|
||||
|
||||
private readonly ManualClock manualClock;
|
||||
private IFrameBasedClock originalClock;
|
||||
|
||||
@ -173,20 +182,19 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
set => manualClock.CurrentTime = value;
|
||||
}
|
||||
|
||||
public TestBreakOverlay(bool letterboxing)
|
||||
: base(letterboxing)
|
||||
public TestBreakTracker()
|
||||
{
|
||||
framedManualClock = new FramedClock(manualClock = new ManualClock());
|
||||
FramedManualClock = new FramedClock(manualClock = new ManualClock());
|
||||
ProcessCustomClock = false;
|
||||
}
|
||||
|
||||
public void ProgressTime()
|
||||
{
|
||||
framedManualClock.ProcessFrame();
|
||||
FramedManualClock.ProcessFrame();
|
||||
Update();
|
||||
}
|
||||
|
||||
public void SwitchClock(bool setManual) => Clock = setManual ? framedManualClock : originalClock;
|
||||
public void SwitchClock(bool setManual) => Clock = setManual ? FramedManualClock : originalClock;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
281
osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs
Normal file
281
osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs
Normal file
@ -0,0 +1,281 @@
|
||||
// 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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Tests.Visual.UserInterface;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneReplayRecorder : OsuManualInputManagerTestScene
|
||||
{
|
||||
private TestRulesetInputManager playbackManager;
|
||||
private TestRulesetInputManager recordingManager;
|
||||
|
||||
private Replay replay;
|
||||
|
||||
private TestReplayRecorder recorder;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
replay = new Replay();
|
||||
|
||||
Add(new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
||||
{
|
||||
Recorder = recorder = new TestReplayRecorder(replay)
|
||||
{
|
||||
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
|
||||
},
|
||||
Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Brown,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "Recording",
|
||||
Scale = new Vector2(3),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new TestInputConsumer()
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
||||
{
|
||||
ReplayInputHandler = new TestFramedReplayInputHandler(replay)
|
||||
{
|
||||
GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
|
||||
},
|
||||
Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.DarkBlue,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "Playback",
|
||||
Scale = new Vector2(3),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new TestInputConsumer()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
|
||||
AddUntilStep("one frame recorded", () => replay.Frames.Count == 1);
|
||||
AddAssert("position matches", () => playbackManager.ChildrenOfType<Box>().First().Position == recordingManager.ChildrenOfType<Box>().First().Position);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHighFrameRate()
|
||||
{
|
||||
ScheduledDelegate moveFunction = null;
|
||||
|
||||
AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
|
||||
AddStep("much move", () => moveFunction = Scheduler.AddDelayed(() =>
|
||||
InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0)), 10, true));
|
||||
AddWaitStep("move", 10);
|
||||
AddStep("stop move", () => moveFunction.Cancel());
|
||||
AddAssert("at least 60 frames recorded", () => replay.Frames.Count > 60);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLimitedFrameRate()
|
||||
{
|
||||
ScheduledDelegate moveFunction = null;
|
||||
|
||||
AddStep("lower rate", () => recorder.RecordFrameRate = 2);
|
||||
AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
|
||||
AddStep("much move", () => moveFunction = Scheduler.AddDelayed(() =>
|
||||
InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0)), 10, true));
|
||||
AddWaitStep("move", 10);
|
||||
AddStep("stop move", () => moveFunction.Cancel());
|
||||
AddAssert("less than 10 frames recorded", () => replay.Frames.Count < 10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLimitedFrameRateWithImportantFrames()
|
||||
{
|
||||
ScheduledDelegate moveFunction = null;
|
||||
|
||||
AddStep("lower rate", () => recorder.RecordFrameRate = 2);
|
||||
AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
|
||||
AddStep("much move with press", () => moveFunction = Scheduler.AddDelayed(() =>
|
||||
{
|
||||
InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0));
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
InputManager.ReleaseButton(MouseButton.Left);
|
||||
}, 10, true));
|
||||
AddWaitStep("move", 10);
|
||||
AddStep("stop move", () => moveFunction.Cancel());
|
||||
AddAssert("at least 60 frames recorded", () => replay.Frames.Count > 60);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
playbackManager?.ReplayInputHandler.SetFrameFromTime(Time.Current - 100);
|
||||
}
|
||||
|
||||
public class TestFramedReplayInputHandler : FramedReplayInputHandler<TestReplayFrame>
|
||||
{
|
||||
public TestFramedReplayInputHandler(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
public override List<IInput> GetPendingInputs()
|
||||
{
|
||||
return new List<IInput>
|
||||
{
|
||||
new MousePositionAbsoluteInput
|
||||
{
|
||||
Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero)
|
||||
},
|
||||
new ReplayState<TestAction>
|
||||
{
|
||||
PressedActions = CurrentFrame?.Actions ?? new List<TestAction>()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class TestInputConsumer : CompositeDrawable, IKeyBindingHandler<TestAction>
|
||||
{
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
private readonly Box box;
|
||||
|
||||
public TestInputConsumer()
|
||||
{
|
||||
Size = new Vector2(30);
|
||||
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
box = new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
Position = e.MousePosition;
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
public bool OnPressed(TestAction action)
|
||||
{
|
||||
box.Colour = Color4.White;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnReleased(TestAction action)
|
||||
{
|
||||
box.Colour = Color4.Black;
|
||||
}
|
||||
}
|
||||
|
||||
public class TestRulesetInputManager : RulesetInputManager<TestAction>
|
||||
{
|
||||
public TestRulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
: base(ruleset, variant, unique)
|
||||
{
|
||||
}
|
||||
|
||||
protected override KeyBindingContainer<TestAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
=> new TestKeyBindingContainer();
|
||||
|
||||
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
|
||||
{
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class TestReplayFrame : ReplayFrame
|
||||
{
|
||||
public Vector2 Position;
|
||||
|
||||
public List<TestAction> Actions = new List<TestAction>();
|
||||
|
||||
public TestReplayFrame(double time, Vector2 position, params TestAction[] actions)
|
||||
: base(time)
|
||||
{
|
||||
Position = position;
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TestAction
|
||||
{
|
||||
Down,
|
||||
}
|
||||
|
||||
internal class TestReplayRecorder : ReplayRecorder<TestAction>
|
||||
{
|
||||
public TestReplayRecorder(Replay target)
|
||||
: base(target)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<TestAction> actions, ReplayFrame previousFrame)
|
||||
=> new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
220
osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs
Normal file
220
osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs
Normal file
@ -0,0 +1,220 @@
|
||||
// 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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Tests.Visual.UserInterface;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneReplayRecording : OsuTestScene
|
||||
{
|
||||
private readonly TestRulesetInputManager playbackManager;
|
||||
|
||||
private readonly TestRulesetInputManager recordingManager;
|
||||
|
||||
public TestSceneReplayRecording()
|
||||
{
|
||||
Replay replay = new Replay();
|
||||
|
||||
Add(new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
||||
{
|
||||
Recorder = new TestReplayRecorder(replay)
|
||||
{
|
||||
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos)
|
||||
},
|
||||
Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Brown,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "Recording",
|
||||
Scale = new Vector2(3),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new TestConsumer()
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
||||
{
|
||||
ReplayInputHandler = new TestFramedReplayInputHandler(replay)
|
||||
{
|
||||
GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
|
||||
},
|
||||
Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.DarkBlue,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "Playback",
|
||||
Scale = new Vector2(3),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new TestConsumer()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
playbackManager.ReplayInputHandler.SetFrameFromTime(Time.Current - 500);
|
||||
}
|
||||
}
|
||||
|
||||
public class TestFramedReplayInputHandler : FramedReplayInputHandler<TestReplayFrame>
|
||||
{
|
||||
public TestFramedReplayInputHandler(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
public override List<IInput> GetPendingInputs()
|
||||
{
|
||||
return new List<IInput>
|
||||
{
|
||||
new MousePositionAbsoluteInput
|
||||
{
|
||||
Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero)
|
||||
},
|
||||
new ReplayState<TestAction>
|
||||
{
|
||||
PressedActions = CurrentFrame?.Actions ?? new List<TestAction>()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class TestConsumer : CompositeDrawable, IKeyBindingHandler<TestAction>
|
||||
{
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
private readonly Box box;
|
||||
|
||||
public TestConsumer()
|
||||
{
|
||||
Size = new Vector2(30);
|
||||
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
box = new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
Position = e.MousePosition;
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
public bool OnPressed(TestAction action)
|
||||
{
|
||||
box.Colour = Color4.White;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnReleased(TestAction action)
|
||||
{
|
||||
box.Colour = Color4.Black;
|
||||
}
|
||||
}
|
||||
|
||||
public class TestRulesetInputManager : RulesetInputManager<TestAction>
|
||||
{
|
||||
public TestRulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
: base(ruleset, variant, unique)
|
||||
{
|
||||
}
|
||||
|
||||
protected override KeyBindingContainer<TestAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
=> new TestKeyBindingContainer();
|
||||
|
||||
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
|
||||
{
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class TestReplayFrame : ReplayFrame
|
||||
{
|
||||
public Vector2 Position;
|
||||
|
||||
public List<TestAction> Actions = new List<TestAction>();
|
||||
|
||||
public TestReplayFrame(double time, Vector2 position, params TestAction[] actions)
|
||||
: base(time)
|
||||
{
|
||||
Position = position;
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TestAction
|
||||
{
|
||||
Down,
|
||||
}
|
||||
|
||||
internal class TestReplayRecorder : ReplayRecorder<TestAction>
|
||||
{
|
||||
public TestReplayRecorder(Replay target)
|
||||
: base(target)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<TestAction> actions, ReplayFrame previousFrame) =>
|
||||
new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
|
||||
}
|
||||
}
|
@ -43,16 +43,15 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling)
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 30, 50 }));
|
||||
AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 50 }));
|
||||
AddStep("adjust scale", () => fill.Scale = new Vector2(2));
|
||||
AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 30, 50 }));
|
||||
AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 50 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -74,7 +73,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Children = new[]
|
||||
{
|
||||
new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling)
|
||||
}
|
||||
@ -82,9 +80,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
};
|
||||
});
|
||||
|
||||
AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 30, 50, 30 }));
|
||||
AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 50, 30 }));
|
||||
AddStep("adjust scale", () => fill.Scale = new Vector2(2));
|
||||
AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 30, 50, 30 }));
|
||||
AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 50, 30 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -182,7 +180,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public new Drawable Drawable => base.Drawable;
|
||||
|
||||
public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null,
|
||||
ConfineMode confineMode = ConfineMode.ScaleDownToFit)
|
||||
ConfineMode confineMode = ConfineMode.ScaleToFit)
|
||||
: base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode)
|
||||
{
|
||||
}
|
||||
|
@ -9,8 +9,12 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Game.Storyboards.Drawables;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
@ -54,7 +58,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
State = { Value = Visibility.Visible },
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStoryboard()
|
||||
{
|
||||
AddStep("Restart", restart);
|
||||
AddToggleStep("Passing", passing =>
|
||||
{
|
||||
@ -62,6 +70,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStoryboardMissingVideo()
|
||||
{
|
||||
AddStep("Load storyboard with missing video", loadStoryboardNoVideo);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@ -94,5 +108,28 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
storyboardContainer.Add(storyboard);
|
||||
decoupledClock.ChangeSource(working.Track);
|
||||
}
|
||||
|
||||
private void loadStoryboardNoVideo()
|
||||
{
|
||||
if (storyboard != null)
|
||||
storyboardContainer.Remove(storyboard);
|
||||
|
||||
var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
|
||||
storyboardContainer.Clock = decoupledClock;
|
||||
|
||||
Storyboard sb;
|
||||
|
||||
using (var str = TestResources.OpenResource("storyboard_no_video.osu"))
|
||||
using (var bfr = new LineBufferedReader(str))
|
||||
{
|
||||
var decoder = new LegacyStoryboardDecoder();
|
||||
sb = decoder.Decode(bfr);
|
||||
}
|
||||
|
||||
storyboard = sb.CreateDrawable(Beatmap.Value);
|
||||
|
||||
storyboardContainer.Add(storyboard);
|
||||
decoupledClock.ChangeSource(Beatmap.Value.Track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
typeof(StartupScreen),
|
||||
typeof(IntroScreen),
|
||||
typeof(OsuScreen),
|
||||
typeof(IntroTestScene),
|
||||
};
|
||||
|
||||
@ -64,6 +63,8 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
|
||||
introStack.Push(CreateScreen());
|
||||
});
|
||||
|
||||
AddUntilStep("wait for menu", () => introStack.CurrentScreen is MainMenu);
|
||||
}
|
||||
|
||||
protected abstract IScreen CreateScreen();
|
||||
|
@ -36,8 +36,6 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
[Test]
|
||||
public void TestInstantLoad()
|
||||
{
|
||||
// visual only, very impossible to test this using asserts.
|
||||
|
||||
AddStep("load immediately", () =>
|
||||
{
|
||||
loader = new TestLoader();
|
||||
@ -46,12 +44,17 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
LoadScreen(loader);
|
||||
});
|
||||
|
||||
AddAssert("spinner did not display", () => loader.LoadingSpinner?.Alpha == 0);
|
||||
spinnerNotPresentOrHidden();
|
||||
|
||||
AddUntilStep("loaded", () => loader.ScreenLoaded);
|
||||
AddUntilStep("not current", () => !loader.IsCurrentScreen());
|
||||
|
||||
spinnerNotPresentOrHidden();
|
||||
}
|
||||
|
||||
private void spinnerNotPresentOrHidden() =>
|
||||
AddAssert("spinner did not display", () => loader.LoadingSpinner == null || loader.LoadingSpinner.Alpha == 0);
|
||||
|
||||
[Test]
|
||||
public void TestDelayedLoad()
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
private readonly Bindable<UserStatus> status = new Bindable<UserStatus>();
|
||||
|
||||
private UserGridPanel peppy;
|
||||
private UserListPanel evast;
|
||||
private TestUserListPanel evast;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesetStore { get; set; }
|
||||
@ -38,6 +38,9 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
UserGridPanel flyte;
|
||||
|
||||
activity.Value = null;
|
||||
status.Value = null;
|
||||
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
@ -63,7 +66,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
IsSupporter = true,
|
||||
SupportLevel = 3,
|
||||
}) { Width = 300 },
|
||||
evast = new UserListPanel(new User
|
||||
evast = new TestUserListPanel(new User
|
||||
{
|
||||
Username = @"Evast",
|
||||
Id = 8195163,
|
||||
@ -96,7 +99,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
[Test]
|
||||
public void TestUserActivity()
|
||||
{
|
||||
AddStep("set online status", () => peppy.Status.Value = evast.Status.Value = new UserStatusOnline());
|
||||
AddStep("set online status", () => status.Value = new UserStatusOnline());
|
||||
|
||||
AddStep("idle", () => activity.Value = null);
|
||||
AddStep("spectating", () => activity.Value = new UserActivity.Spectating());
|
||||
@ -109,6 +112,29 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddStep("modding", () => activity.Value = new UserActivity.Modding());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserActivityChange()
|
||||
{
|
||||
AddAssert("visit message is visible", () => evast.LastVisitMessage.IsPresent);
|
||||
AddStep("set online status", () => status.Value = new UserStatusOnline());
|
||||
AddAssert("visit message is not visible", () => !evast.LastVisitMessage.IsPresent);
|
||||
AddStep("set choosing activity", () => activity.Value = new UserActivity.ChoosingBeatmap());
|
||||
AddStep("set offline status", () => status.Value = new UserStatusOffline());
|
||||
AddAssert("visit message is visible", () => evast.LastVisitMessage.IsPresent);
|
||||
AddStep("set online status", () => status.Value = new UserStatusOnline());
|
||||
AddAssert("visit message is not visible", () => !evast.LastVisitMessage.IsPresent);
|
||||
}
|
||||
|
||||
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.SoloGame(null, rulesetStore.GetRuleset(rulesetId));
|
||||
|
||||
private class TestUserListPanel : UserListPanel
|
||||
{
|
||||
public TestUserListPanel(User user)
|
||||
: base(user)
|
||||
{
|
||||
}
|
||||
|
||||
public new TextFlowContainer LastVisitMessage => base.LastVisitMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,82 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
waitForSelection(set_count, 3);
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void TestTraversalBeyondVisible(bool forwards)
|
||||
{
|
||||
var sets = new List<BeatmapSetInfo>();
|
||||
|
||||
const int total_set_count = 200;
|
||||
|
||||
for (int i = 0; i < total_set_count; i++)
|
||||
sets.Add(createTestBeatmapSet(i + 1));
|
||||
|
||||
loadBeatmaps(sets);
|
||||
|
||||
for (int i = 1; i < total_set_count; i += i)
|
||||
selectNextAndAssert(i);
|
||||
|
||||
void selectNextAndAssert(int amount)
|
||||
{
|
||||
setSelected(forwards ? 1 : total_set_count, 1);
|
||||
|
||||
AddStep($"{(forwards ? "Next" : "Previous")} beatmap {amount} times", () =>
|
||||
{
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
carousel.SelectNext(forwards ? 1 : -1);
|
||||
}
|
||||
});
|
||||
|
||||
waitForSelection(forwards ? amount + 1 : total_set_count - amount);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTraversalBeyondVisibleDifficulties()
|
||||
{
|
||||
var sets = new List<BeatmapSetInfo>();
|
||||
|
||||
const int total_set_count = 20;
|
||||
|
||||
for (int i = 0; i < total_set_count; i++)
|
||||
sets.Add(createTestBeatmapSet(i + 1));
|
||||
|
||||
loadBeatmaps(sets);
|
||||
|
||||
// Selects next set once, difficulty index doesn't change
|
||||
selectNextAndAssert(3, true, 2, 1);
|
||||
|
||||
// Selects next set 16 times (50 \ 3 == 16), difficulty index changes twice (50 % 3 == 2)
|
||||
selectNextAndAssert(50, true, 17, 3);
|
||||
|
||||
// Travels around the carousel thrice (200 \ 60 == 3)
|
||||
// continues to select 20 times (200 \ 60 == 20)
|
||||
// selects next set 6 times (20 \ 3 == 6)
|
||||
// difficulty index changes twice (20 % 3 == 2)
|
||||
selectNextAndAssert(200, true, 7, 3);
|
||||
|
||||
// All same but in reverse
|
||||
selectNextAndAssert(3, false, 19, 3);
|
||||
selectNextAndAssert(50, false, 4, 1);
|
||||
selectNextAndAssert(200, false, 14, 1);
|
||||
|
||||
void selectNextAndAssert(int amount, bool forwards, int expectedSet, int expectedDiff)
|
||||
{
|
||||
// Select very first or very last difficulty
|
||||
setSelected(forwards ? 1 : 20, forwards ? 1 : 3);
|
||||
|
||||
AddStep($"{(forwards ? "Next" : "Previous")} difficulty {amount} times", () =>
|
||||
{
|
||||
for (int i = 0; i < amount; i++)
|
||||
carousel.SelectNext(forwards ? 1 : -1, false);
|
||||
});
|
||||
|
||||
waitForSelection(expectedSet, expectedDiff);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test filtering
|
||||
/// </summary>
|
||||
|
@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Position = new Vector2(0, 25),
|
||||
Position = new Vector2(-5, 25),
|
||||
Current = { BindTarget = modSelect.SelectedMods }
|
||||
}
|
||||
};
|
||||
@ -91,13 +91,14 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
var easierMods = osu.GetModsFor(ModType.DifficultyReduction);
|
||||
var harderMods = osu.GetModsFor(ModType.DifficultyIncrease);
|
||||
var conversionMods = osu.GetModsFor(ModType.Conversion);
|
||||
|
||||
var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail);
|
||||
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
|
||||
|
||||
var doubleTimeMod = harderMods.OfType<MultiMod>().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
|
||||
|
||||
var spunOutMod = easierMods.FirstOrDefault(m => m is OsuModSpunOut);
|
||||
var targetMod = conversionMods.FirstOrDefault(m => m is OsuModTarget);
|
||||
|
||||
var easy = easierMods.FirstOrDefault(m => m is OsuModEasy);
|
||||
var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock);
|
||||
@ -109,7 +110,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
testMultiplierTextColour(noFailMod, () => modSelect.LowMultiplierColour);
|
||||
testMultiplierTextColour(hiddenMod, () => modSelect.HighMultiplierColour);
|
||||
|
||||
testUnimplementedMod(spunOutMod);
|
||||
testUnimplementedMod(targetMod);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
Reference in New Issue
Block a user