1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 20:22:55 +08:00

Merge branch 'master' into refactor-osu-difficulty-hit-object

This commit is contained in:
Dan Balasescu 2021-12-07 21:05:49 +09:00
commit 7c3d1d630e
340 changed files with 5386 additions and 3623 deletions

View File

@ -38,7 +38,7 @@ jobs:
run: dotnet build -c Debug -warnaserror osu.Desktop.slnf run: dotnet build -c Debug -warnaserror osu.Desktop.slnf
- name: Test - name: Test
run: dotnet test $pwd/*.Tests/bin/Debug/*/*.Tests.dll --blame-crash --blame-hang --blame-hang-timeout 5m --logger "trx;LogFileName=TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx" run: dotnet test $pwd/*.Tests/bin/Debug/*/*.Tests.dll --logger "trx;LogFileName=TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx"
shell: pwsh shell: pwsh
# Attempt to upload results even if test fails. # Attempt to upload results even if test fails.
@ -48,7 +48,7 @@ jobs:
if: ${{ always() }} if: ${{ always() }}
with: with:
name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}} name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
path: ${{github.workspace}}/TestResults/**/* path: ${{github.workspace}}/TestResults/TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx
build-only-android: build-only-android:
name: Build only (Android) name: Build only (Android)
@ -77,10 +77,6 @@ jobs:
run: msbuild osu.Android/osu.Android.csproj /restore /p:Configuration=Debug run: msbuild osu.Android/osu.Android.csproj /restore /p:Configuration=Debug
build-only-ios: build-only-ios:
# While this workflow technically *can* run, it fails as iOS builds are blocked by multiple issues.
# See https://github.com/ppy/osu-framework/issues/4677 for the details.
# The job can be unblocked once those issues are resolved and game deployments can happen again.
if: false
name: Build only (iOS) name: Build only (iOS)
runs-on: macos-latest runs-on: macos-latest
timeout-minutes: 60 timeout-minutes: 60

View File

@ -10,3 +10,6 @@ T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal
T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods. T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods.
T:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods. T:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods.
M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast<T>() instead. M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast<T>() instead.
M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IRealmCollection<T>,NotificationCallbackDelegate<T>) instead.
M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable<T>,NotificationCallbackDelegate<T>) instead.
M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IList<T>,NotificationCallbackDelegate<T>) instead.

View File

@ -51,8 +51,8 @@
<Reference Include="Java.Interop" /> <Reference Include="Java.Interop" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1112.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2021.1203.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1119.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2021.1207.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Transitive Dependencies"> <ItemGroup Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. --> <!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->

View File

@ -108,7 +108,10 @@ namespace osu.Desktop
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.GlobalRank > 0 ? $" (rank #{user.Value.Statistics.GlobalRank:N0})" : string.Empty); presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.GlobalRank > 0 ? $" (rank #{user.Value.Statistics.GlobalRank:N0})" : string.Empty);
// update ruleset // update ruleset
presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom"; int onlineID = ruleset.Value.OnlineID;
bool isLegacyRuleset = onlineID >= 0 && onlineID <= ILegacyRuleset.MAX_LEGACY_RULESET_ID;
presence.Assets.SmallImageKey = isLegacyRuleset ? $"mode_{onlineID}" : "mode_custom";
presence.Assets.SmallImageText = ruleset.Value.Name; presence.Assets.SmallImageText = ruleset.Value.Name;
client.SetPresence(presence); client.SetPresence(presence);

View File

@ -0,0 +1,18 @@
// 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.
namespace osu.Desktop.LegacyIpc
{
/// <summary>
/// A difficulty calculation request from the legacy client.
/// </summary>
/// <remarks>
/// Synchronise any changes with osu!stable.
/// </remarks>
public class LegacyIpcDifficultyCalculationRequest
{
public string BeatmapFile { get; set; }
public int RulesetId { get; set; }
public int Mods { get; set; }
}
}

View File

@ -0,0 +1,16 @@
// 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.
namespace osu.Desktop.LegacyIpc
{
/// <summary>
/// A difficulty calculation response returned to the legacy client.
/// </summary>
/// <remarks>
/// Synchronise any changes with osu!stable.
/// </remarks>
public class LegacyIpcDifficultyCalculationResponse
{
public double StarRating { get; set; }
}
}

View File

@ -0,0 +1,53 @@
// 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 osu.Framework.Platform;
using Newtonsoft.Json.Linq;
namespace osu.Desktop.LegacyIpc
{
/// <summary>
/// An <see cref="IpcMessage"/> that can be used to communicate to and from legacy clients.
/// <para>
/// In order to deserialise types at either end, types must be serialised as their <see cref="System.Type.AssemblyQualifiedName"/>,
/// however this cannot be done since osu!stable and osu!lazer live in two different assemblies.
/// <br />
/// To get around this, this class exists which serialises a payload (<see cref="LegacyIpcMessage.Data"/>) as an <see cref="System.Object"/> type,
/// which can be deserialised at either end because it is part of the core library (mscorlib / System.Private.CorLib).
/// The payload contains the data to be sent over the IPC channel.
/// <br />
/// At either end, Json.NET deserialises the payload into a <see cref="JObject"/> which is manually converted back into the expected <see cref="LegacyIpcMessage.Data"/> type,
/// which then further contains another <see cref="JObject"/> representing the data sent over the IPC channel whose type can likewise be lazily matched through
/// <see cref="LegacyIpcMessage.Data.MessageType"/>.
/// </para>
/// </summary>
/// <remarks>
/// Synchronise any changes with osu-stable.
/// </remarks>
public class LegacyIpcMessage : IpcMessage
{
public LegacyIpcMessage()
{
// Types/assemblies are not inter-compatible, so always serialise/deserialise into objects.
base.Type = typeof(object).FullName;
}
public new string Type => base.Type; // Hide setter.
public new object Value
{
get => base.Value;
set => base.Value = new Data
{
MessageType = value.GetType().Name,
MessageData = value
};
}
public class Data
{
public string MessageType { get; set; }
public object MessageData { get; set; }
}
}
}

View File

@ -0,0 +1,121 @@
// 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;
using System.Linq;
using Newtonsoft.Json.Linq;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
#nullable enable
namespace osu.Desktop.LegacyIpc
{
/// <summary>
/// Provides IPC to legacy osu! clients.
/// </summary>
public class LegacyTcpIpcProvider : TcpIpcProvider
{
private static readonly Logger logger = Logger.GetLogger("legacy-ipc");
public LegacyTcpIpcProvider()
: base(45357)
{
MessageReceived += msg =>
{
try
{
logger.Add("Processing legacy IPC message...");
logger.Add($" {msg.Value}", LogLevel.Debug);
// See explanation in LegacyIpcMessage for why this is done this way.
var legacyData = ((JObject)msg.Value).ToObject<LegacyIpcMessage.Data>();
object value = parseObject((JObject)legacyData!.MessageData, legacyData.MessageType);
return new LegacyIpcMessage
{
Value = onLegacyIpcMessageReceived(value)
};
}
catch (Exception ex)
{
logger.Add($"Processing IPC message failed: {msg.Value}", exception: ex);
return null;
}
};
}
private object parseObject(JObject value, string type)
{
switch (type)
{
case nameof(LegacyIpcDifficultyCalculationRequest):
return value.ToObject<LegacyIpcDifficultyCalculationRequest>()
?? throw new InvalidOperationException($"Failed to parse request {value}");
case nameof(LegacyIpcDifficultyCalculationResponse):
return value.ToObject<LegacyIpcDifficultyCalculationResponse>()
?? throw new InvalidOperationException($"Failed to parse request {value}");
default:
throw new ArgumentException($"Unsupported object type {type}");
}
}
private object onLegacyIpcMessageReceived(object message)
{
switch (message)
{
case LegacyIpcDifficultyCalculationRequest req:
try
{
var ruleset = getLegacyRulesetFromID(req.RulesetId);
Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)req.Mods).ToArray();
WorkingBeatmap beatmap = new FlatFileWorkingBeatmap(req.BeatmapFile, _ => ruleset);
return new LegacyIpcDifficultyCalculationResponse
{
StarRating = ruleset.CreateDifficultyCalculator(beatmap).Calculate(mods).StarRating
};
}
catch
{
return new LegacyIpcDifficultyCalculationResponse();
}
default:
throw new ArgumentException($"Unsupported message type {message}");
}
}
private static Ruleset getLegacyRulesetFromID(int rulesetId)
{
switch (rulesetId)
{
case 0:
return new OsuRuleset();
case 1:
return new TaikoRuleset();
case 2:
return new CatchRuleset();
case 3:
return new ManiaRuleset();
default:
throw new ArgumentException("Invalid ruleset id");
}
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Desktop.LegacyIpc;
using osu.Framework; using osu.Framework;
using osu.Framework.Development; using osu.Framework.Development;
using osu.Framework.Logging; using osu.Framework.Logging;
@ -18,8 +19,10 @@ namespace osu.Desktop
{ {
private const string base_game_name = @"osu"; private const string base_game_name = @"osu";
private static LegacyTcpIpcProvider legacyIpc;
[STAThread] [STAThread]
public static int Main(string[] args) public static void Main(string[] args)
{ {
// Back up the cwd before DesktopGameHost changes it // Back up the cwd before DesktopGameHost changes it
string cwd = Environment.CurrentDirectory; string cwd = Environment.CurrentDirectory;
@ -69,14 +72,29 @@ namespace osu.Desktop
throw new TimeoutException(@"IPC took too long to send"); throw new TimeoutException(@"IPC took too long to send");
} }
return 0; return;
} }
// we want to allow multiple instances to be started when in debug. // we want to allow multiple instances to be started when in debug.
if (!DebugUtils.IsDebugBuild) if (!DebugUtils.IsDebugBuild)
{ {
Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error); Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error);
return 0; return;
}
}
if (host.IsPrimaryInstance)
{
try
{
Logger.Log("Starting legacy IPC provider...");
legacyIpc = new LegacyTcpIpcProvider();
legacyIpc.Bind();
legacyIpc.StartAsync();
}
catch (Exception ex)
{
Logger.Error(ex, "Failed to start legacy IPC provider");
} }
} }
@ -84,8 +102,6 @@ namespace osu.Desktop
host.Run(new TournamentGame()); host.Run(new TournamentGame());
else else
host.Run(new OsuGameDesktop(args)); host.Run(new OsuGameDesktop(args));
return 0;
} }
} }

View File

@ -103,7 +103,10 @@ namespace osu.Desktop.Updater
} }
else else
{ {
// In the case of an error, a separate notification will be displayed.
notification.State = ProgressNotificationState.Cancelled; notification.State = ProgressNotificationState.Cancelled;
notification.Close();
Logger.Error(e, @"update failed!"); Logger.Error(e, @"update failed!");
} }
} }

View File

@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Catch.Tests
private Drawable setupSkinHierarchy(Drawable child, ISkin skin) private Drawable setupSkinHierarchy(Drawable child, ISkin skin)
{ {
var legacySkinProvider = new SkinProvidingContainer(skins.GetSkin(DefaultLegacySkin.Info)); var legacySkinProvider = new SkinProvidingContainer(skins.GetSkin(DefaultLegacySkin.CreateInfo()));
var testSkinProvider = new SkinProvidingContainer(skin); var testSkinProvider = new SkinProvidingContainer(skin);
var legacySkinTransformer = new SkinProvidingContainer(new CatchLegacySkinTransformer(testSkinProvider)); var legacySkinTransformer = new SkinProvidingContainer(new CatchLegacySkinTransformer(testSkinProvider));

View File

@ -8,6 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
@ -55,13 +56,13 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
[Test] [Test]
public void TestDefaultSkin() public void TestDefaultSkin()
{ {
AddStep("set default skin", () => skins.CurrentSkinInfo.Value = SkinInfo.Default); AddStep("set default skin", () => skins.CurrentSkinInfo.Value = DefaultSkin.CreateInfo().ToLive());
} }
[Test] [Test]
public void TestLegacySkin() public void TestLegacySkin()
{ {
AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.Info); AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.CreateInfo().ToLive());
} }
} }
} }

View File

@ -9,7 +9,6 @@ using osu.Framework.Bindables;
using osu.Framework.Caching; using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
@ -46,12 +45,6 @@ namespace osu.Game.Rulesets.Mania.Edit
[Resolved] [Resolved]
private EditorBeatmap beatmap { get; set; } private EditorBeatmap beatmap { get; set; }
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }
[Resolved]
private Bindable<WorkingBeatmap> working { get; set; }
[Resolved] [Resolved]
private OsuColour colours { get; set; } private OsuColour colours { get; set; }

View File

@ -7,16 +7,12 @@ using osu.Framework.Allocation;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components;
namespace osu.Game.Rulesets.Mania.Edit namespace osu.Game.Rulesets.Mania.Edit
{ {
public class ManiaSelectionHandler : EditorSelectionHandler public class ManiaSelectionHandler : EditorSelectionHandler
{ {
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }
[Resolved] [Resolved]
private HitObjectComposer composer { get; set; } private HitObjectComposer composer { get; set; }

View File

@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.Mania.UI
public JudgementResult Result { get; private set; } public JudgementResult Result { get; private set; }
[Resolved]
private Column column { get; set; }
private SkinnableDrawable skinnableExplosion; private SkinnableDrawable skinnableExplosion;
public PoolableHitExplosion() public PoolableHitExplosion()

View File

@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep("create slider", () => AddStep("create slider", () =>
{ {
var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.Info); var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo());
tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1"; tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1";
Child = new SkinProvidingContainer(tintingSkin) Child = new SkinProvidingContainer(tintingSkin)

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System; using System;
@ -26,12 +26,12 @@ namespace osu.Game.Rulesets.Osu.Mods
public BindableFloat Scale { get; } = new BindableFloat(4) public BindableFloat Scale { get; } = new BindableFloat(4)
{ {
Precision = 0.1f, Precision = 0.1f,
MinValue = 2, MinValue = 1.5f,
MaxValue = 10, MaxValue = 10,
}; };
[SettingSource("Style", "Change the animation style of the approach circles.", 1)] [SettingSource("Style", "Change the animation style of the approach circles.", 1)]
public Bindable<AnimationStyle> Style { get; } = new Bindable<AnimationStyle>(); public Bindable<AnimationStyle> Style { get; } = new Bindable<AnimationStyle>(AnimationStyle.Gravity);
public void ApplyToDrawableHitObject(DrawableHitObject drawable) public void ApplyToDrawableHitObject(DrawableHitObject drawable)
{ {
@ -52,9 +52,18 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
switch (style) switch (style)
{ {
default: case AnimationStyle.Linear:
return Easing.None; return Easing.None;
case AnimationStyle.Gravity:
return Easing.InBack;
case AnimationStyle.InOut1:
return Easing.InOutCubic;
case AnimationStyle.InOut2:
return Easing.InOutQuint;
case AnimationStyle.Accelerate1: case AnimationStyle.Accelerate1:
return Easing.In; return Easing.In;
@ -64,9 +73,6 @@ namespace osu.Game.Rulesets.Osu.Mods
case AnimationStyle.Accelerate3: case AnimationStyle.Accelerate3:
return Easing.InQuint; return Easing.InQuint;
case AnimationStyle.Gravity:
return Easing.InBack;
case AnimationStyle.Decelerate1: case AnimationStyle.Decelerate1:
return Easing.Out; return Easing.Out;
@ -76,16 +82,14 @@ namespace osu.Game.Rulesets.Osu.Mods
case AnimationStyle.Decelerate3: case AnimationStyle.Decelerate3:
return Easing.OutQuint; return Easing.OutQuint;
case AnimationStyle.InOut1: default:
return Easing.InOutCubic; throw new ArgumentOutOfRangeException(nameof(style), style, @"Unsupported animation style");
case AnimationStyle.InOut2:
return Easing.InOutQuint;
} }
} }
public enum AnimationStyle public enum AnimationStyle
{ {
Linear,
Gravity, Gravity,
InOut1, InOut1,
InOut2, InOut2,

View File

@ -12,7 +12,6 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
@ -149,9 +148,6 @@ namespace osu.Game.Rulesets.Taiko.UI
centreHit.Colour = colours.Pink; centreHit.Colour = colours.Pink;
} }
[Resolved(canBeNull: true)]
private GameplayClock gameplayClock { get; set; }
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e) public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{ {
Drawable target = null; Drawable target = null;

View File

@ -2,14 +2,20 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Taiko;
using osu.Game.Scoring; using osu.Game.Scoring;
@ -21,6 +27,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
[TestFixture] [TestFixture]
public class LegacyScoreDecoderTest public class LegacyScoreDecoderTest
{ {
private CultureInfo originalCulture;
[SetUp]
public void SetUp()
{
originalCulture = CultureInfo.CurrentCulture;
}
[Test] [Test]
public void TestDecodeManiaReplay() public void TestDecodeManiaReplay()
{ {
@ -30,7 +44,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
{ {
var score = decoder.Parse(resourceStream); var score = decoder.Parse(resourceStream);
Assert.AreEqual(3, score.ScoreInfo.Ruleset.ID); Assert.AreEqual(3, score.ScoreInfo.Ruleset.OnlineID);
Assert.AreEqual(2, score.ScoreInfo.Statistics[HitResult.Great]); Assert.AreEqual(2, score.ScoreInfo.Statistics[HitResult.Great]);
Assert.AreEqual(1, score.ScoreInfo.Statistics[HitResult.Good]); Assert.AreEqual(1, score.ScoreInfo.Statistics[HitResult.Good]);
@ -44,6 +58,59 @@ namespace osu.Game.Tests.Beatmaps.Formats
} }
} }
[Test]
public void TestCultureInvariance()
{
var ruleset = new OsuRuleset().RulesetInfo;
var scoreInfo = new TestScoreInfo(ruleset);
var beatmap = new TestBeatmap(ruleset);
var score = new Score
{
ScoreInfo = scoreInfo,
Replay = new Replay
{
Frames = new List<ReplayFrame>
{
new OsuReplayFrame(2000, OsuPlayfield.BASE_SIZE / 2, OsuAction.LeftButton)
}
}
};
// the "se" culture is used here, as it encodes the negative number sign as U+2212 MINUS SIGN,
// rather than the classic ASCII U+002D HYPHEN-MINUS.
CultureInfo.CurrentCulture = new CultureInfo("se");
var encodeStream = new MemoryStream();
var encoder = new LegacyScoreEncoder(score, beatmap);
encoder.Encode(encodeStream);
var decodeStream = new MemoryStream(encodeStream.GetBuffer());
var decoder = new TestLegacyScoreDecoder();
var decodedAfterEncode = decoder.Parse(decodeStream);
Assert.Multiple(() =>
{
Assert.That(decodedAfterEncode, Is.Not.Null);
Assert.That(decodedAfterEncode.ScoreInfo.User.Username, Is.EqualTo(scoreInfo.User.Username));
Assert.That(decodedAfterEncode.ScoreInfo.BeatmapInfoID, Is.EqualTo(scoreInfo.BeatmapInfoID));
Assert.That(decodedAfterEncode.ScoreInfo.Ruleset, Is.EqualTo(scoreInfo.Ruleset));
Assert.That(decodedAfterEncode.ScoreInfo.TotalScore, Is.EqualTo(scoreInfo.TotalScore));
Assert.That(decodedAfterEncode.ScoreInfo.MaxCombo, Is.EqualTo(scoreInfo.MaxCombo));
Assert.That(decodedAfterEncode.ScoreInfo.Date, Is.EqualTo(scoreInfo.Date));
Assert.That(decodedAfterEncode.Replay.Frames.Count, Is.EqualTo(1));
});
}
[TearDown]
public void TearDown()
{
CultureInfo.CurrentCulture = originalCulture;
}
private class TestLegacyScoreDecoder : LegacyScoreDecoder private class TestLegacyScoreDecoder : LegacyScoreDecoder
{ {
private static readonly Dictionary<int, Ruleset> rulesets = new Ruleset[] private static readonly Dictionary<int, Ruleset> rulesets = new Ruleset[]

View File

@ -584,7 +584,7 @@ namespace osu.Game.Tests.Beatmaps.IO
{ {
OnlineID = 1, OnlineID = 1,
Metadata = metadata, Metadata = metadata,
Beatmaps = new List<BeatmapInfo> Beatmaps =
{ {
new BeatmapInfo new BeatmapInfo
{ {
@ -596,7 +596,7 @@ namespace osu.Game.Tests.Beatmaps.IO
{ {
OnlineID = 2, OnlineID = 2,
Metadata = metadata, Metadata = metadata,
Status = BeatmapSetOnlineStatus.Loved, Status = BeatmapOnlineStatus.Loved,
BaseDifficulty = difficulty BaseDifficulty = difficulty
} }
} }
@ -1050,7 +1050,7 @@ namespace osu.Game.Tests.Beatmaps.IO
private static void checkSingleReferencedFileCount(OsuGameBase osu, int expected) private static void checkSingleReferencedFileCount(OsuGameBase osu, int expected)
{ {
Assert.AreEqual(expected, osu.Dependencies.Get<FileStore>().QueryFiles(f => f.ReferenceCount == 1).Count()); Assert.AreEqual(expected, osu.Dependencies.Get<DatabaseContextFactory>().Get().FileInfo.Count(f => f.ReferenceCount == 1));
} }
private static void ensureLoaded(OsuGameBase osu, int timeout = 60000) private static void ensureLoaded(OsuGameBase osu, int timeout = 60000)

View File

@ -25,9 +25,6 @@ namespace osu.Game.Tests.Beatmaps
private BeatmapSetInfo importedSet; private BeatmapSetInfo importedSet;
[Resolved]
private BeatmapManager beatmaps { get; set; }
private TestBeatmapDifficultyCache difficultyCache; private TestBeatmapDifficultyCache difficultyCache;
private IBindable<StarDifficulty?> starDifficultyBindable; private IBindable<StarDifficulty?> starDifficultyBindable;
@ -100,8 +97,8 @@ namespace osu.Game.Tests.Beatmaps
[Test] [Test]
public void TestKeyEqualsWithDifferentModInstances() public void TestKeyEqualsWithDifferentModInstances()
{ {
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
Assert.That(key1, Is.EqualTo(key2)); Assert.That(key1, Is.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode())); Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
@ -110,8 +107,8 @@ namespace osu.Game.Tests.Beatmaps
[Test] [Test]
public void TestKeyEqualsWithDifferentModOrder() public void TestKeyEqualsWithDifferentModOrder()
{ {
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() }); var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
Assert.That(key1, Is.EqualTo(key2)); Assert.That(key1, Is.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode())); Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
@ -120,8 +117,8 @@ namespace osu.Game.Tests.Beatmaps
[Test] [Test]
public void TestKeyDoesntEqualWithDifferentModSettings() public void TestKeyDoesntEqualWithDifferentModSettings()
{ {
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } }); var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } }); var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } });
Assert.That(key1, Is.Not.EqualTo(key2)); Assert.That(key1, Is.Not.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.Not.EqualTo(key2.GetHashCode())); Assert.That(key1.GetHashCode(), Is.Not.EqualTo(key2.GetHashCode()));
@ -130,8 +127,8 @@ namespace osu.Game.Tests.Beatmaps
[Test] [Test]
public void TestKeyEqualWithMatchingModSettings() public void TestKeyEqualWithMatchingModSettings()
{ {
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } }); var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } }); var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
Assert.That(key1, Is.EqualTo(key2)); Assert.That(key1, Is.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode())); Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));

View File

@ -12,6 +12,7 @@ using NUnit.Framework;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Extensions; using osu.Game.Extensions;
@ -474,7 +475,7 @@ namespace osu.Game.Tests.Database
} }
[Test] [Test]
public void TestImportThenDeleteThenImport() public void TestImportThenDeleteThenImportOptimisedPath()
{ {
RunTestWithRealmAsync(async (realmFactory, storage) => RunTestWithRealmAsync(async (realmFactory, storage) =>
{ {
@ -485,11 +486,39 @@ namespace osu.Game.Tests.Database
deleteBeatmapSet(imported, realmFactory.Context); deleteBeatmapSet(imported, realmFactory.Context);
Assert.IsTrue(imported.DeletePending);
var importedSecondTime = await LoadOszIntoStore(importer, realmFactory.Context); var importedSecondTime = await LoadOszIntoStore(importer, realmFactory.Context);
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
Assert.IsTrue(imported.ID == importedSecondTime.ID); Assert.IsTrue(imported.ID == importedSecondTime.ID);
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
Assert.IsFalse(imported.DeletePending);
Assert.IsFalse(importedSecondTime.DeletePending);
});
}
[Test]
public void TestImportThenDeleteThenImportNonOptimisedPath()
{
RunTestWithRealmAsync(async (realmFactory, storage) =>
{
using var importer = new NonOptimisedBeatmapImporter(realmFactory, storage);
using var store = new RealmRulesetStore(realmFactory, storage);
var imported = await LoadOszIntoStore(importer, realmFactory.Context);
deleteBeatmapSet(imported, realmFactory.Context);
Assert.IsTrue(imported.DeletePending);
var importedSecondTime = await LoadOszIntoStore(importer, realmFactory.Context);
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
Assert.IsTrue(imported.ID == importedSecondTime.ID);
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
Assert.IsFalse(imported.DeletePending);
Assert.IsFalse(importedSecondTime.DeletePending);
}); });
} }
@ -550,7 +579,7 @@ namespace osu.Game.Tests.Database
new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), metadata) new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), metadata)
{ {
OnlineID = 2, OnlineID = 2,
Status = BeatmapSetOnlineStatus.Loved, Status = BeatmapOnlineStatus.Loved,
} }
} }
}; };
@ -823,7 +852,11 @@ namespace osu.Game.Tests.Database
{ {
IQueryable<RealmBeatmapSet>? resultSets = null; IQueryable<RealmBeatmapSet>? resultSets = null;
waitForOrAssert(() => (resultSets = realm.All<RealmBeatmapSet>().Where(s => !s.DeletePending && s.OnlineID == 241526)).Any(), waitForOrAssert(() =>
{
realm.Refresh();
return (resultSets = realm.All<RealmBeatmapSet>().Where(s => !s.DeletePending && s.OnlineID == 241526)).Any();
},
@"BeatmapSet did not import to the database in allocated time.", timeout); @"BeatmapSet did not import to the database in allocated time.", timeout);
// ensure we were stored to beatmap database backing... // ensure we were stored to beatmap database backing...
@ -836,16 +869,16 @@ namespace osu.Game.Tests.Database
// ReSharper disable once PossibleUnintendedReferenceComparison // ReSharper disable once PossibleUnintendedReferenceComparison
IEnumerable<RealmBeatmap> queryBeatmaps() => realm.All<RealmBeatmap>().Where(s => s.BeatmapSet != null && s.BeatmapSet == set); IEnumerable<RealmBeatmap> queryBeatmaps() => realm.All<RealmBeatmap>().Where(s => s.BeatmapSet != null && s.BeatmapSet == set);
waitForOrAssert(() => queryBeatmaps().Count() == 12, @"Beatmaps did not import to the database in allocated time", timeout); Assert.AreEqual(12, queryBeatmaps().Count(), @"Beatmap count was not correct");
waitForOrAssert(() => queryBeatmapSets().Count() == 1, @"BeatmapSet did not import to the database in allocated time", timeout); Assert.AreEqual(1, queryBeatmapSets().Count(), @"Beatmapset count was not correct");
int countBeatmapSetBeatmaps = 0; int countBeatmapSetBeatmaps;
int countBeatmaps = 0; int countBeatmaps;
waitForOrAssert(() => Assert.AreEqual(
(countBeatmapSetBeatmaps = queryBeatmapSets().First().Beatmaps.Count) == countBeatmapSetBeatmaps = queryBeatmapSets().First().Beatmaps.Count,
(countBeatmaps = queryBeatmaps().Count()), countBeatmaps = queryBeatmaps().Count(),
$@"Incorrect database beatmap count post-import ({countBeatmaps} but should be {countBeatmapSetBeatmaps}).", timeout); $@"Incorrect database beatmap count post-import ({countBeatmaps} but should be {countBeatmapSetBeatmaps}).");
foreach (RealmBeatmap b in set.Beatmaps) foreach (RealmBeatmap b in set.Beatmaps)
Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineID == b.OnlineID)); Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineID == b.OnlineID));
@ -867,5 +900,15 @@ namespace osu.Game.Tests.Database
Assert.Fail(failureMessage); Assert.Fail(failureMessage);
} }
public class NonOptimisedBeatmapImporter : BeatmapImporter
{
public NonOptimisedBeatmapImporter(RealmContextFactory realmFactory, Storage storage)
: base(realmFactory, storage)
{
}
protected override bool HasCustomHashFunction => true;
}
} }
} }

View File

@ -5,6 +5,8 @@ using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Database;
using osu.Game.Models;
#nullable enable #nullable enable
@ -33,6 +35,39 @@ namespace osu.Game.Tests.Database
}); });
} }
/// <summary>
/// Test to ensure that a `CreateContext` call nested inside a subscription doesn't cause any deadlocks
/// due to context fetching semaphores.
/// </summary>
[Test]
public void TestNestedContextCreationWithSubscription()
{
RunTestWithRealm((realmFactory, _) =>
{
bool callbackRan = false;
using (var context = realmFactory.CreateContext())
{
var subscription = context.All<RealmBeatmap>().QueryAsyncWithNotifications((sender, changes, error) =>
{
using (realmFactory.CreateContext())
{
callbackRan = true;
}
});
// Force the callback above to run.
using (realmFactory.CreateContext())
{
}
subscription?.Dispose();
}
Assert.IsTrue(callbackRan);
});
}
[Test] [Test]
public void TestBlockOperationsWithContention() public void TestBlockOperationsWithContention()
{ {

View File

@ -6,7 +6,6 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Models; using osu.Game.Models;
using Realms; using Realms;
@ -18,61 +17,57 @@ namespace osu.Game.Tests.Database
public class RealmLiveTests : RealmTest public class RealmLiveTests : RealmTest
{ {
[Test] [Test]
public void TestLiveCastability() public void TestLiveEquality()
{ {
RunTestWithRealm((realmFactory, _) => RunTestWithRealm((realmFactory, _) =>
{ {
RealmLive<RealmBeatmap> beatmap = realmFactory.CreateContext().Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))).ToLive(); ILive<RealmBeatmap> beatmap = realmFactory.CreateContext().Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))).ToLive();
ILive<IBeatmapInfo> iBeatmap = beatmap; ILive<RealmBeatmap> beatmap2 = realmFactory.CreateContext().All<RealmBeatmap>().First().ToLive();
Assert.AreEqual(0, iBeatmap.Value.Length); Assert.AreEqual(beatmap, beatmap2);
}); });
} }
[Test] [Test]
public void TestValueAccessWithOpenContext() public void TestAccessAfterAttach()
{ {
RunTestWithRealm((realmFactory, _) => RunTestWithRealm((realmFactory, _) =>
{ {
RealmLive<RealmBeatmap>? liveBeatmap = null; var beatmap = new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata());
Task.Factory.StartNew(() =>
{
using (var threadContext = realmFactory.CreateContext())
{
var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata())));
liveBeatmap = beatmap.ToLive(); var liveBeatmap = beatmap.ToLive();
}
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait();
Debug.Assert(liveBeatmap != null); using (var context = realmFactory.CreateContext())
context.Write(r => r.Add(beatmap));
Task.Factory.StartNew(() => Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
{
Assert.DoesNotThrow(() =>
{
using (realmFactory.CreateContext())
{
var resolved = liveBeatmap.Value;
Assert.IsTrue(resolved.Realm.IsClosed);
Assert.IsTrue(resolved.IsValid);
// can access properties without a crash.
Assert.IsFalse(resolved.Hidden);
}
});
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait();
}); });
} }
[Test]
public void TestAccessNonManaged()
{
var beatmap = new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata());
var liveBeatmap = beatmap.ToLive();
Assert.IsFalse(beatmap.Hidden);
Assert.IsFalse(liveBeatmap.Value.Hidden);
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
Assert.Throws<InvalidOperationException>(() => liveBeatmap.PerformWrite(l => l.Hidden = true));
Assert.IsFalse(beatmap.Hidden);
Assert.IsFalse(liveBeatmap.Value.Hidden);
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
}
[Test] [Test]
public void TestScopedReadWithoutContext() public void TestScopedReadWithoutContext()
{ {
RunTestWithRealm((realmFactory, _) => RunTestWithRealm((realmFactory, _) =>
{ {
RealmLive<RealmBeatmap>? liveBeatmap = null; ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() => Task.Factory.StartNew(() =>
{ {
using (var threadContext = realmFactory.CreateContext()) using (var threadContext = realmFactory.CreateContext())
@ -101,7 +96,7 @@ namespace osu.Game.Tests.Database
{ {
RunTestWithRealm((realmFactory, _) => RunTestWithRealm((realmFactory, _) =>
{ {
RealmLive<RealmBeatmap>? liveBeatmap = null; ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() => Task.Factory.StartNew(() =>
{ {
using (var threadContext = realmFactory.CreateContext()) using (var threadContext = realmFactory.CreateContext())
@ -122,12 +117,66 @@ namespace osu.Game.Tests.Database
}); });
} }
[Test]
public void TestValueAccessNonManaged()
{
RunTestWithRealm((realmFactory, _) =>
{
var beatmap = new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata());
var liveBeatmap = beatmap.ToLive();
Assert.DoesNotThrow(() =>
{
var __ = liveBeatmap.Value;
});
});
}
[Test]
public void TestValueAccessWithOpenContextFails()
{
RunTestWithRealm((realmFactory, _) =>
{
ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() =>
{
using (var threadContext = realmFactory.CreateContext())
{
var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata())));
liveBeatmap = beatmap.ToLive();
}
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait();
Debug.Assert(liveBeatmap != null);
Task.Factory.StartNew(() =>
{
// Can't be used, without a valid context.
Assert.Throws<InvalidOperationException>(() =>
{
var __ = liveBeatmap.Value;
});
// Can't be used, even from within a valid context.
using (realmFactory.CreateContext())
{
Assert.Throws<InvalidOperationException>(() =>
{
var __ = liveBeatmap.Value;
});
}
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait();
});
}
[Test] [Test]
public void TestValueAccessWithoutOpenContextFails() public void TestValueAccessWithoutOpenContextFails()
{ {
RunTestWithRealm((realmFactory, _) => RunTestWithRealm((realmFactory, _) =>
{ {
RealmLive<RealmBeatmap>? liveBeatmap = null; ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() => Task.Factory.StartNew(() =>
{ {
using (var threadContext = realmFactory.CreateContext()) using (var threadContext = realmFactory.CreateContext())
@ -159,8 +208,8 @@ namespace osu.Game.Tests.Database
using (var updateThreadContext = realmFactory.CreateContext()) using (var updateThreadContext = realmFactory.CreateContext())
{ {
updateThreadContext.All<RealmBeatmap>().SubscribeForNotifications(gotChange); updateThreadContext.All<RealmBeatmap>().QueryAsyncWithNotifications(gotChange);
RealmLive<RealmBeatmap>? liveBeatmap = null; ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() => Task.Factory.StartNew(() =>
{ {
@ -183,23 +232,22 @@ namespace osu.Game.Tests.Database
Assert.AreEqual(0, updateThreadContext.All<RealmBeatmap>().Count()); Assert.AreEqual(0, updateThreadContext.All<RealmBeatmap>().Count());
Assert.AreEqual(0, changesTriggered); Assert.AreEqual(0, changesTriggered);
var resolved = liveBeatmap.Value; liveBeatmap.PerformRead(resolved =>
// retrieval causes an implicit refresh. even changes that aren't related to the retrieval are fired at this point.
Assert.AreEqual(2, updateThreadContext.All<RealmBeatmap>().Count());
Assert.AreEqual(1, changesTriggered);
// even though the realm that this instance was resolved for was closed, it's still valid.
Assert.IsTrue(resolved.Realm.IsClosed);
Assert.IsTrue(resolved.IsValid);
// can access properties without a crash.
Assert.IsFalse(resolved.Hidden);
updateThreadContext.Write(r =>
{ {
// can use with the main context. // retrieval causes an implicit refresh. even changes that aren't related to the retrieval are fired at this point.
r.Remove(resolved); // ReSharper disable once AccessToDisposedClosure
Assert.AreEqual(2, updateThreadContext.All<RealmBeatmap>().Count());
Assert.AreEqual(1, changesTriggered);
// can access properties without a crash.
Assert.IsFalse(resolved.Hidden);
// ReSharper disable once AccessToDisposedClosure
updateThreadContext.Write(r =>
{
// can use with the main context.
r.Remove(resolved);
});
}); });
} }

View File

@ -45,9 +45,9 @@ namespace osu.Game.Tests.Database
{ {
var rulesets = new RealmRulesetStore(realmFactory, storage); var rulesets = new RealmRulesetStore(realmFactory, storage);
Assert.IsTrue((rulesets.AvailableRulesets.First() as RealmRuleset)?.IsManaged == false); Assert.IsFalse(rulesets.AvailableRulesets.First().IsManaged);
Assert.IsTrue((rulesets.GetRuleset(0) as RealmRuleset)?.IsManaged == false); Assert.IsFalse(rulesets.GetRuleset(0)?.IsManaged);
Assert.IsTrue((rulesets.GetRuleset("mania") as RealmRuleset)?.IsManaged == false); Assert.IsFalse(rulesets.GetRuleset("mania")?.IsManaged);
}); });
} }
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Moq; using Moq;
@ -13,7 +12,6 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Storyboards; using osu.Game.Storyboards;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
using FileInfo = osu.Game.IO.FileInfo;
namespace osu.Game.Tests.Editing.Checks namespace osu.Game.Tests.Editing.Checks
{ {
@ -33,14 +31,10 @@ namespace osu.Game.Tests.Editing.Checks
{ {
BeatmapSet = new BeatmapSetInfo BeatmapSet = new BeatmapSetInfo
{ {
Files = new List<BeatmapSetFileInfo>(new[] Files =
{ {
new BeatmapSetFileInfo CheckTestHelpers.CreateMockFile("mp4"),
{ }
Filename = "abc123.mp4",
FileInfo = new FileInfo { Hash = "abcdef" }
}
})
} }
} }
}; };

View File

@ -1,7 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
@ -12,7 +12,6 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using FileInfo = osu.Game.IO.FileInfo;
namespace osu.Game.Tests.Editing.Checks namespace osu.Game.Tests.Editing.Checks
{ {
@ -25,25 +24,17 @@ namespace osu.Game.Tests.Editing.Checks
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
var file = CheckTestHelpers.CreateMockFile("jpg");
check = new CheckBackgroundQuality(); check = new CheckBackgroundQuality();
beatmap = new Beatmap<HitObject> beatmap = new Beatmap<HitObject>
{ {
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
Metadata = new BeatmapMetadata { BackgroundFile = "abc123.jpg" }, Metadata = new BeatmapMetadata { BackgroundFile = file.Filename },
BeatmapSet = new BeatmapSetInfo BeatmapSet = new BeatmapSetInfo
{ {
Files = new List<BeatmapSetFileInfo>(new[] Files = { file }
{
new BeatmapSetFileInfo
{
Filename = "abc123.jpg",
FileInfo = new FileInfo
{
Hash = "abcdef"
}
}
})
} }
} }
}; };
@ -54,7 +45,7 @@ namespace osu.Game.Tests.Editing.Checks
{ {
// While this is a problem, it is out of scope for this check and is caught by a different one. // While this is a problem, it is out of scope for this check and is caught by a different one.
beatmap.Metadata.BackgroundFile = string.Empty; beatmap.Metadata.BackgroundFile = string.Empty;
var context = getContext(null, new MemoryStream(System.Array.Empty<byte>())); var context = getContext(null, new MemoryStream(Array.Empty<byte>()));
Assert.That(check.Run(context), Is.Empty); Assert.That(check.Run(context), Is.Empty);
} }

View File

@ -1,11 +1,9 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.IO;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -22,22 +20,17 @@ namespace osu.Game.Tests.Editing.Checks
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
var file = CheckTestHelpers.CreateMockFile("jpg");
check = new CheckBackgroundPresence(); check = new CheckBackgroundPresence();
beatmap = new Beatmap<HitObject> beatmap = new Beatmap<HitObject>
{ {
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
Metadata = new BeatmapMetadata { BackgroundFile = "abc123.jpg" }, Metadata = new BeatmapMetadata { BackgroundFile = file.Filename },
BeatmapSet = new BeatmapSetInfo BeatmapSet = new BeatmapSetInfo
{ {
Files = new List<BeatmapSetFileInfo>(new[] Files = { file }
{
new BeatmapSetFileInfo
{
Filename = "abc123.jpg",
FileInfo = new FileInfo { Hash = "abcdef" }
}
})
} }
} }
}; };

View File

@ -0,0 +1,18 @@
// 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 osu.Game.Beatmaps;
using osu.Game.IO;
namespace osu.Game.Tests.Editing.Checks
{
public static class CheckTestHelpers
{
public static BeatmapSetFileInfo CreateMockFile(string extension) =>
new BeatmapSetFileInfo
{
Filename = $"abc123.{extension}",
FileInfo = new FileInfo { Hash = "abcdef" }
};
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using ManagedBass; using ManagedBass;
@ -14,7 +13,6 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
using osuTK.Audio; using osuTK.Audio;
using FileInfo = osu.Game.IO.FileInfo;
namespace osu.Game.Tests.Editing.Checks namespace osu.Game.Tests.Editing.Checks
{ {
@ -34,14 +32,7 @@ namespace osu.Game.Tests.Editing.Checks
{ {
BeatmapSet = new BeatmapSetInfo BeatmapSet = new BeatmapSetInfo
{ {
Files = new List<BeatmapSetFileInfo>(new[] Files = { CheckTestHelpers.CreateMockFile("wav") }
{
new BeatmapSetFileInfo
{
Filename = "abc123.wav",
FileInfo = new FileInfo { Hash = "abcdef" }
}
})
} }
} }
}; };
@ -55,11 +46,7 @@ namespace osu.Game.Tests.Editing.Checks
public void TestDifferentExtension() public void TestDifferentExtension()
{ {
beatmap.BeatmapInfo.BeatmapSet.Files.Clear(); beatmap.BeatmapInfo.BeatmapSet.Files.Clear();
beatmap.BeatmapInfo.BeatmapSet.Files.Add(new BeatmapSetFileInfo beatmap.BeatmapInfo.BeatmapSet.Files.Add(CheckTestHelpers.CreateMockFile("jpg"));
{
Filename = "abc123.jpg",
FileInfo = new FileInfo { Hash = "abcdef" }
});
// Should fail to load, but not produce an error due to the extension not being expected to load. // Should fail to load, but not produce an error due to the extension not being expected to load.
Assert.IsEmpty(check.Run(getContext(null, allowMissing: true))); Assert.IsEmpty(check.Run(getContext(null, allowMissing: true)));

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Moq; using Moq;
@ -10,7 +9,6 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using FileInfo = osu.Game.IO.FileInfo;
namespace osu.Game.Tests.Editing.Checks namespace osu.Game.Tests.Editing.Checks
{ {
@ -30,14 +28,10 @@ namespace osu.Game.Tests.Editing.Checks
{ {
BeatmapSet = new BeatmapSetInfo BeatmapSet = new BeatmapSetInfo
{ {
Files = new List<BeatmapSetFileInfo>(new[] Files =
{ {
new BeatmapSetFileInfo CheckTestHelpers.CreateMockFile("jpg"),
{ }
Filename = "abc123.jpg",
FileInfo = new FileInfo { Hash = "abcdef" }
}
})
} }
} }
}; };

View File

@ -167,7 +167,7 @@ namespace osu.Game.Tests.Gameplay
private class TestSkin : LegacySkin private class TestSkin : LegacySkin
{ {
public TestSkin(string resourceName, IStorageResourceProvider resources) public TestSkin(string resourceName, IStorageResourceProvider resources)
: base(DefaultLegacySkin.Info, new TestResourceStore(resourceName), resources, "skin.ini") : base(DefaultLegacySkin.CreateInfo(), new TestResourceStore(resourceName), resources, "skin.ini")
{ {
} }
} }

View File

@ -18,9 +18,6 @@ namespace osu.Game.Tests.Input
[Resolved] [Resolved]
private FrameworkConfigManager frameworkConfigManager { get; set; } private FrameworkConfigManager frameworkConfigManager { get; set; }
[Resolved]
private OsuConfigManager osuConfigManager { get; set; }
[TestCase(WindowMode.Windowed)] [TestCase(WindowMode.Windowed)]
[TestCase(WindowMode.Borderless)] [TestCase(WindowMode.Borderless)]
public void TestDisableConfining(WindowMode windowMode) public void TestDisableConfining(WindowMode windowMode)

View File

@ -29,9 +29,9 @@ namespace osu.Game.Tests.Models
{ {
var mock = new Mock<IBeatmapSetInfo>(); var mock = new Mock<IBeatmapSetInfo>();
mock.Setup(m => m.Metadata!.Artist).Returns("artist"); mock.Setup(m => m.Metadata.Artist).Returns("artist");
mock.Setup(m => m.Metadata!.Title).Returns("title"); mock.Setup(m => m.Metadata.Title).Returns("title");
mock.Setup(m => m.Metadata!.Author.Username).Returns("author"); mock.Setup(m => m.Metadata.Author.Username).Returns("author");
Assert.That(mock.Object.GetDisplayString(), Is.EqualTo("artist - title (author)")); Assert.That(mock.Object.GetDisplayString(), Is.EqualTo("artist - title (author)"));
} }
@ -41,9 +41,9 @@ namespace osu.Game.Tests.Models
{ {
var mock = new Mock<IBeatmapSetInfo>(); var mock = new Mock<IBeatmapSetInfo>();
mock.Setup(m => m.Metadata!.Artist).Returns("artist"); mock.Setup(m => m.Metadata.Artist).Returns("artist");
mock.Setup(m => m.Metadata!.Title).Returns("title"); mock.Setup(m => m.Metadata.Title).Returns("title");
mock.Setup(m => m.Metadata!.Author.Username).Returns(string.Empty); mock.Setup(m => m.Metadata.Author.Username).Returns(string.Empty);
Assert.That(mock.Object.GetDisplayString(), Is.EqualTo("artist - title")); Assert.That(mock.Object.GetDisplayString(), Is.EqualTo("artist - title"));
} }

View File

@ -3,12 +3,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Utils;
namespace osu.Game.Tests.NonVisual namespace osu.Game.Tests.NonVisual
{ {
@ -20,8 +22,10 @@ namespace osu.Game.Tests.NonVisual
{ {
var combinations = new TestLegacyDifficultyCalculator().CreateDifficultyAdjustmentModCombinations(); var combinations = new TestLegacyDifficultyCalculator().CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(1, combinations.Length); assertCombinations(new[]
Assert.IsTrue(combinations[0] is ModNoMod); {
new[] { typeof(ModNoMod) }
}, combinations);
} }
[Test] [Test]
@ -29,9 +33,11 @@ namespace osu.Game.Tests.NonVisual
{ {
var combinations = new TestLegacyDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations(); var combinations = new TestLegacyDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(2, combinations.Length); assertCombinations(new[]
Assert.IsTrue(combinations[0] is ModNoMod); {
Assert.IsTrue(combinations[1] is ModA); new[] { typeof(ModNoMod) },
new[] { typeof(ModA) }
}, combinations);
} }
[Test] [Test]
@ -39,14 +45,13 @@ namespace osu.Game.Tests.NonVisual
{ {
var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations(); var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(4, combinations.Length); assertCombinations(new[]
Assert.IsTrue(combinations[0] is ModNoMod); {
Assert.IsTrue(combinations[1] is ModA); new[] { typeof(ModNoMod) },
Assert.IsTrue(combinations[2] is MultiMod); new[] { typeof(ModA) },
Assert.IsTrue(combinations[3] is ModB); new[] { typeof(ModA), typeof(ModB) },
new[] { typeof(ModB) }
Assert.IsTrue(((MultiMod)combinations[2]).Mods[0] is ModA); }, combinations);
Assert.IsTrue(((MultiMod)combinations[2]).Mods[1] is ModB);
} }
[Test] [Test]
@ -54,10 +59,12 @@ namespace osu.Game.Tests.NonVisual
{ {
var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations(); var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(3, combinations.Length); assertCombinations(new[]
Assert.IsTrue(combinations[0] is ModNoMod); {
Assert.IsTrue(combinations[1] is ModA); new[] { typeof(ModNoMod) },
Assert.IsTrue(combinations[2] is ModIncompatibleWithA); new[] { typeof(ModA) },
new[] { typeof(ModIncompatibleWithA) }
}, combinations);
} }
[Test] [Test]
@ -65,22 +72,17 @@ namespace osu.Game.Tests.NonVisual
{ {
var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations(); var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(8, combinations.Length); assertCombinations(new[]
Assert.IsTrue(combinations[0] is ModNoMod); {
Assert.IsTrue(combinations[1] is ModA); new[] { typeof(ModNoMod) },
Assert.IsTrue(combinations[2] is MultiMod); new[] { typeof(ModA) },
Assert.IsTrue(combinations[3] is ModB); new[] { typeof(ModA), typeof(ModB) },
Assert.IsTrue(combinations[4] is MultiMod); new[] { typeof(ModB) },
Assert.IsTrue(combinations[5] is ModIncompatibleWithA); new[] { typeof(ModB), typeof(ModIncompatibleWithA) },
Assert.IsTrue(combinations[6] is MultiMod); new[] { typeof(ModIncompatibleWithA) },
Assert.IsTrue(combinations[7] is ModIncompatibleWithAAndB); new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) },
new[] { typeof(ModIncompatibleWithAAndB) },
Assert.IsTrue(((MultiMod)combinations[2]).Mods[0] is ModA); }, combinations);
Assert.IsTrue(((MultiMod)combinations[2]).Mods[1] is ModB);
Assert.IsTrue(((MultiMod)combinations[4]).Mods[0] is ModB);
Assert.IsTrue(((MultiMod)combinations[4]).Mods[1] is ModIncompatibleWithA);
Assert.IsTrue(((MultiMod)combinations[6]).Mods[0] is ModIncompatibleWithA);
Assert.IsTrue(((MultiMod)combinations[6]).Mods[1] is ModIncompatibleWithAAndB);
} }
[Test] [Test]
@ -88,10 +90,12 @@ namespace osu.Game.Tests.NonVisual
{ {
var combinations = new TestLegacyDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations(); var combinations = new TestLegacyDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(3, combinations.Length); assertCombinations(new[]
Assert.IsTrue(combinations[0] is ModNoMod); {
Assert.IsTrue(combinations[1] is ModAofA); new[] { typeof(ModNoMod) },
Assert.IsTrue(combinations[2] is ModIncompatibleWithAofA); new[] { typeof(ModAofA) },
new[] { typeof(ModIncompatibleWithAofA) }
}, combinations);
} }
[Test] [Test]
@ -99,17 +103,13 @@ namespace osu.Game.Tests.NonVisual
{ {
var combinations = new TestLegacyDifficultyCalculator(new ModA(), new MultiMod(new ModB(), new ModC())).CreateDifficultyAdjustmentModCombinations(); var combinations = new TestLegacyDifficultyCalculator(new ModA(), new MultiMod(new ModB(), new ModC())).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(4, combinations.Length); assertCombinations(new[]
Assert.IsTrue(combinations[0] is ModNoMod); {
Assert.IsTrue(combinations[1] is ModA); new[] { typeof(ModNoMod) },
Assert.IsTrue(combinations[2] is MultiMod); new[] { typeof(ModA) },
Assert.IsTrue(combinations[3] is MultiMod); new[] { typeof(ModA), typeof(ModB), typeof(ModC) },
new[] { typeof(ModB), typeof(ModC) }
Assert.IsTrue(((MultiMod)combinations[2]).Mods[0] is ModA); }, combinations);
Assert.IsTrue(((MultiMod)combinations[2]).Mods[1] is ModB);
Assert.IsTrue(((MultiMod)combinations[2]).Mods[2] is ModC);
Assert.IsTrue(((MultiMod)combinations[3]).Mods[0] is ModB);
Assert.IsTrue(((MultiMod)combinations[3]).Mods[1] is ModC);
} }
[Test] [Test]
@ -117,13 +117,12 @@ namespace osu.Game.Tests.NonVisual
{ {
var combinations = new TestLegacyDifficultyCalculator(new ModA(), new MultiMod(new ModB(), new ModIncompatibleWithA())).CreateDifficultyAdjustmentModCombinations(); var combinations = new TestLegacyDifficultyCalculator(new ModA(), new MultiMod(new ModB(), new ModIncompatibleWithA())).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(3, combinations.Length); assertCombinations(new[]
Assert.IsTrue(combinations[0] is ModNoMod); {
Assert.IsTrue(combinations[1] is ModA); new[] { typeof(ModNoMod) },
Assert.IsTrue(combinations[2] is MultiMod); new[] { typeof(ModA) },
new[] { typeof(ModB), typeof(ModIncompatibleWithA) }
Assert.IsTrue(((MultiMod)combinations[2]).Mods[0] is ModB); }, combinations);
Assert.IsTrue(((MultiMod)combinations[2]).Mods[1] is ModIncompatibleWithA);
} }
[Test] [Test]
@ -131,13 +130,28 @@ namespace osu.Game.Tests.NonVisual
{ {
var combinations = new TestLegacyDifficultyCalculator(new ModA(), new MultiMod(new ModA(), new ModB())).CreateDifficultyAdjustmentModCombinations(); var combinations = new TestLegacyDifficultyCalculator(new ModA(), new MultiMod(new ModA(), new ModB())).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(3, combinations.Length); assertCombinations(new[]
Assert.IsTrue(combinations[0] is ModNoMod); {
Assert.IsTrue(combinations[1] is ModA); new[] { typeof(ModNoMod) },
Assert.IsTrue(combinations[2] is MultiMod); new[] { typeof(ModA) },
new[] { typeof(ModA), typeof(ModB) }
}, combinations);
}
Assert.IsTrue(((MultiMod)combinations[2]).Mods[0] is ModA); private void assertCombinations(Type[][] expectedCombinations, Mod[] actualCombinations)
Assert.IsTrue(((MultiMod)combinations[2]).Mods[1] is ModB); {
Assert.AreEqual(expectedCombinations.Length, actualCombinations.Length);
Assert.Multiple(() =>
{
for (int i = 0; i < expectedCombinations.Length; ++i)
{
Type[] expectedTypes = expectedCombinations[i];
Type[] actualTypes = ModUtils.FlattenMod(actualCombinations[i]).Select(m => m.GetType()).ToArray();
Assert.That(expectedTypes, Is.EquivalentTo(actualTypes));
}
});
} }
private class ModA : Mod private class ModA : Mod

View File

@ -16,7 +16,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
{ {
private BeatmapInfo getExampleBeatmap() => new BeatmapInfo private BeatmapInfo getExampleBeatmap() => new BeatmapInfo
{ {
Ruleset = new RulesetInfo { ID = 5 }, Ruleset = new RulesetInfo { OnlineID = 5 },
StarRating = 4.0d, StarRating = 4.0d,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
@ -38,7 +38,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
Length = 2500, Length = 2500,
BPM = 160, BPM = 160,
BeatDivisor = 12, BeatDivisor = 12,
Status = BeatmapSetOnlineStatus.Loved Status = BeatmapOnlineStatus.Loved
}; };
[Test] [Test]
@ -57,7 +57,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
var exampleBeatmapInfo = getExampleBeatmap(); var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria var criteria = new FilterCriteria
{ {
Ruleset = new RulesetInfo { ID = 6 } Ruleset = new RulesetInfo { OnlineID = 6 }
}; };
var carouselItem = new CarouselBeatmap(exampleBeatmapInfo); var carouselItem = new CarouselBeatmap(exampleBeatmapInfo);
carouselItem.Filter(criteria); carouselItem.Filter(criteria);
@ -70,7 +70,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
var exampleBeatmapInfo = getExampleBeatmap(); var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria var criteria = new FilterCriteria
{ {
Ruleset = new RulesetInfo { ID = 6 }, Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true AllowConvertedBeatmaps = true
}; };
var carouselItem = new CarouselBeatmap(exampleBeatmapInfo); var carouselItem = new CarouselBeatmap(exampleBeatmapInfo);
@ -86,7 +86,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
var exampleBeatmapInfo = getExampleBeatmap(); var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria var criteria = new FilterCriteria
{ {
Ruleset = new RulesetInfo { ID = 6 }, Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true, AllowConvertedBeatmaps = true,
ApproachRate = new FilterCriteria.OptionalRange<float> ApproachRate = new FilterCriteria.OptionalRange<float>
{ {
@ -107,7 +107,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
var exampleBeatmapInfo = getExampleBeatmap(); var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria var criteria = new FilterCriteria
{ {
Ruleset = new RulesetInfo { ID = 6 }, Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true, AllowConvertedBeatmaps = true,
BPM = new FilterCriteria.OptionalRange<double> BPM = new FilterCriteria.OptionalRange<double>
{ {
@ -132,7 +132,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
var exampleBeatmapInfo = getExampleBeatmap(); var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria var criteria = new FilterCriteria
{ {
Ruleset = new RulesetInfo { ID = 6 }, Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true, AllowConvertedBeatmaps = true,
SearchText = terms SearchText = terms
}; };

View File

@ -162,9 +162,9 @@ namespace osu.Game.Tests.NonVisual.Filtering
FilterQueryParser.ApplyQueries(filterCriteria, query); FilterQueryParser.ApplyQueries(filterCriteria, query);
Assert.AreEqual("I want the pp", filterCriteria.SearchText.Trim()); Assert.AreEqual("I want the pp", filterCriteria.SearchText.Trim());
Assert.AreEqual(4, filterCriteria.SearchTerms.Length); Assert.AreEqual(4, filterCriteria.SearchTerms.Length);
Assert.AreEqual(BeatmapSetOnlineStatus.Ranked, filterCriteria.OnlineStatus.Min); Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Min);
Assert.IsTrue(filterCriteria.OnlineStatus.IsLowerInclusive); Assert.IsTrue(filterCriteria.OnlineStatus.IsLowerInclusive);
Assert.AreEqual(BeatmapSetOnlineStatus.Ranked, filterCriteria.OnlineStatus.Max); Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Max);
Assert.IsTrue(filterCriteria.OnlineStatus.IsUpperInclusive); Assert.IsTrue(filterCriteria.OnlineStatus.IsUpperInclusive);
} }

View File

@ -16,6 +16,27 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
[HeadlessTest] [HeadlessTest]
public class StatefulMultiplayerClientTest : MultiplayerTestScene public class StatefulMultiplayerClientTest : MultiplayerTestScene
{ {
[Test]
public void TestUserAddedOnJoin()
{
var user = new APIUser { Id = 33 };
AddRepeatStep("add user multiple times", () => Client.AddUser(user), 3);
AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
}
[Test]
public void TestUserRemovedOnLeave()
{
var user = new APIUser { Id = 44 };
AddStep("add user", () => Client.AddUser(user));
AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
AddRepeatStep("remove user multiple times", () => Client.RemoveUser(user), 3);
AddAssert("room has 1 user", () => Client.Room?.Users.Count == 1);
}
[Test] [Test]
public void TestPlayingUserTracking() public void TestPlayingUserTracking()
{ {

View File

@ -12,9 +12,9 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Tests.Online namespace osu.Game.Tests.Online
{ {
[HeadlessTest] [HeadlessTest]
public class TestSceneBeatmapManager : OsuTestScene public class TestSceneBeatmapDownloading : OsuTestScene
{ {
private BeatmapManager beatmaps; private BeatmapModelDownloader beatmaps;
private ProgressNotification recentNotification; private ProgressNotification recentNotification;
private static readonly BeatmapSetInfo test_db_model = new BeatmapSetInfo private static readonly BeatmapSetInfo test_db_model = new BeatmapSetInfo
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Online
}; };
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(BeatmapManager beatmaps) private void load(BeatmapModelDownloader beatmaps)
{ {
this.beatmaps = beatmaps; this.beatmaps = beatmaps;

View File

@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -33,6 +33,7 @@ namespace osu.Game.Tests.Online
{ {
private RulesetStore rulesets; private RulesetStore rulesets;
private TestBeatmapManager beatmaps; private TestBeatmapManager beatmaps;
private TestBeatmapModelDownloader beatmapDownloader;
private string testBeatmapFile; private string testBeatmapFile;
private BeatmapInfo testBeatmapInfo; private BeatmapInfo testBeatmapInfo;
@ -46,6 +47,7 @@ namespace osu.Game.Tests.Online
{ {
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, API, audio, Resources, host, Beatmap.Default)); Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, API, audio, Resources, host, Beatmap.Default));
Dependencies.CacheAs<BeatmapModelDownloader>(beatmapDownloader = new TestBeatmapModelDownloader(beatmaps, API, host));
} }
[SetUp] [SetUp]
@ -80,13 +82,13 @@ namespace osu.Game.Tests.Online
AddAssert("ensure beatmap unavailable", () => !beatmaps.IsAvailableLocally(testBeatmapSet)); AddAssert("ensure beatmap unavailable", () => !beatmaps.IsAvailableLocally(testBeatmapSet));
addAvailabilityCheckStep("state not downloaded", BeatmapAvailability.NotDownloaded); addAvailabilityCheckStep("state not downloaded", BeatmapAvailability.NotDownloaded);
AddStep("start downloading", () => beatmaps.Download(testBeatmapSet)); AddStep("start downloading", () => beatmapDownloader.Download(testBeatmapSet));
addAvailabilityCheckStep("state downloading 0%", () => BeatmapAvailability.Downloading(0.0f)); addAvailabilityCheckStep("state downloading 0%", () => BeatmapAvailability.Downloading(0.0f));
AddStep("set progress 40%", () => ((TestDownloadRequest)beatmaps.GetExistingDownload(testBeatmapSet)).SetProgress(0.4f)); AddStep("set progress 40%", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet)).SetProgress(0.4f));
addAvailabilityCheckStep("state downloading 40%", () => BeatmapAvailability.Downloading(0.4f)); addAvailabilityCheckStep("state downloading 40%", () => BeatmapAvailability.Downloading(0.4f));
AddStep("finish download", () => ((TestDownloadRequest)beatmaps.GetExistingDownload(testBeatmapSet)).TriggerSuccess(testBeatmapFile)); AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet)).TriggerSuccess(testBeatmapFile));
addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing); addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing);
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true)); AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
@ -143,7 +145,10 @@ namespace osu.Game.Tests.Online
var beatmap = decoder.Decode(reader); var beatmap = decoder.Decode(reader);
info = beatmap.BeatmapInfo; info = beatmap.BeatmapInfo;
info.BeatmapSet.Beatmaps = new List<BeatmapInfo> { info };
Debug.Assert(info.BeatmapSet != null);
info.BeatmapSet.Beatmaps.Add(info);
info.BeatmapSet.Metadata = info.Metadata; info.BeatmapSet.Metadata = info.Metadata;
info.MD5Hash = stream.ComputeMD5Hash(); info.MD5Hash = stream.ComputeMD5Hash();
info.Hash = stream.ComputeSHA2Hash(); info.Hash = stream.ComputeSHA2Hash();
@ -168,22 +173,6 @@ namespace osu.Game.Tests.Online
return new TestBeatmapModelManager(this, storage, contextFactory, rulesets, api, host); return new TestBeatmapModelManager(this, storage, contextFactory, rulesets, api, host);
} }
protected override BeatmapModelDownloader CreateBeatmapModelDownloader(IModelImporter<BeatmapSetInfo> manager, IAPIProvider api, GameHost host)
{
return new TestBeatmapModelDownloader(manager, api, host);
}
internal class TestBeatmapModelDownloader : BeatmapModelDownloader
{
public TestBeatmapModelDownloader(IModelImporter<BeatmapSetInfo> importer, IAPIProvider apiProvider, GameHost gameHost)
: base(importer, apiProvider, gameHost)
{
}
protected override ArchiveDownloadRequest<IBeatmapSetInfo> CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize)
=> new TestDownloadRequest(set);
}
internal class TestBeatmapModelManager : BeatmapModelManager internal class TestBeatmapModelManager : BeatmapModelManager
{ {
private readonly TestBeatmapManager testBeatmapManager; private readonly TestBeatmapManager testBeatmapManager;
@ -202,6 +191,17 @@ namespace osu.Game.Tests.Online
} }
} }
internal class TestBeatmapModelDownloader : BeatmapModelDownloader
{
public TestBeatmapModelDownloader(IModelImporter<BeatmapSetInfo> importer, IAPIProvider apiProvider, GameHost gameHost)
: base(importer, apiProvider)
{
}
protected override ArchiveDownloadRequest<IBeatmapSetInfo> CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize)
=> new TestDownloadRequest(set);
}
private class TestDownloadRequest : ArchiveDownloadRequest<IBeatmapSetInfo> private class TestDownloadRequest : ArchiveDownloadRequest<IBeatmapSetInfo>
{ {
public new void SetProgress(float progress) => base.SetProgress(progress); public new void SetProgress(float progress) => base.SetProgress(progress);

View File

@ -1,35 +0,0 @@
// 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 NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.OnlinePlay
{
[HeadlessTest]
public class StatefulMultiplayerClientTest : MultiplayerTestScene
{
[Test]
public void TestUserAddedOnJoin()
{
var user = new APIUser { Id = 33 };
AddRepeatStep("add user multiple times", () => Client.AddUser(user), 3);
AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
}
[Test]
public void TestUserRemovedOnLeave()
{
var user = new APIUser { Id = 44 };
AddStep("add user", () => Client.AddUser(user));
AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
AddRepeatStep("remove user multiple times", () => Client.RemoveUser(user), 3);
AddAssert("room has 1 user", () => Client.Room?.Users.Count == 1);
}
}
}

View File

@ -2,10 +2,18 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using System.Threading;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Extensions;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
namespace osu.Game.Tests.Resources namespace osu.Game.Tests.Resources
{ {
@ -56,5 +64,78 @@ namespace osu.Game.Tests.Resources
} }
private static string getTempFilename() => temp_storage.GetFullPath(Guid.NewGuid() + ".osz"); private static string getTempFilename() => temp_storage.GetFullPath(Guid.NewGuid() + ".osz");
private static int importId;
/// <summary>
/// Create a test beatmap set model.
/// </summary>
/// <param name="difficultyCount">Number of difficulties. If null, a random number between 1 and 20 will be used.</param>
/// <param name="rulesets">Rulesets to cycle through when creating difficulties. If <c>null</c>, osu! ruleset will be used.</param>
public static BeatmapSetInfo CreateTestBeatmapSetInfo(int? difficultyCount = null, RulesetInfo[] rulesets = null)
{
int j = 0;
RulesetInfo getRuleset() => rulesets?[j++ % rulesets.Length] ?? new OsuRuleset().RulesetInfo;
int setId = Interlocked.Increment(ref importId);
var metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = "Some Artist " + RNG.Next(0, 9),
Title = $"Some Song (set id {setId}) {Guid.NewGuid()}",
AuthorString = "Some Guy " + RNG.Next(0, 9),
};
var beatmapSet = new BeatmapSetInfo
{
OnlineID = setId,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
DateAdded = DateTimeOffset.UtcNow,
Metadata = metadata
};
foreach (var b in getBeatmaps(difficultyCount ?? RNG.Next(1, 20)))
beatmapSet.Beatmaps.Add(b);
return beatmapSet;
IEnumerable<BeatmapInfo> getBeatmaps(int count)
{
for (int i = 0; i < count; i++)
{
int beatmapId = setId * 1000 + i;
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
float diff = (float)i / count * 10;
string version = "Normal";
if (diff > 6.6)
version = "Insane";
else if (diff > 3.3)
version = "Hard";
var rulesetInfo = getRuleset();
yield return new BeatmapInfo
{
OnlineID = beatmapId,
DifficultyName = $"{version} {beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
StarRating = diff,
Length = length,
BPM = bpm,
Ruleset = rulesetInfo,
RulesetID = rulesetInfo.ID ?? -1,
Metadata = metadata,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = diff,
}
};
}
}
}
} }
} }

View File

@ -142,7 +142,7 @@ namespace osu.Game.Tests.Scores.IO
var scoreManager = osu.Dependencies.Get<ScoreManager>(); var scoreManager = osu.Dependencies.Get<ScoreManager>();
beatmapManager.Delete(beatmapManager.QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == imported.BeatmapInfo.ID))); beatmapManager.Delete(beatmapManager.QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == imported.BeatmapInfo.ID)));
Assert.That(scoreManager.Query(s => s.ID == imported.ID).DeletePending, Is.EqualTo(true)); Assert.That(scoreManager.Query(s => s.Equals(imported)).DeletePending, Is.EqualTo(true));
var secondImport = await LoadScoreIntoOsu(osu, imported); var secondImport = await LoadScoreIntoOsu(osu, imported);
Assert.That(secondImport, Is.Null); Assert.That(secondImport, Is.Null);

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.IO.Archives; using osu.Game.IO.Archives;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -163,32 +164,109 @@ namespace osu.Game.Tests.Skins.IO
assertCorrectMetadata(import2, "name 1 [my custom skin 2]", "author 1", osu); assertCorrectMetadata(import2, "name 1 [my custom skin 2]", "author 1", osu);
}); });
[Test]
public Task TestExportThenImportDefaultSkin() => runSkinTest(osu =>
{
var skinManager = osu.Dependencies.Get<SkinManager>();
skinManager.EnsureMutableSkin();
MemoryStream exportStream = new MemoryStream();
Guid originalSkinId = skinManager.CurrentSkinInfo.Value.ID;
skinManager.CurrentSkinInfo.Value.PerformRead(s =>
{
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(DefaultSkin), s.CreateInstance(skinManager).GetType());
new LegacySkinExporter(osu.Dependencies.Get<Storage>()).ExportModelTo(s, exportStream);
Assert.Greater(exportStream.Length, 0);
});
var imported = skinManager.Import(new ImportTask(exportStream, "exported.osk"));
imported.Result.PerformRead(s =>
{
Assert.IsFalse(s.Protected);
Assert.AreNotEqual(originalSkinId, s.ID);
Assert.AreEqual(typeof(DefaultSkin), s.CreateInstance(skinManager).GetType());
});
return Task.CompletedTask;
});
[Test]
public Task TestExportThenImportClassicSkin() => runSkinTest(osu =>
{
var skinManager = osu.Dependencies.Get<SkinManager>();
skinManager.CurrentSkinInfo.Value = skinManager.DefaultLegacySkin.SkinInfo;
skinManager.EnsureMutableSkin();
MemoryStream exportStream = new MemoryStream();
Guid originalSkinId = skinManager.CurrentSkinInfo.Value.ID;
skinManager.CurrentSkinInfo.Value.PerformRead(s =>
{
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(DefaultLegacySkin), s.CreateInstance(skinManager).GetType());
new LegacySkinExporter(osu.Dependencies.Get<Storage>()).ExportModelTo(s, exportStream);
Assert.Greater(exportStream.Length, 0);
});
var imported = skinManager.Import(new ImportTask(exportStream, "exported.osk"));
imported.Result.PerformRead(s =>
{
Assert.IsFalse(s.Protected);
Assert.AreNotEqual(originalSkinId, s.ID);
Assert.AreEqual(typeof(DefaultLegacySkin), s.CreateInstance(skinManager).GetType());
});
return Task.CompletedTask;
});
#endregion #endregion
private void assertCorrectMetadata(SkinInfo import1, string name, string creator, OsuGameBase osu) private void assertCorrectMetadata(ILive<SkinInfo> import1, string name, string creator, OsuGameBase osu)
{ {
Assert.That(import1.Name, Is.EqualTo(name)); import1.PerformRead(i =>
Assert.That(import1.Creator, Is.EqualTo(creator)); {
Assert.That(i.Name, Is.EqualTo(name));
Assert.That(i.Creator, Is.EqualTo(creator));
// for extra safety let's reconstruct the skin, reading from the skin.ini. // for extra safety let's reconstruct the skin, reading from the skin.ini.
var instance = import1.CreateInstance((IStorageResourceProvider)osu.Dependencies.Get(typeof(SkinManager))); var instance = i.CreateInstance((IStorageResourceProvider)osu.Dependencies.Get(typeof(SkinManager)));
Assert.That(instance.Configuration.SkinInfo.Name, Is.EqualTo(name)); Assert.That(instance.Configuration.SkinInfo.Name, Is.EqualTo(name));
Assert.That(instance.Configuration.SkinInfo.Creator, Is.EqualTo(creator)); Assert.That(instance.Configuration.SkinInfo.Creator, Is.EqualTo(creator));
});
} }
private void assertImportedBoth(SkinInfo import1, SkinInfo import2) private void assertImportedBoth(ILive<SkinInfo> import1, ILive<SkinInfo> import2)
{ {
Assert.That(import2.ID, Is.Not.EqualTo(import1.ID)); import1.PerformRead(i1 => import2.PerformRead(i2 =>
Assert.That(import2.Hash, Is.Not.EqualTo(import1.Hash)); {
Assert.That(import2.Files.Select(f => f.FileInfoID), Is.Not.EquivalentTo(import1.Files.Select(f => f.FileInfoID))); Assert.That(i2.ID, Is.Not.EqualTo(i1.ID));
Assert.That(i2.Hash, Is.Not.EqualTo(i1.Hash));
Assert.That(i2.Files.First(), Is.Not.EqualTo(i1.Files.First()));
}));
} }
private void assertImportedOnce(SkinInfo import1, SkinInfo import2) private void assertImportedOnce(ILive<SkinInfo> import1, ILive<SkinInfo> import2)
{ {
Assert.That(import2.ID, Is.EqualTo(import1.ID)); import1.PerformRead(i1 => import2.PerformRead(i2 =>
Assert.That(import2.Hash, Is.EqualTo(import1.Hash)); {
Assert.That(import2.Files.Select(f => f.FileInfoID), Is.EquivalentTo(import1.Files.Select(f => f.FileInfoID))); Assert.That(i2.ID, Is.EqualTo(i1.ID));
Assert.That(i2.Hash, Is.EqualTo(i1.Hash));
Assert.That(i2.Files.First(), Is.EqualTo(i1.Files.First()));
}));
} }
private MemoryStream createEmptyOsk() private MemoryStream createEmptyOsk()
@ -255,10 +333,10 @@ namespace osu.Game.Tests.Skins.IO
} }
} }
private async Task<SkinInfo> loadSkinIntoOsu(OsuGameBase osu, ArchiveReader archive = null) private async Task<ILive<SkinInfo>> loadSkinIntoOsu(OsuGameBase osu, ArchiveReader archive = null)
{ {
var skinManager = osu.Dependencies.Get<SkinManager>(); var skinManager = osu.Dependencies.Get<SkinManager>();
return (await skinManager.Import(archive)).Value; return await skinManager.Import(archive);
} }
} }
} }

View File

@ -24,7 +24,7 @@ namespace osu.Game.Tests.Skins
private void load() private void load()
{ {
var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).Result; var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).Result;
skin = skins.GetSkin(imported.Value); skin = imported.PerformRead(skinInfo => skins.GetSkin(skinInfo));
} }
[Test] [Test]

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tests.Testing
{ {
// temporary ID to let RulesetConfigCache pass our // temporary ID to let RulesetConfigCache pass our
// config manager to the ruleset dependencies. // config manager to the ruleset dependencies.
RulesetInfo.ID = -1; RulesetInfo.OnlineID = -1;
} }
public override IResourceStore<byte[]> CreateResourceStore() => new NamespacedResourceStore<byte[]>(TestResources.GetStore(), @"Resources"); public override IResourceStore<byte[]> CreateResourceStore() => new NamespacedResourceStore<byte[]>(TestResources.GetStore(), @"Resources");

View File

@ -8,6 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
@ -122,7 +123,7 @@ namespace osu.Game.Tests.Visual.Background
private void setCustomSkin() private void setCustomSkin()
{ {
// feign a skin switch. this doesn't do anything except force CurrentSkin to become a LegacySkin. // feign a skin switch. this doesn't do anything except force CurrentSkin to become a LegacySkin.
AddStep("set custom skin", () => skins.CurrentSkinInfo.Value = new SkinInfo { ID = 5 }); AddStep("set custom skin", () => skins.CurrentSkinInfo.Value = new SkinInfo().ToLive());
} }
private void setDefaultSkin() => AddStep("set default skin", () => skins.CurrentSkinInfo.SetDefault()); private void setDefaultSkin() => AddStep("set default skin", () => skins.CurrentSkinInfo.SetDefault());

View File

@ -23,6 +23,11 @@ namespace osu.Game.Tests.Visual.Beatmaps
{ {
public class TestSceneBeatmapCard : OsuTestScene public class TestSceneBeatmapCard : OsuTestScene
{ {
/// <summary>
/// All cards on this scene use a common online ID to ensure that map download, preview tracks, etc. can be tested manually with online sources.
/// </summary>
private const int online_id = 163112;
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
private APIBeatmapSet[] testCases; private APIBeatmapSet[] testCases;
@ -38,11 +43,10 @@ namespace osu.Game.Tests.Visual.Beatmaps
var normal = CreateAPIBeatmapSet(Ruleset.Value); var normal = CreateAPIBeatmapSet(Ruleset.Value);
normal.HasVideo = true; normal.HasVideo = true;
normal.HasStoryboard = true; normal.HasStoryboard = true;
normal.OnlineID = 241526;
var withStatistics = CreateAPIBeatmapSet(Ruleset.Value); var withStatistics = CreateAPIBeatmapSet(Ruleset.Value);
withStatistics.Title = withStatistics.TitleUnicode = "play favourite stats"; withStatistics.Title = withStatistics.TitleUnicode = "play favourite stats";
withStatistics.Status = BeatmapSetOnlineStatus.Approved; withStatistics.Status = BeatmapOnlineStatus.Approved;
withStatistics.FavouriteCount = 284_239; withStatistics.FavouriteCount = 284_239;
withStatistics.PlayCount = 999_001; withStatistics.PlayCount = 999_001;
withStatistics.Ranked = DateTimeOffset.Now.AddDays(-45); withStatistics.Ranked = DateTimeOffset.Now.AddDays(-45);
@ -63,7 +67,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
var someDifficulties = getManyDifficultiesBeatmapSet(11); var someDifficulties = getManyDifficultiesBeatmapSet(11);
someDifficulties.Title = someDifficulties.TitleUnicode = "favourited"; someDifficulties.Title = someDifficulties.TitleUnicode = "favourited";
someDifficulties.Title = someDifficulties.TitleUnicode = "some difficulties"; someDifficulties.Title = someDifficulties.TitleUnicode = "some difficulties";
someDifficulties.Status = BeatmapSetOnlineStatus.Qualified; someDifficulties.Status = BeatmapOnlineStatus.Qualified;
someDifficulties.HasFavourited = true; someDifficulties.HasFavourited = true;
someDifficulties.FavouriteCount = 1; someDifficulties.FavouriteCount = 1;
someDifficulties.NominationStatus = new BeatmapSetNominationStatus someDifficulties.NominationStatus = new BeatmapSetNominationStatus
@ -73,7 +77,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
}; };
var manyDifficulties = getManyDifficultiesBeatmapSet(100); var manyDifficulties = getManyDifficultiesBeatmapSet(100);
manyDifficulties.Status = BeatmapSetOnlineStatus.Pending; manyDifficulties.Status = BeatmapOnlineStatus.Pending;
var explicitMap = CreateAPIBeatmapSet(Ruleset.Value); var explicitMap = CreateAPIBeatmapSet(Ruleset.Value);
explicitMap.Title = someDifficulties.TitleUnicode = "explicit beatmap"; explicitMap.Title = someDifficulties.TitleUnicode = "explicit beatmap";
@ -106,6 +110,9 @@ namespace osu.Game.Tests.Visual.Beatmaps
explicitFeaturedMap, explicitFeaturedMap,
longName longName
}; };
foreach (var testCase in testCases)
testCase.OnlineID = online_id;
} }
private APIBeatmapSet getUndownloadableBeatmapSet() => new APIBeatmapSet private APIBeatmapSet getUndownloadableBeatmapSet() => new APIBeatmapSet
@ -191,9 +198,9 @@ namespace osu.Game.Tests.Visual.Beatmaps
private void ensureSoleilyRemoved() private void ensureSoleilyRemoved()
{ {
AddUntilStep("ensure manager loaded", () => beatmaps != null); AddUntilStep("ensure manager loaded", () => beatmaps != null);
AddStep("remove soleily", () => AddStep("remove map", () =>
{ {
var beatmap = beatmaps.QueryBeatmapSet(b => b.OnlineID == 241526); var beatmap = beatmaps.QueryBeatmapSet(b => b.OnlineID == online_id);
if (beatmap != null) beatmaps.Delete(beatmap); if (beatmap != null) beatmaps.Delete(beatmap);
}); });

View File

@ -0,0 +1,71 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.Drawables.Cards;
using osu.Game.Graphics;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Beatmaps
{
public class TestSceneBeatmapCardDifficultyList : OsuTestScene
{
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
var beatmapSet = new APIBeatmapSet
{
Beatmaps = new[]
{
new APIBeatmap { RulesetID = 1, StarRating = 5.76, DifficultyName = "Oni" },
new APIBeatmap { RulesetID = 1, StarRating = 3.20, DifficultyName = "Muzukashii" },
new APIBeatmap { RulesetID = 1, StarRating = 2.45, DifficultyName = "Futsuu" },
new APIBeatmap { RulesetID = 0, StarRating = 2.04, DifficultyName = "Normal" },
new APIBeatmap { RulesetID = 0, StarRating = 3.51, DifficultyName = "Hard" },
new APIBeatmap { RulesetID = 0, StarRating = 5.25, DifficultyName = "Insane" },
new APIBeatmap { RulesetID = 2, StarRating = 2.64, DifficultyName = "Salad" },
new APIBeatmap { RulesetID = 2, StarRating = 3.56, DifficultyName = "Platter" },
new APIBeatmap { RulesetID = 2, StarRating = 4.65, DifficultyName = "Rain" },
new APIBeatmap { RulesetID = 3, StarRating = 1.93, DifficultyName = "[7K] Normal" },
new APIBeatmap { RulesetID = 3, StarRating = 3.18, DifficultyName = "[7K] Hyper" },
new APIBeatmap { RulesetID = 3, StarRating = 4.82, DifficultyName = "[7K] Another" },
new APIBeatmap { RulesetID = 4, StarRating = 9.99, DifficultyName = "Unknown?!" },
}
};
Child = new Container
{
Width = 300,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background2
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(10),
Child = new BeatmapCardDifficultyList(beatmapSet)
}
}
};
}
}
}

View File

@ -0,0 +1,84 @@
// 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.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing;
using osu.Game.Beatmaps.Drawables.Cards;
using osu.Game.Beatmaps.Drawables.Cards.Buttons;
using osu.Game.Overlays;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Beatmaps
{
public class TestSceneBeatmapCardThumbnail : OsuManualInputManagerTestScene
{
private PlayButton playButton => this.ChildrenOfType<PlayButton>().Single();
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
[Test]
public void TestThumbnailPreview()
{
BeatmapCardThumbnail thumbnail = null;
AddStep("create thumbnail", () =>
{
var beatmapSet = CreateAPIBeatmapSet(Ruleset.Value);
beatmapSet.OnlineID = 241526; // ID hardcoded to ensure that the preview track exists online.
Child = thumbnail = new BeatmapCardThumbnail(beatmapSet)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200)
};
});
AddStep("enable dim", () => thumbnail.Dimmed.Value = true);
AddUntilStep("button visible", () => playButton.IsPresent);
AddStep("click button", () =>
{
InputManager.MoveMouseTo(playButton);
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for start", () => playButton.Playing.Value && playButton.Enabled.Value);
iconIs(FontAwesome.Solid.Stop);
AddStep("click again", () =>
{
InputManager.MoveMouseTo(playButton);
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for stop", () => !playButton.Playing.Value && playButton.Enabled.Value);
iconIs(FontAwesome.Solid.Play);
AddStep("click again", () =>
{
InputManager.MoveMouseTo(playButton);
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for start", () => playButton.Playing.Value && playButton.Enabled.Value);
iconIs(FontAwesome.Solid.Stop);
AddStep("disable dim", () => thumbnail.Dimmed.Value = false);
AddWaitStep("wait some", 3);
AddAssert("button still visible", () => playButton.IsPresent);
// The track plays in real-time, so we need to check for progress in increments to avoid timeout.
AddUntilStep("progress > 0.25", () => thumbnail.ChildrenOfType<PlayButton>().Single().Progress.Value > 0.25);
AddUntilStep("progress > 0.5", () => thumbnail.ChildrenOfType<PlayButton>().Single().Progress.Value > 0.5);
AddUntilStep("progress > 0.75", () => thumbnail.ChildrenOfType<PlayButton>().Single().Progress.Value > 0.75);
AddUntilStep("wait for track to end", () => !playButton.Playing.Value);
AddUntilStep("button hidden", () => !playButton.IsPresent);
}
private void iconIs(IconUsage usage) => AddUntilStep("icon is correct", () => playButton.ChildrenOfType<SpriteIcon>().Any(icon => icon.Icon.Equals(usage)));
}
}

View File

@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
Origin = Anchor.Centre, Origin = Anchor.Centre,
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10), Spacing = new Vector2(0, 10),
ChildrenEnumerable = Enum.GetValues(typeof(BeatmapSetOnlineStatus)).Cast<BeatmapSetOnlineStatus>().Select(status => new BeatmapSetOnlineStatusPill ChildrenEnumerable = Enum.GetValues(typeof(BeatmapOnlineStatus)).Cast<BeatmapOnlineStatus>().Select(status => new BeatmapSetOnlineStatusPill
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,

View File

@ -69,7 +69,10 @@ namespace osu.Game.Tests.Visual.Editing
AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu); AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
PushAndConfirm(() => new PlaySongSelect()); Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new PlaySongSelect());
AddUntilStep("wait for carousel load", () => songSelect.BeatmapSetsLoaded);
AddUntilStep("Wait for beatmap selected", () => !Game.Beatmap.IsDefault); AddUntilStep("Wait for beatmap selected", () => !Game.Beatmap.IsDefault);
AddStep("Open options", () => InputManager.Key(Key.F3)); AddStep("Open options", () => InputManager.Key(Key.F3));

View File

@ -12,6 +12,7 @@ using osu.Framework.Testing;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Extensions; using osu.Game.Extensions;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
@ -41,7 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestEmptyLegacyBeatmapSkinFallsBack() public void TestEmptyLegacyBeatmapSkinFallsBack()
{ {
CreateSkinTest(SkinInfo.Default, () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null)); CreateSkinTest(DefaultSkin.CreateInfo(), () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null));
AddUntilStep("wait for hud load", () => Player.ChildrenOfType<SkinnableTargetContainer>().All(c => c.ComponentsLoaded)); AddUntilStep("wait for hud load", () => Player.ChildrenOfType<SkinnableTargetContainer>().All(c => c.ComponentsLoaded));
AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value));
} }
@ -52,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
AddStep("setup skins", () => AddStep("setup skins", () =>
{ {
skinManager.CurrentSkinInfo.Value = gameCurrentSkin; skinManager.CurrentSkinInfo.Value = gameCurrentSkin.ToLive();
currentBeatmapSkin = getBeatmapSkin(); currentBeatmapSkin = getBeatmapSkin();
}); });
}); });

View File

@ -233,7 +233,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
prepareTokenResponse(true); prepareTokenResponse(true);
createPlayerTest(false, createRuleset: () => new OsuRuleset { RulesetInfo = { ID = rulesetId } }); createPlayerTest(false, createRuleset: () => new OsuRuleset { RulesetInfo = { OnlineID = rulesetId ?? -1 } });
AddUntilStep("wait for token request", () => Player.TokenCreationRequested); AddUntilStep("wait for token request", () => Player.TokenCreationRequested);

View File

@ -43,83 +43,88 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached] [Cached]
private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty<Mod>()); private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty<Mod>());
[SetUp] [SetUpSteps]
public void SetUp() => Schedule(() => public void SetUpSteps()
{ {
replay = new Replay(); AddStep("Reset recorder state", cleanUpState);
Add(new GridContainer AddStep("Setup containers", () =>
{ {
RelativeSizeAxes = Axes.Both, replay = new Replay();
Content = new[]
Add(new GridContainer
{ {
new Drawable[] RelativeSizeAxes = Axes.Both,
Content = new[]
{ {
recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) new Drawable[]
{ {
Recorder = recorder = new TestReplayRecorder(new Score recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{ {
Replay = replay, Recorder = recorder = new TestReplayRecorder(new Score
ScoreInfo = { BeatmapInfo = gameplayState.Beatmap.BeatmapInfo }
})
{
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{ {
new Box Replay = replay,
ScoreInfo = { BeatmapInfo = gameplayState.Beatmap.BeatmapInfo }
})
{
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{ {
Colour = Color4.Brown, new Box
RelativeSizeAxes = Axes.Both, {
}, Colour = Color4.Brown,
new OsuSpriteText RelativeSizeAxes = Axes.Both,
{ },
Text = "Recording", new OsuSpriteText
Scale = new Vector2(3), {
Anchor = Anchor.Centre, Text = "Recording",
Origin = Anchor.Centre, Scale = new Vector2(3),
}, Anchor = Anchor.Centre,
new TestInputConsumer() Origin = Anchor.Centre,
} },
}, new TestInputConsumer()
} }
}, },
new Drawable[] }
{ },
playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) new Drawable[]
{ {
ReplayInputHandler = new TestFramedReplayInputHandler(replay) playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{ {
GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos), ReplayInputHandler = new TestFramedReplayInputHandler(replay)
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{ {
new Box GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{ {
Colour = Color4.DarkBlue, new Box
RelativeSizeAxes = Axes.Both, {
}, Colour = Color4.DarkBlue,
new OsuSpriteText RelativeSizeAxes = Axes.Both,
{ },
Text = "Playback", new OsuSpriteText
Scale = new Vector2(3), {
Anchor = Anchor.Centre, Text = "Playback",
Origin = Anchor.Centre, Scale = new Vector2(3),
}, Anchor = Anchor.Centre,
new TestInputConsumer() Origin = Anchor.Centre,
} },
}, new TestInputConsumer()
}
},
}
} }
} }
} });
}); });
}); }
[Test] [Test]
public void TestBasic() public void TestBasic()
@ -184,7 +189,14 @@ namespace osu.Game.Tests.Visual.Gameplay
[TearDownSteps] [TearDownSteps]
public void TearDown() public void TearDown()
{ {
AddStep("stop recorder", () => recorder.Expire()); AddStep("stop recorder", cleanUpState);
}
private void cleanUpState()
{
// Ensure previous recorder is disposed else it may affect the global playing state of `SpectatorClient`.
recorder?.RemoveAndDisposeImmediately();
recorder = null;
} }
public class TestFramedReplayInputHandler : FramedReplayInputHandler<TestReplayFrame> public class TestFramedReplayInputHandler : FramedReplayInputHandler<TestReplayFrame>

View File

@ -1,228 +0,0 @@
// 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;
using System.Collections.Generic;
using osu.Framework.Allocation;
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.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Replays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
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;
[Cached]
private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty<Mod>());
public TestSceneReplayRecording()
{
Replay replay = new Replay();
Add(new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
Recorder = new TestReplayRecorder(new Score
{
Replay = replay,
ScoreInfo = { BeatmapInfo = gameplayState.Beatmap.BeatmapInfo }
})
{
ScreenSpaceToGamefield = pos => recordingManager?.ToLocalSpace(pos) ?? Vector2.Zero,
},
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(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
ReplayInputHandler = new TestFramedReplayInputHandler(replay)
{
GamefieldToScreenSpace = pos => playbackManager?.ToScreenSpace(pos) ?? Vector2.Zero,
},
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 void CollectPendingInputs(List<IInput> inputs)
{
inputs.Add(new MousePositionAbsoluteInput { Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero) });
inputs.Add(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(KeyBindingPressEvent<TestAction> e)
{
if (e.Repeat)
return false;
box.Colour = Color4.White;
return true;
}
public void OnReleased(KeyBindingReleaseEvent<TestAction> e)
{
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<IKeyBinding> 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(Score target)
: base(target)
{
}
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<TestAction> actions, ReplayFrame previousFrame) =>
new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
}
}

View File

@ -2,12 +2,10 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Skinning;
using osu.Game.Skinning.Editor; using osu.Game.Skinning.Editor;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
@ -16,9 +14,6 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
private SkinEditor skinEditor; private SkinEditor skinEditor;
[Resolved]
private SkinManager skinManager { get; set; }
protected override bool Autoplay => true; protected override bool Autoplay => true;
[SetUpSteps] [SetUpSteps]

View File

@ -10,7 +10,6 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
@ -36,9 +35,6 @@ namespace osu.Game.Tests.Visual.Gameplay
private Drawable hideTarget => hudOverlay.KeyCounter; private Drawable hideTarget => hudOverlay.KeyCounter;
private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First(); private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First();
[Resolved]
private OsuConfigManager config { get; set; }
[Test] [Test]
public void TestComboCounterIncrementing() public void TestComboCounterIncrementing()
{ {

View File

@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestClientSendsCorrectRuleset() public void TestClientSendsCorrectRuleset()
{ {
AddUntilStep("spectator client sending frames", () => spectatorClient.PlayingUserStates.ContainsKey(dummy_user_id)); AddUntilStep("spectator client sending frames", () => spectatorClient.PlayingUserStates.ContainsKey(dummy_user_id));
AddAssert("spectator client sent correct ruleset", () => spectatorClient.PlayingUserStates[dummy_user_id].RulesetID == Ruleset.Value.ID); AddAssert("spectator client sent correct ruleset", () => spectatorClient.PlayingUserStates[dummy_user_id].RulesetID == Ruleset.Value.OnlineID);
} }
public override void TearDownSteps() public override void TearDownSteps()

View File

@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private TestReplayRecorder recorder; private TestReplayRecorder recorder;
private readonly ManualClock manualClock = new ManualClock(); private ManualClock manualClock;
private OsuSpriteText latencyDisplay; private OsuSpriteText latencyDisplay;
@ -66,113 +66,121 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached] [Cached]
private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty<Mod>()); private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty<Mod>());
[SetUp] [SetUpSteps]
public void SetUp() => Schedule(() => public void SetUpSteps()
{ {
replay = new Replay(); AddStep("Reset recorder state", cleanUpState);
users.BindTo(spectatorClient.PlayingUsers); AddStep("Setup containers", () =>
users.BindCollectionChanged((obj, args) =>
{ {
switch (args.Action) replay = new Replay();
manualClock = new ManualClock();
spectatorClient.OnNewFrames += onNewFrames;
users.BindTo(spectatorClient.PlayingUsers);
users.BindCollectionChanged((obj, args) =>
{ {
case NotifyCollectionChangedAction.Add: switch (args.Action)
Debug.Assert(args.NewItems != null);
foreach (int user in args.NewItems)
{
if (user == api.LocalUser.Value.Id)
spectatorClient.WatchUser(user);
}
break;
case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
foreach (int user in args.OldItems)
{
if (user == api.LocalUser.Value.Id)
spectatorClient.StopWatchingUser(user);
}
break;
}
}, true);
spectatorClient.OnNewFrames += onNewFrames;
Add(new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{ {
recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
foreach (int user in args.NewItems)
{
if (user == api.LocalUser.Value.Id)
spectatorClient.WatchUser(user);
}
break;
case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
foreach (int user in args.OldItems)
{
if (user == api.LocalUser.Value.Id)
spectatorClient.StopWatchingUser(user);
}
break;
}
}, true);
Children = new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{ {
Recorder = recorder = new TestReplayRecorder new Drawable[]
{ {
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos), recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{ {
new Box Recorder = recorder = new TestReplayRecorder
{
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
},
Child = new Container
{ {
Colour = Color4.Brown,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Brown,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Text = "Sending",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestInputConsumer()
}
}, },
new OsuSpriteText
{
Text = "Sending",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestInputConsumer()
} }
}, },
new Drawable[]
{
playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
Clock = new FramedClock(manualClock),
ReplayInputHandler = replayHandler = 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 = "Receiving",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestInputConsumer()
}
},
}
}
} }
}, },
new Drawable[] latencyDisplay = new OsuSpriteText()
{ };
playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
Clock = new FramedClock(manualClock),
ReplayInputHandler = replayHandler = 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 = "Receiving",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestInputConsumer()
}
},
}
}
}
}); });
}
Add(latencyDisplay = new OsuSpriteText());
});
private void onNewFrames(int userId, FrameDataBundle frames) private void onNewFrames(int userId, FrameDataBundle frames)
{ {
@ -189,6 +197,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestBasic() public void TestBasic()
{ {
AddStep("Wait for user input", () => { });
} }
private double latency = SpectatorClient.TIME_BETWEEN_SENDS; private double latency = SpectatorClient.TIME_BETWEEN_SENDS;
@ -232,11 +241,15 @@ namespace osu.Game.Tests.Visual.Gameplay
[TearDownSteps] [TearDownSteps]
public void TearDown() public void TearDown()
{ {
AddStep("stop recorder", () => AddStep("stop recorder", cleanUpState);
{ }
recorder.Expire();
spectatorClient.OnNewFrames -= onNewFrames; private void cleanUpState()
}); {
// Ensure previous recorder is disposed else it may affect the global playing state of `SpectatorClient`.
recorder?.RemoveAndDisposeImmediately();
recorder = null;
spectatorClient.OnNewFrames -= onNewFrames;
} }
public class TestFramedReplayInputHandler : FramedReplayInputHandler<TestReplayFrame> public class TestFramedReplayInputHandler : FramedReplayInputHandler<TestReplayFrame>

View File

@ -30,25 +30,10 @@ namespace osu.Game.Tests.Visual.Menus
[Test] [Test]
public void TestMusicNavigationActions() public void TestMusicNavigationActions()
{ {
int importId = 0;
Queue<(IWorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null; Queue<(IWorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null;
// ensure we have at least two beatmaps available to identify the direction the music controller navigated to. // ensure we have at least two beatmaps available to identify the direction the music controller navigated to.
AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(new BeatmapSetInfo AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(TestResources.CreateTestBeatmapSetInfo()).Wait(), 5);
{
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty(),
}
},
Metadata = new BeatmapMetadata
{
Artist = $"a test map {importId++}",
Title = "title",
}
}).Wait(), 5);
AddStep("import beatmap with track", () => AddStep("import beatmap with track", () =>
{ {

View File

@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Menus
private TestToolbar toolbar; private TestToolbar toolbar;
[Resolved] [Resolved]
private RulesetStore rulesets { get; set; } private IRulesetStore rulesets { get; set; }
[SetUp] [SetUp]
public void SetUp() => Schedule(() => public void SetUp() => Schedule(() =>

View File

@ -78,6 +78,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("playlist item is not expired", () => Client.APIRoom?.Playlist[1].Expired == false); AddAssert("playlist item is not expired", () => Client.APIRoom?.Playlist[1].Expired == false);
} }
[Test]
public void TestCorrectItemSelectedAfterNewItemAdded()
{
addItem(() => OtherBeatmap);
AddAssert("selected beatmap is initial beatmap", () => Beatmap.Value.BeatmapInfo.OnlineID == InitialBeatmap.OnlineID);
}
private void addItem(Func<BeatmapInfo> beatmap) private void addItem(Func<BeatmapInfo> beatmap)
{ {
AddStep("click edit button", () => AddStep("click edit button", () =>
@ -86,7 +93,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.Click(MouseButton.Left); InputManager.Click(MouseButton.Left);
}); });
AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.IsLoaded); AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded);
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(beatmap())); AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(beatmap()));
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen); AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
} }

View File

@ -13,15 +13,16 @@ using osu.Framework.Platform;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Overlays.BeatmapListing.Panels;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using osu.Game.Users.Drawables;
using osuTK; using osuTK;
using osuTK.Input; using osuTK.Input;
@ -34,6 +35,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
private BeatmapManager manager; private BeatmapManager manager;
private RulesetStore rulesets; private RulesetStore rulesets;
[Cached(typeof(UserLookupCache))]
private readonly TestUserLookupCache userLookupCache = new TestUserLookupCache();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio) private void load(GameHost host, AudioManager audio)
{ {
@ -231,7 +235,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
assertDownloadButtonVisible(false); assertDownloadButtonVisible(false);
void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}", void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}",
() => playlist.ChildrenOfType<BeatmapPanelDownloadButton>().Single().Alpha == (visible ? 1 : 0)); () => playlist.ChildrenOfType<BeatmapDownloadButton>().Single().Alpha == (visible ? 1 : 0));
} }
[Test] [Test]
@ -245,7 +249,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
createPlaylist(byOnlineId, byChecksum); createPlaylist(byOnlineId, byChecksum);
AddAssert("download buttons shown", () => playlist.ChildrenOfType<BeatmapPanelDownloadButton>().All(d => d.IsPresent)); AddAssert("download buttons shown", () => playlist.ChildrenOfType<BeatmapDownloadButton>().All(d => d.IsPresent));
} }
[Test] [Test]
@ -304,6 +308,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded)); AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded));
} }
[TestCase(false)]
[TestCase(true)]
public void TestWithOwner(bool withOwner)
{
createPlaylist(false, false, withOwner);
AddAssert("owner visible", () => playlist.ChildrenOfType<UpdateableAvatar>().All(a => a.IsPresent == withOwner));
}
private void moveToItem(int index, Vector2? offset = null) private void moveToItem(int index, Vector2? offset = null)
=> AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType<DifficultyIcon>().ElementAt(index), offset)); => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType<DifficultyIcon>().ElementAt(index), offset));
@ -327,11 +340,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
=> AddAssert($"delete button {index} {(visible ? "is" : "is not")} visible", => AddAssert($"delete button {index} {(visible ? "is" : "is not")} visible",
() => (playlist.ChildrenOfType<DrawableRoomPlaylistItem.PlaylistRemoveButton>().ElementAt(2 + index * 2).Alpha > 0) == visible); () => (playlist.ChildrenOfType<DrawableRoomPlaylistItem.PlaylistRemoveButton>().ElementAt(2 + index * 2).Alpha > 0) == visible);
private void createPlaylist(bool allowEdit, bool allowSelection) private void createPlaylist(bool allowEdit, bool allowSelection, bool showItemOwner = false)
{ {
AddStep("create playlist", () => AddStep("create playlist", () =>
{ {
Child = playlist = new TestPlaylist(allowEdit, allowSelection) Child = playlist = new TestPlaylist(allowEdit, allowSelection, showItemOwner)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -343,6 +356,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
playlist.Items.Add(new PlaylistItem playlist.Items.Add(new PlaylistItem
{ {
ID = i, ID = i,
OwnerID = 2,
Beatmap = Beatmap =
{ {
Value = i % 2 == 1 Value = i % 2 == 1
@ -390,6 +404,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
playlist.Items.Add(new PlaylistItem playlist.Items.Add(new PlaylistItem
{ {
ID = index++, ID = index++,
OwnerID = 2,
Beatmap = { Value = b }, Beatmap = { Value = b },
Ruleset = { Value = new OsuRuleset().RulesetInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo },
RequiredMods = RequiredMods =
@ -409,8 +424,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
public new IReadOnlyDictionary<PlaylistItem, RearrangeableListItem<PlaylistItem>> ItemMap => base.ItemMap; public new IReadOnlyDictionary<PlaylistItem, RearrangeableListItem<PlaylistItem>> ItemMap => base.ItemMap;
public TestPlaylist(bool allowEdit, bool allowSelection) public TestPlaylist(bool allowEdit, bool allowSelection, bool showItemOwner = false)
: base(allowEdit, allowSelection) : base(allowEdit, allowSelection, showItemOwner: showItemOwner)
{ {
} }
} }

View File

@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.Click(MouseButton.Left); InputManager.Click(MouseButton.Left);
}); });
AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.IsLoaded); AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded);
BeatmapInfo otherBeatmap = null; BeatmapInfo otherBeatmap = null;
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(otherBeatmap = beatmap())); AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(otherBeatmap = beatmap()));

View File

@ -2,11 +2,8 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Components;
@ -18,12 +15,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
public class TestSceneMatchBeatmapDetailArea : OnlinePlayTestScene public class TestSceneMatchBeatmapDetailArea : OnlinePlayTestScene
{ {
[Resolved]
private BeatmapManager beatmapManager { get; set; }
[Resolved]
private RulesetStore rulesetStore { get; set; }
[SetUp] [SetUp]
public new void Setup() => Schedule(() => public new void Setup() => Schedule(() =>
{ {

View File

@ -204,7 +204,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
// edit playlist item // edit playlist item
AddStep("Press select", () => InputManager.Key(Key.Enter)); AddStep("Press select", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for song select", () => InputManager.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault() != null); AddUntilStep("wait for song select", () => InputManager.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
// select beatmap // select beatmap
AddStep("Press select", () => InputManager.Key(Key.Enter)); AddStep("Press select", () => InputManager.Key(Key.Enter));
@ -253,7 +253,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Ruleset = { Value = new OsuRuleset().RulesetInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo },
} }
} }
}); }, API.LocalUser.Value);
}); });
AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter()); AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter());
@ -283,7 +283,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Ruleset = { Value = new OsuRuleset().RulesetInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo },
} }
} }
}); }, API.LocalUser.Value);
}); });
AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter()); AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter());
@ -336,7 +336,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Ruleset = { Value = new OsuRuleset().RulesetInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo },
} }
} }
}); }, API.LocalUser.Value);
}); });
AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter()); AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter());
@ -397,6 +397,44 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("user state is idle", () => client.LocalUser?.State == MultiplayerUserState.Idle); AddUntilStep("user state is idle", () => client.LocalUser?.State == MultiplayerUserState.Idle);
} }
[Test]
public void TestPlayStartsWithCorrectBeatmapWhileAtSongSelect()
{
createRoom(() => new Room
{
Name = { Value = "Test Room" },
Playlist =
{
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
}
}
});
AddStep("Enter song select", () =>
{
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerScreenStack.CurrentScreen).CurrentSubScreen;
((MultiplayerMatchSubScreen)currentSubScreen).SelectBeatmap();
});
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == client.Room?.Playlist.First().BeatmapID);
AddStep("Select next beatmap", () => InputManager.Key(Key.Down));
AddUntilStep("Beatmap doesn't match current item", () => Beatmap.Value.BeatmapInfo.OnlineID != client.Room?.Playlist.First().BeatmapID);
AddStep("start match externally", () => client.StartMatch());
AddUntilStep("play started", () => multiplayerScreenStack.CurrentScreen is Player);
AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == client.Room?.Playlist.First().BeatmapID);
}
[Test] [Test]
public void TestLocalPlayDoesNotStartWhileSpectatingWithNoBeatmap() public void TestLocalPlayDoesNotStartWhileSpectatingWithNoBeatmap()
{ {
@ -597,7 +635,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Ruleset = { Value = new OsuRuleset().RulesetInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo },
} }
} }
}); }, API.LocalUser.Value);
}); });
AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter()); AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter());

View File

@ -47,6 +47,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
beatmaps = new List<BeatmapInfo>(); beatmaps = new List<BeatmapInfo>();
var metadata = new BeatmapMetadata
{
Artist = "Some Artist",
Title = "Some Beatmap",
AuthorString = "Some Author"
};
var beatmapSetInfo = new BeatmapSetInfo
{
OnlineID = 10,
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
Metadata = metadata,
DateAdded = DateTimeOffset.UtcNow
};
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
int beatmapId = 10 * 10 + i; int beatmapId = 10 * 10 + i;
@ -54,29 +69,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
int length = RNG.Next(30000, 200000); int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200); double bpm = RNG.NextSingle(80, 200);
beatmaps.Add(new BeatmapInfo var beatmap = new BeatmapInfo
{ {
Ruleset = rulesets.GetRuleset(i % 4), Ruleset = rulesets.GetRuleset(i % 4),
OnlineID = beatmapId, OnlineID = beatmapId,
Length = length, Length = length,
BPM = bpm, BPM = bpm,
Metadata = metadata,
BaseDifficulty = new BeatmapDifficulty() BaseDifficulty = new BeatmapDifficulty()
}); };
beatmaps.Add(beatmap);
beatmapSetInfo.Beatmaps.Add(beatmap);
} }
manager.Import(new BeatmapSetInfo manager.Import(beatmapSetInfo).Wait();
{
OnlineID = 10,
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
Artist = "Some Artist",
Title = "Some Beatmap",
AuthorString = "Some Author"
},
Beatmaps = beatmaps,
DateAdded = DateTimeOffset.UtcNow
}).Wait();
} }
public override void SetUpSteps() public override void SetUpSteps()
@ -91,7 +98,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
}); });
AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value))); AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value)));
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen()); AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
} }
[Test] [Test]
@ -137,7 +144,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("set mods", () => SelectedMods.Value = new[] { new TaikoModDoubleTime() }); AddStep("set mods", () => SelectedMods.Value = new[] { new TaikoModDoubleTime() });
AddStep("confirm selection", () => songSelect.FinaliseSelection()); AddStep("confirm selection", () => songSelect.FinaliseSelection());
AddStep("exit song select", () => songSelect.Exit());
AddUntilStep("song select exited", () => !songSelect.IsCurrentScreen());
AddAssert("beatmap not changed", () => Beatmap.Value.BeatmapInfo.Equals(selectedBeatmap)); AddAssert("beatmap not changed", () => Beatmap.Value.BeatmapInfo.Equals(selectedBeatmap));
AddAssert("ruleset not changed", () => Ruleset.Value.Equals(new TaikoRuleset().RulesetInfo)); AddAssert("ruleset not changed", () => Ruleset.Value.Equals(new TaikoRuleset().RulesetInfo));

View File

@ -0,0 +1,234 @@
// 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;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Screens.OnlinePlay;
using osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Resources;
using osuTK;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMultiplayerPlaylist : MultiplayerTestScene
{
private MultiplayerPlaylist list;
private BeatmapManager beatmaps;
private RulesetStore rulesets;
private BeatmapSetInfo importedSet;
private BeatmapInfo importedBeatmap;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
}
[SetUp]
public new void Setup() => Schedule(() =>
{
Child = list = new MultiplayerPlaylist
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.4f, 0.8f)
};
});
[SetUpSteps]
public new void SetUpSteps()
{
AddStep("import beatmap", () =>
{
beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First();
importedBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0);
});
AddStep("change to all players mode", () => Client.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }));
}
[Test]
public void TestNonExpiredItemsAddedToQueueList()
{
assertItemInQueueListStep(1, 0);
addItemStep();
assertItemInQueueListStep(2, 1);
addItemStep();
assertItemInQueueListStep(3, 2);
}
[Test]
public void TestExpiredItemsAddedToHistoryList()
{
assertItemInQueueListStep(1, 0);
addItemStep(true);
assertItemInHistoryListStep(2, 0);
addItemStep(true);
assertItemInHistoryListStep(3, 0);
assertItemInHistoryListStep(2, 1);
// Initial item is still in the queue.
assertItemInQueueListStep(1, 0);
}
[Test]
public void TestExpiredItemsMoveToQueueList()
{
addItemStep();
addItemStep();
AddStep("finish current item", () => Client.FinishCurrentItem());
assertItemInHistoryListStep(1, 0);
assertItemInQueueListStep(2, 0);
assertItemInQueueListStep(3, 1);
AddStep("finish current item", () => Client.FinishCurrentItem());
assertItemInHistoryListStep(2, 0);
assertItemInHistoryListStep(1, 1);
assertItemInQueueListStep(3, 0);
AddStep("finish current item", () => Client.FinishCurrentItem());
assertItemInHistoryListStep(3, 0);
assertItemInHistoryListStep(2, 1);
assertItemInHistoryListStep(1, 2);
}
[Test]
public void TestListsClearedWhenRoomLeft()
{
addItemStep();
AddStep("finish current item", () => Client.FinishCurrentItem());
AddStep("leave room", () => RoomManager.PartRoom());
AddUntilStep("wait for room part", () => Client.Room == null);
AddUntilStep("item 0 not in lists", () => !inHistoryList(0) && !inQueueList(0));
AddUntilStep("item 1 not in lists", () => !inHistoryList(0) && !inQueueList(0));
}
[Ignore("Expired items are initially removed from the room.")]
[Test]
public void TestJoinRoomWithMixedItemsAddedInCorrectLists()
{
AddStep("leave room", () => RoomManager.PartRoom());
AddUntilStep("wait for room part", () => Client.Room == null);
AddStep("join room with items", () =>
{
RoomManager.CreateRoom(new Room
{
Name = { Value = "test name" },
Playlist =
{
new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
Ruleset = { Value = Ruleset.Value }
},
new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
Ruleset = { Value = Ruleset.Value },
Expired = true
}
}
});
});
AddUntilStep("wait for room join", () => RoomJoined);
assertItemInQueueListStep(1, 0);
assertItemInHistoryListStep(2, 0);
}
/// <summary>
/// Adds a step to create a new playlist item.
/// </summary>
private void addItemStep(bool expired = false) => AddStep("add item", () => Client.AddPlaylistItem(new MultiplayerPlaylistItem(new PlaylistItem
{
Beatmap = { Value = importedBeatmap },
BeatmapID = importedBeatmap.OnlineID ?? -1,
Expired = expired,
PlayedAt = DateTimeOffset.Now
})));
/// <summary>
/// Asserts the position of a given playlist item in the queue list.
/// </summary>
/// <param name="playlistItemId">The item id.</param>
/// <param name="visualIndex">The index at which the item should appear visually. The item with index 0 is at the top of the list.</param>
private void assertItemInQueueListStep(int playlistItemId, int visualIndex)
{
changeDisplayModeStep(MultiplayerPlaylistDisplayMode.Queue);
AddUntilStep($"{playlistItemId} in queue at pos = {visualIndex}", () =>
{
return !inHistoryList(playlistItemId)
&& this.ChildrenOfType<MultiplayerQueueList>()
.Single()
.ChildrenOfType<DrawableRoomPlaylistItem>()
.OrderBy(drawable => drawable.Position.Y)
.TakeWhile(drawable => drawable.Item.ID != playlistItemId)
.Count() == visualIndex;
});
}
/// <summary>
/// Asserts the position of a given playlist item in the history list.
/// </summary>
/// <param name="playlistItemId">The item id.</param>
/// <param name="visualIndex">The index at which the item should appear visually. The item with index 0 is at the top of the list.</param>
private void assertItemInHistoryListStep(int playlistItemId, int visualIndex)
{
changeDisplayModeStep(MultiplayerPlaylistDisplayMode.History);
AddUntilStep($"{playlistItemId} in history at pos = {visualIndex}", () =>
{
return !inQueueList(playlistItemId)
&& this.ChildrenOfType<MultiplayerHistoryList>()
.Single()
.ChildrenOfType<DrawableRoomPlaylistItem>()
.OrderBy(drawable => drawable.Position.Y)
.TakeWhile(drawable => drawable.Item.ID != playlistItemId)
.Count() == visualIndex;
});
}
private void changeDisplayModeStep(MultiplayerPlaylistDisplayMode mode) => AddStep($"change list to {mode}", () => list.DisplayMode.Value = mode);
private bool inQueueList(int playlistItemId)
{
return this.ChildrenOfType<MultiplayerQueueList>()
.Single()
.Items.Any(i => i.ID == playlistItemId);
}
private bool inHistoryList(int playlistItemId)
{
return this.ChildrenOfType<MultiplayerHistoryList>()
.Single()
.Items.Any(i => i.ID == playlistItemId);
}
}
}

View File

@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
PlaylistItem playlistItem = new PlaylistItem PlaylistItem playlistItem = new PlaylistItem
{ {
BeatmapID = beatmapInfo.ID, BeatmapID = beatmapInfo.OnlineID ?? -1,
}; };
Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem)); Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem));

View File

@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
PlaylistItem playlistItem = new PlaylistItem PlaylistItem playlistItem = new PlaylistItem
{ {
BeatmapID = beatmapInfo.ID, BeatmapID = beatmapInfo.OnlineID ?? -1,
}; };
SortedDictionary<int, BindableInt> teamScores = new SortedDictionary<int, BindableInt> SortedDictionary<int, BindableInt> teamScores = new SortedDictionary<int, BindableInt>

View File

@ -2,14 +2,10 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Extensions;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Utils; using osu.Framework.Utils;
@ -21,15 +17,13 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Tests.Resources;
using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Tests.Visual.OnlinePlay;
namespace osu.Game.Tests.Visual.Multiplayer namespace osu.Game.Tests.Visual.Multiplayer
{ {
public class TestScenePlaylistsSongSelect : OnlinePlayTestScene public class TestScenePlaylistsSongSelect : OnlinePlayTestScene
{ {
[Resolved]
private BeatmapManager beatmapManager { get; set; }
private BeatmapManager manager; private BeatmapManager manager;
private RulesetStore rulesets; private RulesetStore rulesets;
@ -42,43 +36,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
var beatmaps = new List<BeatmapInfo>(); var beatmapSet = TestResources.CreateTestBeatmapSetInfo();
for (int i = 0; i < 6; i++) manager.Import(beatmapSet).Wait();
{
int beatmapId = 10 * 10 + i;
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
beatmaps.Add(new BeatmapInfo
{
Ruleset = new OsuRuleset().RulesetInfo,
OnlineID = beatmapId,
DifficultyName = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
Length = length,
BPM = bpm,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 3.5f,
},
});
}
manager.Import(new BeatmapSetInfo
{
OnlineID = 10,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = "Some Artist " + RNG.Next(0, 9),
Title = $"Some Song (set id 10), max bpm {beatmaps.Max(b => b.BPM):0.#})",
AuthorString = "Some Guy " + RNG.Next(0, 9),
},
Beatmaps = beatmaps,
DateAdded = DateTimeOffset.UtcNow,
}).Wait();
} }
public override void SetUpSteps() public override void SetUpSteps()
@ -94,7 +54,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
}); });
AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(SelectedRoom.Value))); AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(SelectedRoom.Value)));
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen()); AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
} }
[Test] [Test]

View File

@ -11,6 +11,7 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -118,6 +119,33 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("user still on team 0", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0); AddAssert("user still on team 0", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
} }
[Test]
public void TestSettingsUpdatedWhenChangingMatchType()
{
createRoom(() => new Room
{
Name = { Value = "Test Room" },
Type = { Value = MatchType.HeadToHead },
Playlist =
{
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
}
}
});
AddUntilStep("match type head to head", () => client.APIRoom?.Type.Value == MatchType.HeadToHead);
AddStep("change match type", () => client.ChangeSettings(new MultiplayerRoomSettings
{
MatchType = MatchType.TeamVersus
}));
AddUntilStep("api room updated to team versus", () => client.APIRoom?.Type.Value == MatchType.TeamVersus);
}
[Test] [Test]
public void TestChangeTypeViaMatchSettings() public void TestChangeTypeViaMatchSettings()
{ {
@ -152,6 +180,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true); AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
AddWaitStep("wait for transition", 2); AddWaitStep("wait for transition", 2);
AddUntilStep("create room button enabled", () => this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
AddStep("create room", () => AddStep("create room", () =>
{ {
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single()); InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single());

View File

@ -0,0 +1,39 @@
// 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.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Overlays.Settings.Sections;
using osu.Game.Skinning;
using osu.Game.Skinning.Editor;
namespace osu.Game.Tests.Visual.Navigation
{
public class TestSceneEditDefaultSkin : OsuGameTestScene
{
private SkinManager skinManager => Game.Dependencies.Get<SkinManager>();
private SkinEditorOverlay skinEditor => Game.Dependencies.Get<SkinEditorOverlay>();
[Test]
public void TestEditDefaultSkin()
{
AddAssert("is default skin", () => skinManager.CurrentSkinInfo.Value.ID == SkinInfo.DEFAULT_SKIN);
AddStep("open settings", () => { Game.Settings.Show(); });
// Until step requires as settings has a delayed load.
AddUntilStep("export button disabled", () => Game.Settings.ChildrenOfType<SkinSection.ExportSkinButton>().SingleOrDefault()?.Enabled.Value == false);
// Will create a mutable skin.
AddStep("open skin editor", () => skinEditor.Show());
// Until step required as the skin editor may take time to load (and an extra scheduled frame for the mutable part).
AddUntilStep("is modified default skin", () => skinManager.CurrentSkinInfo.Value.ID != SkinInfo.DEFAULT_SKIN);
AddAssert("is not protected", () => skinManager.CurrentSkinInfo.Value.PerformRead(s => !s.Protected));
AddUntilStep("export button enabled", () => Game.Settings.ChildrenOfType<SkinSection.ExportSkinButton>().SingleOrDefault()?.Enabled.Value == true);
}
}
}

View File

@ -0,0 +1,99 @@
// 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 NUnit.Framework;
using osu.Game.Configuration;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps.IO;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Navigation
{
public class TestSceneMouseWheelVolumeAdjust : OsuGameTestScene
{
public override void SetUpSteps()
{
base.SetUpSteps();
// Headless tests are always at minimum volume. This covers interactive tests, matching that initial value.
AddStep("Set volume to min", () => Game.Audio.Volume.Value = 0);
AddAssert("Volume is min", () => Game.Audio.AggregateVolume.Value == 0);
AddStep("Move mouse to centre", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre));
}
[Test]
public void TestAdjustVolumeFromMainMenu()
{
// First scroll makes volume controls appear, second adjusts volume.
AddRepeatStep("Adjust volume using mouse wheel", () => InputManager.ScrollVerticalBy(5), 2);
AddUntilStep("Volume is above zero", () => Game.Audio.AggregateVolume.Value > 0);
}
[Test]
public void TestAdjustVolumeFromPlayerWheelEnabled()
{
loadToPlayerNonBreakTime();
// First scroll makes volume controls appear, second adjusts volume.
AddRepeatStep("Adjust volume using mouse wheel", () => InputManager.ScrollVerticalBy(5), 2);
AddAssert("Volume is above zero", () => Game.Audio.Volume.Value > 0);
}
[Test]
public void TestAdjustVolumeFromPlayerWheelDisabled()
{
AddStep("disable wheel volume adjust", () => Game.LocalConfig.SetValue(OsuSetting.MouseDisableWheel, true));
loadToPlayerNonBreakTime();
// First scroll makes volume controls appear, second adjusts volume.
AddRepeatStep("Adjust volume using mouse wheel", () => InputManager.ScrollVerticalBy(5), 2);
AddAssert("Volume is still zero", () => Game.Audio.Volume.Value == 0);
}
[Test]
public void TestAdjustVolumeFromPlayerWheelDisabledHoldingAlt()
{
AddStep("disable wheel volume adjust", () => Game.LocalConfig.SetValue(OsuSetting.MouseDisableWheel, true));
loadToPlayerNonBreakTime();
// First scroll makes volume controls appear, second adjusts volume.
AddRepeatStep("Adjust volume using mouse wheel holding alt", () =>
{
InputManager.PressKey(Key.AltLeft);
InputManager.ScrollVerticalBy(5);
InputManager.ReleaseKey(Key.AltLeft);
}, 2);
AddAssert("Volume is above zero", () => Game.Audio.Volume.Value > 0);
}
private void loadToPlayerNonBreakTime()
{
Player player = null;
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestSceneScreenNavigation.TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
AddStep("press enter", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
});
AddUntilStep("wait for play time active", () => !player.IsBreakTime.Value);
}
private void clickMouseInCentre()
{
InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre);
InputManager.Click(MouseButton.Left);
}
}
}

View File

@ -83,9 +83,6 @@ namespace osu.Game.Tests.Visual.Navigation
[Resolved] [Resolved]
private OsuGameBase gameBase { get; set; } private OsuGameBase gameBase { get; set; }
[Resolved]
private GameHost host { get; set; }
[Test] [Test]
public void TestNullRulesetHandled() public void TestNullRulesetHandled()
{ {

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Screens; using osu.Framework.Screens;
@ -110,7 +109,7 @@ namespace osu.Game.Tests.Visual.Navigation
Hash = Guid.NewGuid().ToString(), Hash = Guid.NewGuid().ToString(),
OnlineID = i, OnlineID = i,
Metadata = metadata, Metadata = metadata,
Beatmaps = new List<BeatmapInfo> Beatmaps =
{ {
new BeatmapInfo new BeatmapInfo
{ {
@ -141,7 +140,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect); AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect);
AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapSetInfo.MatchesOnlineID(getImport())); AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapSetInfo.MatchesOnlineID(getImport()));
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID); AddAssert("correct ruleset selected", () => Game.Ruleset.Value.Equals(getImport().Beatmaps.First().Ruleset));
} }
private void presentSecondDifficultyAndConfirm(Func<BeatmapSetInfo> getImport, int importedID) private void presentSecondDifficultyAndConfirm(Func<BeatmapSetInfo> getImport, int importedID)
@ -151,7 +150,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect); AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect);
AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineID == importedID * 2048); AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineID == importedID * 2048);
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID); AddAssert("correct ruleset selected", () => Game.Ruleset.Value.Equals(getImport().Beatmaps.First().Ruleset));
} }
} }
} }

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Screens; using osu.Framework.Screens;
@ -41,7 +40,7 @@ namespace osu.Game.Tests.Visual.Navigation
Hash = Guid.NewGuid().ToString(), Hash = Guid.NewGuid().ToString(),
OnlineID = 1, OnlineID = 1,
Metadata = metadata, Metadata = metadata,
Beatmaps = new List<BeatmapInfo> Beatmaps =
{ {
new BeatmapInfo new BeatmapInfo
{ {
@ -155,15 +154,15 @@ namespace osu.Game.Tests.Visual.Navigation
case ScorePresentType.Results: case ScorePresentType.Results:
AddUntilStep("wait for results", () => lastWaitedScreen != Game.ScreenStack.CurrentScreen && Game.ScreenStack.CurrentScreen is ResultsScreen); AddUntilStep("wait for results", () => lastWaitedScreen != Game.ScreenStack.CurrentScreen && Game.ScreenStack.CurrentScreen is ResultsScreen);
AddStep("store last waited screen", () => lastWaitedScreen = Game.ScreenStack.CurrentScreen); AddStep("store last waited screen", () => lastWaitedScreen = Game.ScreenStack.CurrentScreen);
AddUntilStep("correct score displayed", () => ((ResultsScreen)Game.ScreenStack.CurrentScreen).Score.ID == getImport().ID); AddUntilStep("correct score displayed", () => ((ResultsScreen)Game.ScreenStack.CurrentScreen).Score.Equals(getImport()));
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Ruleset.ID); AddAssert("correct ruleset selected", () => Game.Ruleset.Value.Equals(getImport().Ruleset));
break; break;
case ScorePresentType.Gameplay: case ScorePresentType.Gameplay:
AddUntilStep("wait for player loader", () => lastWaitedScreen != Game.ScreenStack.CurrentScreen && Game.ScreenStack.CurrentScreen is ReplayPlayerLoader); AddUntilStep("wait for player loader", () => lastWaitedScreen != Game.ScreenStack.CurrentScreen && Game.ScreenStack.CurrentScreen is ReplayPlayerLoader);
AddStep("store last waited screen", () => lastWaitedScreen = Game.ScreenStack.CurrentScreen); AddStep("store last waited screen", () => lastWaitedScreen = Game.ScreenStack.CurrentScreen);
AddUntilStep("correct score displayed", () => ((ReplayPlayerLoader)Game.ScreenStack.CurrentScreen).Score.ID == getImport().ID); AddUntilStep("correct score displayed", () => ((ReplayPlayerLoader)Game.ScreenStack.CurrentScreen).Score.Equals(getImport()));
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Ruleset.ID); AddAssert("correct ruleset selected", () => Game.Ruleset.Value.Equals(getImport().Ruleset));
break; break;
} }
} }

View File

@ -66,7 +66,9 @@ namespace osu.Game.Tests.Visual.Navigation
{ {
Player player = null; Player player = null;
PushAndConfirm(() => new TestPlaySongSelect()); Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait());
@ -98,7 +100,9 @@ namespace osu.Game.Tests.Visual.Navigation
IWorkingBeatmap beatmap() => Game.Beatmap.Value; IWorkingBeatmap beatmap() => Game.Beatmap.Value;
PushAndConfirm(() => new TestPlaySongSelect()); Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait());
@ -130,7 +134,9 @@ namespace osu.Game.Tests.Visual.Navigation
IWorkingBeatmap beatmap() => Game.Beatmap.Value; IWorkingBeatmap beatmap() => Game.Beatmap.Value;
PushAndConfirm(() => new TestPlaySongSelect()); Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait()); AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait());
@ -257,7 +263,7 @@ namespace osu.Game.Tests.Visual.Navigation
InputManager.ReleaseKey(Key.ControlLeft); InputManager.ReleaseKey(Key.ControlLeft);
}); });
AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.ID == 1); AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.OnlineID == 1);
AddAssert("Mods overlay still visible", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); AddAssert("Mods overlay still visible", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
} }
@ -278,7 +284,7 @@ namespace osu.Game.Tests.Visual.Navigation
InputManager.ReleaseKey(Key.ControlLeft); InputManager.ReleaseKey(Key.ControlLeft);
}); });
AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.ID == 1); AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.OnlineID == 1);
AddAssert("Options overlay still visible", () => songSelect.BeatmapOptionsOverlay.State.Value == Visibility.Visible); AddAssert("Options overlay still visible", () => songSelect.BeatmapOptionsOverlay.State.Value == Visibility.Visible);
} }

View File

@ -6,15 +6,15 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Online; using osu.Game.Online;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.BeatmapListing.Panels;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
public class TestSceneDirectDownloadButton : OsuTestScene public class TestSceneBeatmapDownloadButton : OsuTestScene
{ {
private TestDownloadButton downloadButton; private TestDownloadButton downloadButton;
@ -137,7 +137,7 @@ namespace osu.Game.Tests.Visual.Online
return apiBeatmapSet; return apiBeatmapSet;
} }
private class TestDownloadButton : BeatmapPanelDownloadButton private class TestDownloadButton : BeatmapDownloadButton
{ {
public new bool DownloadEnabled => base.DownloadEnabled; public new bool DownloadEnabled => base.DownloadEnabled;

View File

@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Online
} }
[Resolved] [Resolved]
private RulesetStore rulesets { get; set; } private IRulesetStore rulesets { get; set; }
[Test] [Test]
public void TestMultipleRulesetsBeatmapSet() public void TestMultipleRulesetsBeatmapSet()

View File

@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Online
} }
[Resolved] [Resolved]
private RulesetStore rulesets { get; set; } private IRulesetStore rulesets { get; set; }
[Test] [Test]
public void TestLoading() public void TestLoading()
@ -200,7 +200,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
OnlineID = i * 10, OnlineID = i * 10,
DifficultyName = $"Test #{i}", DifficultyName = $"Test #{i}",
RulesetID = Ruleset.Value.ID ?? -1, RulesetID = Ruleset.Value.OnlineID,
StarRating = 2 + i * 0.1, StarRating = 2 + i * 0.1,
OverallDifficulty = 3.5f, OverallDifficulty = 3.5f,
FailTimes = new APIFailTimes FailTimes = new APIFailTimes

View File

@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Online
} }
}, },
Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray(), Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray(),
Status = BeatmapSetOnlineStatus.Ranked Status = BeatmapOnlineStatus.Ranked
}; };
} }

View File

@ -13,7 +13,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Platform;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API; using osu.Game.Online.API;
@ -47,9 +46,6 @@ namespace osu.Game.Tests.Visual.Online
[CanBeNull] [CanBeNull]
private Func<Channel, List<Message>> onGetMessages; private Func<Channel, List<Message>> onGetMessages;
[Resolved]
private GameHost host { get; set; }
public TestSceneChatOverlay() public TestSceneChatOverlay()
{ {
channels = Enumerable.Range(1, 10) channels = Enumerable.Range(1, 10)

View File

@ -1,137 +0,0 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.BeatmapListing.Panels;
using osu.Game.Rulesets;
using osuTK;
using APIUser = osu.Game.Online.API.Requests.Responses.APIUser;
namespace osu.Game.Tests.Visual.Online
{
[Cached(typeof(IPreviewTrackOwner))]
public class TestSceneDirectPanel : OsuTestScene, IPreviewTrackOwner
{
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
var normal = getBeatmapSet();
normal.HasVideo = true;
normal.HasStoryboard = true;
var undownloadable = getUndownloadableBeatmapSet();
var manyDifficulties = getManyDifficultiesBeatmapSet();
var explicitMap = getBeatmapSet();
explicitMap.HasExplicitContent = true;
var featuredMap = getBeatmapSet();
featuredMap.TrackId = 1;
var explicitFeaturedMap = getBeatmapSet();
explicitFeaturedMap.HasExplicitContent = true;
explicitFeaturedMap.TrackId = 2;
Child = new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
Padding = new MarginPadding(20),
Spacing = new Vector2(5, 20),
Children = new Drawable[]
{
new GridBeatmapPanel(normal),
new GridBeatmapPanel(undownloadable),
new GridBeatmapPanel(manyDifficulties),
new GridBeatmapPanel(explicitMap),
new GridBeatmapPanel(featuredMap),
new GridBeatmapPanel(explicitFeaturedMap),
new ListBeatmapPanel(normal),
new ListBeatmapPanel(undownloadable),
new ListBeatmapPanel(manyDifficulties),
new ListBeatmapPanel(explicitMap),
new ListBeatmapPanel(featuredMap),
new ListBeatmapPanel(explicitFeaturedMap)
},
},
};
APIBeatmapSet getBeatmapSet() => CreateAPIBeatmapSet(Ruleset.Value);
APIBeatmapSet getUndownloadableBeatmapSet() => new APIBeatmapSet
{
OnlineID = 123,
Title = "undownloadable beatmap",
Artist = "test",
Source = "more tests",
Author = new APIUser
{
Username = "BanchoBot",
Id = 3,
},
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = true,
},
Preview = @"https://b.ppy.sh/preview/12345.mp3",
PlayCount = 123,
FavouriteCount = 456,
BPM = 111,
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
Beatmaps = new[]
{
new APIBeatmap
{
RulesetID = Ruleset.Value.ID ?? 0,
DifficultyName = "Test",
StarRating = 6.42,
}
}
};
APIBeatmapSet getManyDifficultiesBeatmapSet()
{
var beatmaps = new List<APIBeatmap>();
for (int i = 0; i < 100; i++)
{
beatmaps.Add(new APIBeatmap
{
RulesetID = i % 4,
StarRating = 2 + i % 4 * 2,
OverallDifficulty = 3.5f,
});
}
return new APIBeatmapSet
{
OnlineID = 1,
Title = "undownloadable beatmap",
Artist = "test",
Source = "more tests",
Author = new APIUser
{
Username = "BanchoBot",
Id = 3,
},
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
Beatmaps = beatmaps.ToArray(),
};
}
}
}
}

View File

@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Online
AddStep("Set beatmap", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null) AddStep("Set beatmap", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null)
{ {
BeatmapInfo = { OnlineID = hasOnlineId ? 1234 : (int?)null } BeatmapInfo = { OnlineID = hasOnlineId ? 1234 : -1 }
}); });
AddStep("Run command", () => Add(new NowPlayingCommand())); AddStep("Run command", () => Add(new NowPlayingCommand()));

View File

@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online
private TestUserListPanel evast; private TestUserListPanel evast;
[Resolved] [Resolved]
private RulesetStore rulesetStore { get; set; } private IRulesetStore rulesetStore { get; set; }
[SetUp] [SetUp]
public void SetUp() => Schedule(() => public void SetUp() => Schedule(() =>

View File

@ -4,8 +4,6 @@
using System; using System;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile;
@ -20,9 +18,6 @@ namespace osu.Game.Tests.Visual.Online
private readonly TestUserProfileOverlay profile; private readonly TestUserProfileOverlay profile;
[Resolved]
private IAPIProvider api { get; set; }
public static readonly APIUser TEST_USER = new APIUser public static readonly APIUser TEST_USER = new APIUser
{ {
Username = @"Somebody", Username = @"Somebody",

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
@ -20,9 +19,6 @@ namespace osu.Game.Tests.Visual.Ranking
{ {
public class TestSceneContractedPanelMiddleContent : OsuTestScene public class TestSceneContractedPanelMiddleContent : OsuTestScene
{ {
[Resolved]
private RulesetStore rulesetStore { get; set; }
[Test] [Test]
public void TestShowPanel() public void TestShowPanel()
{ {

View File

@ -338,7 +338,7 @@ namespace osu.Game.Tests.Visual.Ranking
: base(score, true) : base(score, true)
{ {
Score.BeatmapInfo.OnlineID = 0; Score.BeatmapInfo.OnlineID = 0;
Score.BeatmapInfo.Status = BeatmapSetOnlineStatus.Pending; Score.BeatmapInfo.Status = BeatmapOnlineStatus.Pending;
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -3,15 +3,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -19,6 +15,7 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter; using osu.Game.Screens.Select.Filter;
using osu.Game.Tests.Resources;
using osuTK.Input; using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect namespace osu.Game.Tests.Visual.SongSelect
@ -152,7 +149,7 @@ namespace osu.Game.Tests.Visual.SongSelect
const int total_set_count = 200; const int total_set_count = 200;
for (int i = 0; i < total_set_count; i++) for (int i = 0; i < total_set_count; i++)
sets.Add(createTestBeatmapSet(i + 1)); sets.Add(TestResources.CreateTestBeatmapSetInfo());
loadBeatmaps(sets); loadBeatmaps(sets);
@ -183,7 +180,7 @@ namespace osu.Game.Tests.Visual.SongSelect
const int total_set_count = 20; const int total_set_count = 20;
for (int i = 0; i < total_set_count; i++) for (int i = 0; i < total_set_count; i++)
sets.Add(createTestBeatmapSet(i + 1)); sets.Add(TestResources.CreateTestBeatmapSetInfo(3));
loadBeatmaps(sets); loadBeatmaps(sets);
@ -228,10 +225,9 @@ namespace osu.Game.Tests.Visual.SongSelect
loadBeatmaps(); loadBeatmaps();
// basic filtering // basic filtering
setSelected(1, 1); setSelected(1, 1);
AddStep("Filter", () => carousel.Filter(new FilterCriteria { SearchText = "set #3!" }, false)); AddStep("Filter", () => carousel.Filter(new FilterCriteria { SearchText = carousel.BeatmapSets.ElementAt(2).Metadata.Title }, false));
checkVisibleItemCount(diff: false, count: 1); checkVisibleItemCount(diff: false, count: 1);
checkVisibleItemCount(diff: true, count: 3); checkVisibleItemCount(diff: true, count: 3);
waitForSelection(3, 1); waitForSelection(3, 1);
@ -275,16 +271,20 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test] [Test]
public void TestFilterRange() public void TestFilterRange()
{ {
string searchText = null;
loadBeatmaps(); loadBeatmaps();
// buffer the selection // buffer the selection
setSelected(3, 2); setSelected(3, 2);
AddStep("get search text", () => searchText = carousel.SelectedBeatmapSet.Metadata.Title);
setSelected(1, 3); setSelected(1, 3);
AddStep("Apply a range filter", () => carousel.Filter(new FilterCriteria AddStep("Apply a range filter", () => carousel.Filter(new FilterCriteria
{ {
SearchText = "#3", SearchText = searchText,
StarDifficulty = new FilterCriteria.OptionalRange<double> StarDifficulty = new FilterCriteria.OptionalRange<double>
{ {
Min = 2, Min = 2,
@ -327,7 +327,7 @@ namespace osu.Game.Tests.Visual.SongSelect
nextRandom(); nextRandom();
AddAssert("ensure repeat", () => selectedSets.Contains(carousel.SelectedBeatmapSet)); AddAssert("ensure repeat", () => selectedSets.Contains(carousel.SelectedBeatmapSet));
AddStep("Add set with 100 difficulties", () => carousel.UpdateBeatmapSet(createTestBeatmapSetWithManyDifficulties(set_count + 1))); AddStep("Add set with 100 difficulties", () => carousel.UpdateBeatmapSet(TestResources.CreateTestBeatmapSetInfo(100, rulesets.AvailableRulesets.ToArray())));
AddStep("Filter Extra", () => carousel.Filter(new FilterCriteria { SearchText = "Extra 10" }, false)); AddStep("Filter Extra", () => carousel.Filter(new FilterCriteria { SearchText = "Extra 10" }, false));
checkInvisibleDifficultiesUnselectable(); checkInvisibleDifficultiesUnselectable();
checkInvisibleDifficultiesUnselectable(); checkInvisibleDifficultiesUnselectable();
@ -345,18 +345,21 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
loadBeatmaps(); loadBeatmaps();
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 1))); var firstAdded = TestResources.CreateTestBeatmapSetInfo();
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 2))); var secondAdded = TestResources.CreateTestBeatmapSetInfo();
AddStep("Add new set", () => carousel.UpdateBeatmapSet(firstAdded));
AddStep("Add new set", () => carousel.UpdateBeatmapSet(secondAdded));
checkVisibleItemCount(false, set_count + 2); checkVisibleItemCount(false, set_count + 2);
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 2))); AddStep("Remove set", () => carousel.RemoveBeatmapSet(firstAdded));
checkVisibleItemCount(false, set_count + 1); checkVisibleItemCount(false, set_count + 1);
setSelected(set_count + 1, 1); setSelected(set_count + 1, 1);
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 1))); AddStep("Remove set", () => carousel.RemoveBeatmapSet(secondAdded));
checkVisibleItemCount(false, set_count); checkVisibleItemCount(false, set_count);
@ -372,7 +375,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
sets.Clear(); sets.Clear();
var rulesetBeatmapSet = createTestBeatmapSet(1); var rulesetBeatmapSet = TestResources.CreateTestBeatmapSetInfo(1);
var taikoRuleset = rulesets.AvailableRulesets.ElementAt(1); var taikoRuleset = rulesets.AvailableRulesets.ElementAt(1);
rulesetBeatmapSet.Beatmaps.ForEach(b => rulesetBeatmapSet.Beatmaps.ForEach(b =>
{ {
@ -397,12 +400,29 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test] [Test]
public void TestSorting() public void TestSorting()
{ {
loadBeatmaps(); var sets = new List<BeatmapSetInfo>();
const string zzz_string = "zzzzz";
for (int i = 0; i < 20; i++)
{
var set = TestResources.CreateTestBeatmapSetInfo();
if (i == 4)
set.Metadata.Artist = zzz_string;
if (i == 16)
set.Metadata.AuthorString = zzz_string;
sets.Add(set);
}
loadBeatmaps(sets);
AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false)); AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false));
AddAssert("Check zzzzz is at bottom", () => carousel.BeatmapSets.Last().Metadata.Author.Username == "zzzzz"); AddAssert($"Check {zzz_string} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Author.Username == zzz_string);
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false)); AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!", StringComparison.Ordinal)); AddAssert($"Check {zzz_string} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Artist == zzz_string);
} }
[Test] [Test]
@ -412,20 +432,21 @@ namespace osu.Game.Tests.Visual.SongSelect
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{ {
// index + 1 because we are using OnlineID which should never be zero. var set = TestResources.CreateTestBeatmapSetInfo();
var set = createTestBeatmapSet(i + 1);
set.Metadata.Artist = "same artist"; set.Metadata.Artist = "same artist";
set.Metadata.Title = "same title"; set.Metadata.Title = "same title";
sets.Add(set); sets.Add(set);
} }
int idOffset = sets.First().OnlineID ?? 0;
loadBeatmaps(sets); loadBeatmaps(sets);
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false)); AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == index + 1).All(b => b)); AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == index + idOffset).All(b => b));
AddStep("Sort by title", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false)); AddStep("Sort by title", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false));
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == index + 1).All(b => b)); AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == index + idOffset).All(b => b));
} }
[Test] [Test]
@ -435,7 +456,7 @@ namespace osu.Game.Tests.Visual.SongSelect
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
var set = createTestBeatmapSet(i); var set = TestResources.CreateTestBeatmapSetInfo(3);
set.Beatmaps[0].StarRating = 3 - i; set.Beatmaps[0].StarRating = 3 - i;
set.Beatmaps[2].StarRating = 6 + i; set.Beatmaps[2].StarRating = 6 + i;
sets.Add(set); sets.Add(set);
@ -504,7 +525,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("create hidden set", () => AddStep("create hidden set", () =>
{ {
hidingSet = createTestBeatmapSet(1); hidingSet = TestResources.CreateTestBeatmapSetInfo(3);
hidingSet.Beatmaps[1].Hidden = true; hidingSet.Beatmaps[1].Hidden = true;
hiddenList.Clear(); hiddenList.Clear();
@ -551,7 +572,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("add mixed ruleset beatmapset", () => AddStep("add mixed ruleset beatmapset", () =>
{ {
testMixed = createTestBeatmapSet(set_count + 1); testMixed = TestResources.CreateTestBeatmapSetInfo(3);
for (int i = 0; i <= 2; i++) for (int i = 0; i <= 2; i++)
{ {
@ -574,7 +595,7 @@ namespace osu.Game.Tests.Visual.SongSelect
BeatmapSetInfo testSingle = null; BeatmapSetInfo testSingle = null;
AddStep("add single ruleset beatmapset", () => AddStep("add single ruleset beatmapset", () =>
{ {
testSingle = createTestBeatmapSet(set_count + 2); testSingle = TestResources.CreateTestBeatmapSetInfo(3);
testSingle.Beatmaps.ForEach(b => testSingle.Beatmaps.ForEach(b =>
{ {
b.Ruleset = rulesets.AvailableRulesets.ElementAt(1); b.Ruleset = rulesets.AvailableRulesets.ElementAt(1);
@ -594,7 +615,7 @@ namespace osu.Game.Tests.Visual.SongSelect
List<BeatmapSetInfo> manySets = new List<BeatmapSetInfo>(); List<BeatmapSetInfo> manySets = new List<BeatmapSetInfo>();
for (int i = 1; i <= 50; i++) for (int i = 1; i <= 50; i++)
manySets.Add(createTestBeatmapSet(i)); manySets.Add(TestResources.CreateTestBeatmapSetInfo(3));
loadBeatmaps(manySets); loadBeatmaps(manySets);
@ -627,18 +648,11 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
var set = createTestBeatmapSet(i); manySets.Add(TestResources.CreateTestBeatmapSetInfo(3, new[]
foreach (var b in set.Beatmaps)
{ {
// all taiko except for first // all taiko except for first
int ruleset = i > 0 ? 1 : 0; rulesets.GetRuleset(i > 0 ? 1 : 0)
}));
b.Ruleset = rulesets.GetRuleset(ruleset);
b.RulesetID = ruleset;
}
manySets.Add(set);
} }
}); });
@ -660,7 +674,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("Restore different ruleset filter", () => AddStep("Restore different ruleset filter", () =>
{ {
carousel.Filter(new FilterCriteria { Ruleset = rulesets.GetRuleset(1) }, false); carousel.Filter(new FilterCriteria { Ruleset = rulesets.GetRuleset(1) }, false);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID); eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.OnlineID ?? -1);
}); });
AddAssert("selection changed", () => !carousel.SelectedBeatmapInfo.Equals(manySets.First().Beatmaps.First())); AddAssert("selection changed", () => !carousel.SelectedBeatmapInfo.Equals(manySets.First().Beatmaps.First()));
@ -678,7 +692,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("add mixed difficulty set", () => AddStep("add mixed difficulty set", () =>
{ {
set = createTestBeatmapSet(1); set = TestResources.CreateTestBeatmapSetInfo(1);
set.Beatmaps.Clear(); set.Beatmaps.Clear();
for (int i = 1; i <= 15; i++) for (int i = 1; i <= 15; i++)
@ -730,7 +744,11 @@ namespace osu.Game.Tests.Visual.SongSelect
beatmapSets = new List<BeatmapSetInfo>(); beatmapSets = new List<BeatmapSetInfo>();
for (int i = 1; i <= (count ?? set_count); i++) for (int i = 1; i <= (count ?? set_count); i++)
beatmapSets.Add(createTestBeatmapSet(i, randomDifficulties)); {
beatmapSets.Add(randomDifficulties
? TestResources.CreateTestBeatmapSetInfo()
: TestResources.CreateTestBeatmapSetInfo(3));
}
} }
carousel.Filter(initialCriteria?.Invoke() ?? new FilterCriteria()); carousel.Filter(initialCriteria?.Invoke() ?? new FilterCriteria());
@ -766,7 +784,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () => AddUntilStep($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () =>
{ {
if (diff != null) if (diff != null)
return carousel.SelectedBeatmapInfo.Equals(carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First()); return carousel.SelectedBeatmapInfo?.Equals(carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First()) == true;
return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmapInfo); return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmapInfo);
}); });
@ -834,88 +852,6 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("Selection is visible", selectedBeatmapVisible); AddAssert("Selection is visible", selectedBeatmapVisible);
} }
private BeatmapSetInfo createTestBeatmapSet(int id, bool randomDifficultyCount = false)
{
return new BeatmapSetInfo
{
ID = id,
OnlineID = id,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = $"peppy{id.ToString().PadLeft(6, '0')}",
Title = $"test set #{id}!",
AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, id - 1)), 5))
},
Beatmaps = getBeatmaps(randomDifficultyCount ? RNG.Next(1, 20) : 3).ToList()
};
}
private IEnumerable<BeatmapInfo> getBeatmaps(int count)
{
int id = 0;
for (int i = 0; i < count; i++)
{
float diff = (float)i / count * 10;
string version = "Normal";
if (diff > 6.6)
version = "Insane";
else if (diff > 3.3)
version = "Hard";
yield return new BeatmapInfo
{
OnlineID = id++ * 10,
DifficultyName = version,
StarRating = diff,
Ruleset = new OsuRuleset().RulesetInfo,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = diff,
}
};
}
}
private BeatmapSetInfo createTestBeatmapSetWithManyDifficulties(int id)
{
var toReturn = new BeatmapSetInfo
{
ID = id,
OnlineID = id,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = $"peppy{id.ToString().PadLeft(6, '0')}",
Title = $"test set #{id}!",
AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, id - 1)), 5))
},
Beatmaps = new List<BeatmapInfo>(),
};
for (int b = 1; b < 101; b++)
{
toReturn.Beatmaps.Add(new BeatmapInfo
{
OnlineID = b * 10,
Path = $"extra{b}.osu",
DifficultyName = $"Extra {b}",
Ruleset = rulesets.GetRuleset((b - 1) % 4),
StarRating = 2,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 3.5f,
}
});
}
return toReturn;
}
private class TestBeatmapCarousel : BeatmapCarousel private class TestBeatmapCarousel : BeatmapCarousel
{ {
public bool PendingFilterTask => PendingFilter != null; public bool PendingFilterTask => PendingFilter != null;

View File

@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.SongSelect
beatmaps.Add(testBeatmap); beatmaps.Add(testBeatmap);
AddStep("set ruleset", () => Ruleset.Value = rulesetInfo); setRuleset(rulesetInfo);
selectBeatmap(testBeatmap); selectBeatmap(testBeatmap);
@ -167,6 +167,22 @@ namespace osu.Game.Tests.Visual.SongSelect
label => label.Statistic.Name == "BPM" && label.Statistic.Content == target.ToString(CultureInfo.InvariantCulture))); label => label.Statistic.Name == "BPM" && label.Statistic.Content == target.ToString(CultureInfo.InvariantCulture)));
} }
private void setRuleset(RulesetInfo rulesetInfo)
{
Container containerBefore = null;
AddStep("set ruleset", () =>
{
// wedge content is only refreshed if the ruleset changes, so only wait for load in that case.
if (!rulesetInfo.Equals(Ruleset.Value))
containerBefore = infoWedge.DisplayedContent;
Ruleset.Value = rulesetInfo;
});
AddUntilStep("wait for async load", () => infoWedge.DisplayedContent != containerBefore);
}
private void selectBeatmap([CanBeNull] IBeatmap b) private void selectBeatmap([CanBeNull] IBeatmap b)
{ {
Container containerBefore = null; Container containerBefore = null;
@ -220,7 +236,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Title = "Verrrrry long Title" Title = "Verrrrry long Title"
}, },
DifficultyName = "Verrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Version", DifficultyName = "Verrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Version",
Status = BeatmapSetOnlineStatus.Graveyard, Status = BeatmapOnlineStatus.Graveyard,
}, },
}; };
} }

View File

@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.SongSelect
dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory)); dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory));
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default)); dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory, Scheduler)); dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, ContextFactory, Scheduler));
return dependencies; return dependencies;
} }
@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test] [Test]
public void TestBeatmapStates() public void TestBeatmapStates()
{ {
foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus))) foreach (BeatmapOnlineStatus status in Enum.GetValues(typeof(BeatmapOnlineStatus)))
AddStep($"{status} beatmap", () => showBeatmapWithStatus(status)); AddStep($"{status} beatmap", () => showBeatmapWithStatus(status));
} }
@ -384,7 +384,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}; };
} }
private void showBeatmapWithStatus(BeatmapSetOnlineStatus status) private void showBeatmapWithStatus(BeatmapOnlineStatus status)
{ {
leaderboard.BeatmapInfo = new BeatmapInfo leaderboard.BeatmapInfo = new BeatmapInfo
{ {

View File

@ -16,6 +16,7 @@ using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Taiko;
using osu.Game.Tests.Resources;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Tests.Visual.SongSelect namespace osu.Game.Tests.Visual.SongSelect
@ -32,7 +33,7 @@ namespace osu.Game.Tests.Visual.SongSelect
switch (req) switch (req)
{ {
case GetUserRequest userRequest: case GetUserRequest userRequest:
userRequest.TriggerSuccess(getUser(userRequest.Ruleset.ID)); userRequest.TriggerSuccess(getUser(userRequest.Ruleset.OnlineID));
return true; return true;
} }
@ -89,7 +90,7 @@ namespace osu.Game.Tests.Visual.SongSelect
for (int i = 0; i < import_count; ++i) for (int i = 0; i < import_count; ++i)
{ {
beatmapSets.Add(importBeatmapSet(i, Enumerable.Repeat(new OsuRuleset().RulesetInfo, 5))); beatmapSets.Add(importBeatmapSet(Enumerable.Repeat(new OsuRuleset().RulesetInfo, 5)));
} }
}); });
@ -103,9 +104,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
BeatmapSetInfo catchSet = null, mixedSet = null; BeatmapSetInfo catchSet = null, mixedSet = null;
AddStep("create catch beatmapset", () => catchSet = importBeatmapSet(1, new[] { new CatchRuleset().RulesetInfo })); AddStep("create catch beatmapset", () => catchSet = importBeatmapSet(new[] { new CatchRuleset().RulesetInfo }));
AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(2, AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo }));
new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo }));
AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { catchSet, mixedSet })); AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { catchSet, mixedSet }));
@ -121,9 +121,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
BeatmapSetInfo osuSet = null, mixedSet = null; BeatmapSetInfo osuSet = null, mixedSet = null;
AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(1, new[] { new OsuRuleset().RulesetInfo })); AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(new[] { new OsuRuleset().RulesetInfo }));
AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(2, AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo }));
new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo }));
AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, mixedSet })); AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, mixedSet }));
@ -139,9 +138,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
BeatmapSetInfo osuSet = null, mixedSet = null; BeatmapSetInfo osuSet = null, mixedSet = null;
AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(1, new[] { new OsuRuleset().RulesetInfo })); AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(new[] { new OsuRuleset().RulesetInfo }));
AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(2, AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new TaikoRuleset().RulesetInfo }));
new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new TaikoRuleset().RulesetInfo }));
AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, mixedSet })); AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, mixedSet }));
@ -157,8 +155,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
BeatmapSetInfo osuSet = null, maniaSet = null; BeatmapSetInfo osuSet = null, maniaSet = null;
AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(1, new[] { new OsuRuleset().RulesetInfo })); AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(new[] { new OsuRuleset().RulesetInfo }));
AddStep("create mania beatmapset", () => maniaSet = importBeatmapSet(2, Enumerable.Repeat(new ManiaRuleset().RulesetInfo, 10))); AddStep("create mania beatmapset", () => maniaSet = importBeatmapSet(Enumerable.Repeat(new ManiaRuleset().RulesetInfo, 10)));
AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, maniaSet })); AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, maniaSet }));
@ -169,30 +167,19 @@ namespace osu.Game.Tests.Visual.SongSelect
presentAndConfirm(() => maniaSet, 5); presentAndConfirm(() => maniaSet, 5);
} }
private BeatmapSetInfo importBeatmapSet(int importID, IEnumerable<RulesetInfo> difficultyRulesets) private BeatmapSetInfo importBeatmapSet(IEnumerable<RulesetInfo> difficultyRulesets)
{ {
var metadata = new BeatmapMetadata var rulesets = difficultyRulesets.ToArray();
{
Artist = "SomeArtist",
AuthorString = "SomeAuthor",
Title = $"import {importID}"
};
var beatmapSet = new BeatmapSetInfo var beatmapSet = TestResources.CreateTestBeatmapSetInfo(rulesets.Length, rulesets);
for (int i = 0; i < rulesets.Length; i++)
{ {
Hash = Guid.NewGuid().ToString(), var beatmap = beatmapSet.Beatmaps[i];
OnlineID = importID,
Metadata = metadata, beatmap.StarRating = i + 1;
Beatmaps = difficultyRulesets.Select((ruleset, difficultyIndex) => new BeatmapInfo beatmap.DifficultyName = $"SR{i + 1}";
{ }
OnlineID = importID * 1024 + difficultyIndex,
Metadata = metadata,
BaseDifficulty = new BeatmapDifficulty(),
Ruleset = ruleset,
StarRating = difficultyIndex + 1,
DifficultyName = $"SR{difficultyIndex + 1}"
}).ToList()
};
return Game.BeatmapManager.Import(beatmapSet).Result.Value; return Game.BeatmapManager.Import(beatmapSet).Result.Value;
} }

View File

@ -3,15 +3,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Utils;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
@ -31,6 +27,7 @@ using osu.Game.Screens.Play;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter; using osu.Game.Screens.Select.Filter;
using osu.Game.Tests.Resources;
using osuTK.Input; using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect namespace osu.Game.Tests.Visual.SongSelect
@ -258,8 +255,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
AddStep("import multi-ruleset map", () => AddStep("import multi-ruleset map", () =>
{ {
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray(); var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait(); manager.Import(TestResources.CreateTestBeatmapSetInfo(rulesets: usableRulesets)).Wait();
}); });
} }
else else
@ -354,7 +351,7 @@ namespace osu.Game.Tests.Visual.SongSelect
target = manager.GetAllUsableBeatmapSets() target = manager.GetAllUsableBeatmapSets()
.Last(b => b.Beatmaps.Any(bi => bi.RulesetID == 0)).Beatmaps.Last(); .Last(b => b.Beatmaps.Any(bi => bi.RulesetID == 0)).Beatmaps.Last();
Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == 0); Ruleset.Value = rulesets.AvailableRulesets.First(r => r.OnlineID == 0);
Beatmap.Value = manager.GetWorkingBeatmap(target); Beatmap.Value = manager.GetWorkingBeatmap(target);
}); });
@ -385,12 +382,12 @@ namespace osu.Game.Tests.Visual.SongSelect
.Last(b => b.Beatmaps.Any(bi => bi.RulesetID == 0)).Beatmaps.Last(); .Last(b => b.Beatmaps.Any(bi => bi.RulesetID == 0)).Beatmaps.Last();
Beatmap.Value = manager.GetWorkingBeatmap(target); Beatmap.Value = manager.GetWorkingBeatmap(target);
Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == 0); Ruleset.Value = rulesets.AvailableRulesets.First(r => r.OnlineID == 0);
}); });
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.Equals(target)); AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.Equals(target));
AddUntilStep("has correct ruleset", () => Ruleset.Value.ID == 0); AddUntilStep("has correct ruleset", () => Ruleset.Value.OnlineID == 0);
// this is an important check, to make sure updateComponentFromBeatmap() was actually run // this is an important check, to make sure updateComponentFromBeatmap() was actually run
AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.MatchesOnlineID(target)); AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.MatchesOnlineID(target));
@ -505,7 +502,7 @@ namespace osu.Game.Tests.Visual.SongSelect
// special case for converts checked here. // special case for converts checked here.
return selectedPanel.ChildrenOfType<FilterableDifficultyIcon>().All(i => return selectedPanel.ChildrenOfType<FilterableDifficultyIcon>().All(i =>
i.IsFiltered || i.Item.BeatmapInfo.Ruleset.ID == targetRuleset || i.Item.BeatmapInfo.Ruleset.ID == 0); i.IsFiltered || i.Item.BeatmapInfo.Ruleset.OnlineID == targetRuleset || i.Item.BeatmapInfo.Ruleset.OnlineID == 0);
}); });
AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.MatchesOnlineID(target) == true); AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.MatchesOnlineID(target) == true);
@ -665,8 +662,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import multi-ruleset map", () => AddStep("import multi-ruleset map", () =>
{ {
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray(); var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait(); manager.Import(TestResources.CreateTestBeatmapSetInfo(3, usableRulesets)).Wait();
}); });
int previousSetID = 0; int previousSetID = 0;
@ -676,11 +673,11 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("record set ID", () => previousSetID = ((IBeatmapSetInfo)Beatmap.Value.BeatmapSetInfo).OnlineID); AddStep("record set ID", () => previousSetID = ((IBeatmapSetInfo)Beatmap.Value.BeatmapSetInfo).OnlineID);
AddAssert("selection changed once", () => changeCount == 1); AddAssert("selection changed once", () => changeCount == 1);
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0); AddAssert("Check ruleset is osu!", () => Ruleset.Value.OnlineID == 0);
changeRuleset(3); changeRuleset(3);
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3); AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.OnlineID == 3);
AddUntilStep("selection changed", () => changeCount > 1); AddUntilStep("selection changed", () => changeCount > 1);
@ -705,8 +702,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import multi-ruleset map", () => AddStep("import multi-ruleset map", () =>
{ {
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray(); var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait(); manager.Import(TestResources.CreateTestBeatmapSetInfo(3, usableRulesets)).Wait();
}); });
DrawableCarouselBeatmapSet set = null; DrawableCarouselBeatmapSet set = null;
@ -720,11 +717,11 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("Find an icon for different ruleset", () => AddUntilStep("Find an icon for different ruleset", () =>
{ {
difficultyIcon = set.ChildrenOfType<FilterableDifficultyIcon>() difficultyIcon = set.ChildrenOfType<FilterableDifficultyIcon>()
.FirstOrDefault(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3); .FirstOrDefault(icon => icon.Item.BeatmapInfo.Ruleset.OnlineID == 3);
return difficultyIcon != null; return difficultyIcon != null;
}); });
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0); AddAssert("Check ruleset is osu!", () => Ruleset.Value.OnlineID == 0);
int previousSetID = 0; int previousSetID = 0;
@ -737,7 +734,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Click(MouseButton.Left); InputManager.Click(MouseButton.Left);
}); });
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3); AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.OnlineID == 3);
AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmapInfo.BeatmapSet.OnlineID == previousSetID); AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmapInfo.BeatmapSet.OnlineID == previousSetID);
AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.OnlineID == 3); AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.OnlineID == 3);
@ -754,8 +751,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import huge difficulty count map", () => AddStep("import huge difficulty count map", () =>
{ {
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray(); var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
imported = manager.Import(createTestBeatmapSet(usableRulesets, 50)).Result.Value; imported = manager.Import(TestResources.CreateTestBeatmapSetInfo(50, usableRulesets)).Result.Value;
}); });
AddStep("select the first beatmap of import", () => Beatmap.Value = manager.GetWorkingBeatmap(imported.Beatmaps.First())); AddStep("select the first beatmap of import", () => Beatmap.Value = manager.GetWorkingBeatmap(imported.Beatmaps.First()));
@ -771,10 +768,10 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("Find group icon for different ruleset", () => AddUntilStep("Find group icon for different ruleset", () =>
{ {
return (groupIcon = set.ChildrenOfType<FilterableGroupedDifficultyIcon>() return (groupIcon = set.ChildrenOfType<FilterableGroupedDifficultyIcon>()
.FirstOrDefault(icon => icon.Items.First().BeatmapInfo.Ruleset.ID == 3)) != null; .FirstOrDefault(icon => icon.Items.First().BeatmapInfo.Ruleset.OnlineID == 3)) != null;
}); });
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0); AddAssert("Check ruleset is osu!", () => Ruleset.Value.OnlineID == 0);
AddStep("Click on group", () => AddStep("Click on group", () =>
{ {
@ -783,7 +780,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Click(MouseButton.Left); InputManager.Click(MouseButton.Left);
}); });
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3); AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.OnlineID == 3);
AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(groupIcon.Items.First().BeatmapInfo)); AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(groupIcon.Items.First().BeatmapInfo));
} }
@ -817,7 +814,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen());
AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(getPresentBeatmap())); AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(getPresentBeatmap()));
AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); AddAssert("check ruleset is correct for score", () => Ruleset.Value.OnlineID == 0);
} }
[Test] [Test]
@ -849,7 +846,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen());
AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(getPresentBeatmap())); AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(getPresentBeatmap()));
AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); AddAssert("check ruleset is correct for score", () => Ruleset.Value.OnlineID == 0);
} }
private void waitForInitialSelection() private void waitForInitialSelection()
@ -869,18 +866,14 @@ namespace osu.Game.Tests.Visual.SongSelect
private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id)); private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id));
private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait(); private void importForRuleset(int id) => manager.Import(TestResources.CreateTestBeatmapSetInfo(3, rulesets.AvailableRulesets.Where(r => r.OnlineID == id).ToArray())).Wait();
private static int importId;
private int getImportId() => ++importId;
private void checkMusicPlaying(bool playing) => private void checkMusicPlaying(bool playing) =>
AddUntilStep($"music {(playing ? "" : "not ")}playing", () => music.IsPlaying == playing); AddUntilStep($"music {(playing ? "" : "not ")}playing", () => music.IsPlaying == playing);
private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => SelectedMods.Value = mods); private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => SelectedMods.Value = mods);
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id)); private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.OnlineID == id));
private void createSongSelect() private void createSongSelect()
{ {
@ -893,59 +886,13 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
AddStep("import test maps", () => AddStep("import test maps", () =>
{ {
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray(); var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
for (int i = 0; i < 100; i += 10) for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(usableRulesets)).Wait(); manager.Import(TestResources.CreateTestBeatmapSetInfo(rulesets: usableRulesets)).Wait();
}); });
} }
private BeatmapSetInfo createTestBeatmapSet(RulesetInfo[] rulesets, int countPerRuleset = 6)
{
int j = 0;
RulesetInfo getRuleset() => rulesets[j++ % rulesets.Length];
int setId = getImportId();
var beatmaps = new List<BeatmapInfo>();
for (int i = 0; i < countPerRuleset; i++)
{
int beatmapId = setId * 1000 + i;
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
beatmaps.Add(new BeatmapInfo
{
Ruleset = getRuleset(),
OnlineID = beatmapId,
DifficultyName = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
Length = length,
BPM = bpm,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 3.5f,
},
});
}
return new BeatmapSetInfo
{
OnlineID = setId,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = "Some Artist " + RNG.Next(0, 9),
Title = $"Some Song (set id {setId}, max bpm {beatmaps.Max(b => b.BPM):0.#})",
AuthorString = "Some Guy " + RNG.Next(0, 9),
},
Beatmaps = beatmaps,
DateAdded = DateTimeOffset.UtcNow,
};
}
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);

View File

@ -135,6 +135,35 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("bpm is default", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60)); AddAssert("bpm is default", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60));
} }
[TestCase(true)]
[TestCase(false)]
public void TestEarlyActivationEffectPoint(bool earlyActivating)
{
double earlyActivationMilliseconds = earlyActivating ? 100 : 0;
ControlPoint actualEffectPoint = null;
AddStep($"set early activation to {earlyActivationMilliseconds}", () => beatContainer.EarlyActivationMilliseconds = earlyActivationMilliseconds);
AddStep("seek before kiai effect point", () =>
{
ControlPoint expectedEffectPoint = Beatmap.Value.Beatmap.ControlPointInfo.EffectPoints.First(ep => ep.KiaiMode);
actualEffectPoint = null;
beatContainer.AllowMistimedEventFiring = false;
beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) =>
{
if (Precision.AlmostEquals(gameplayClockContainer.CurrentTime + earlyActivationMilliseconds, expectedEffectPoint.Time, BeatSyncedContainer.MISTIMED_ALLOWANCE))
actualEffectPoint = effectControlPoint;
};
gameplayClockContainer.Seek(expectedEffectPoint.Time - earlyActivationMilliseconds);
});
AddUntilStep("wait for effect point", () => actualEffectPoint != null);
AddAssert("effect has kiai", () => actualEffectPoint != null && ((EffectControlPoint)actualEffectPoint).KiaiMode);
}
private class TestBeatSyncedContainer : BeatSyncedContainer private class TestBeatSyncedContainer : BeatSyncedContainer
{ {
private const int flash_layer_height = 150; private const int flash_layer_height = 150;
@ -145,6 +174,12 @@ namespace osu.Game.Tests.Visual.UserInterface
set => base.AllowMistimedEventFiring = value; set => base.AllowMistimedEventFiring = value;
} }
public new double EarlyActivationMilliseconds
{
get => base.EarlyActivationMilliseconds;
set => base.EarlyActivationMilliseconds = value;
}
private readonly InfoString timingPointCount; private readonly InfoString timingPointCount;
private readonly InfoString currentTimingPoint; private readonly InfoString currentTimingPoint;
private readonly InfoString beatCount; private readonly InfoString beatCount;

View File

@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.UserInterface
dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory)); dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory));
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default)); dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory, Scheduler)); dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, ContextFactory, Scheduler));
beatmapInfo = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Value.Beatmaps[0]; beatmapInfo = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Value.Beatmaps[0];

View File

@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface
[Test] [Test]
public void TestLabelledEnumDropdown() public void TestLabelledEnumDropdown()
=> AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown<BeatmapSetOnlineStatus> => AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown<BeatmapOnlineStatus>
{ {
Label = @"Beatmap status", Label = @"Beatmap status",
Description = @"This is a description" Description = @"This is a description"

View File

@ -378,9 +378,9 @@ namespace osu.Game.Tests.Visual.UserInterface
}); });
} }
private void changeRuleset(int? id) private void changeRuleset(int? onlineId)
{ {
AddStep($"change ruleset to {(id?.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == id); }); AddStep($"change ruleset to {(onlineId?.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.OnlineID == onlineId); });
waitForLoad(); waitForLoad();
} }

View File

@ -10,7 +10,7 @@ namespace osu.Game.Tests.Visual.UserInterface
public class TestSceneOsuDropdown : ThemeComparisonTestScene public class TestSceneOsuDropdown : ThemeComparisonTestScene
{ {
protected override Drawable CreateContent() => protected override Drawable CreateContent() =>
new OsuEnumDropdown<BeatmapSetOnlineStatus> new OsuEnumDropdown<BeatmapOnlineStatus>
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

Some files were not shown because too many files have changed in this diff Show More