1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 14:32: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
- 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
# Attempt to upload results even if test fails.
@ -48,7 +48,7 @@ jobs:
if: ${{ always() }}
with:
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:
name: Build only (Android)
@ -77,10 +77,6 @@ jobs:
run: msbuild osu.Android/osu.Android.csproj /restore /p:Configuration=Debug
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)
runs-on: macos-latest
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:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods.
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" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1112.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1119.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1203.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1207.0" />
</ItemGroup>
<ItemGroup Label="Transitive Dependencies">
<!-- 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);
// 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;
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.Threading;
using System.Threading.Tasks;
using osu.Desktop.LegacyIpc;
using osu.Framework;
using osu.Framework.Development;
using osu.Framework.Logging;
@ -18,8 +19,10 @@ namespace osu.Desktop
{
private const string base_game_name = @"osu";
private static LegacyTcpIpcProvider legacyIpc;
[STAThread]
public static int Main(string[] args)
public static void Main(string[] args)
{
// Back up the cwd before DesktopGameHost changes it
string cwd = Environment.CurrentDirectory;
@ -69,14 +72,29 @@ namespace osu.Desktop
throw new TimeoutException(@"IPC took too long to send");
}
return 0;
return;
}
// we want to allow multiple instances to be started when in debug.
if (!DebugUtils.IsDebugBuild)
{
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());
else
host.Run(new OsuGameDesktop(args));
return 0;
}
}

View File

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

View File

@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Catch.Tests
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 legacySkinTransformer = new SkinProvidingContainer(new CatchLegacySkinTransformer(testSkinProvider));

View File

@ -8,6 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Screens.Edit;
@ -55,13 +56,13 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
[Test]
public void TestDefaultSkin()
{
AddStep("set default skin", () => skins.CurrentSkinInfo.Value = SkinInfo.Default);
AddStep("set default skin", () => skins.CurrentSkinInfo.Value = DefaultSkin.CreateInfo().ToLive());
}
[Test]
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.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.UI;
@ -46,12 +45,6 @@ namespace osu.Game.Rulesets.Mania.Edit
[Resolved]
private EditorBeatmap beatmap { get; set; }
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }
[Resolved]
private Bindable<WorkingBeatmap> working { get; set; }
[Resolved]
private OsuColour colours { get; set; }

View File

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

View File

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

View File

@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep("create slider", () =>
{
var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo());
tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1";
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.
using System;
@ -26,12 +26,12 @@ namespace osu.Game.Rulesets.Osu.Mods
public BindableFloat Scale { get; } = new BindableFloat(4)
{
Precision = 0.1f,
MinValue = 2,
MinValue = 1.5f,
MaxValue = 10,
};
[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)
{
@ -52,9 +52,18 @@ namespace osu.Game.Rulesets.Osu.Mods
{
switch (style)
{
default:
case AnimationStyle.Linear:
return Easing.None;
case AnimationStyle.Gravity:
return Easing.InBack;
case AnimationStyle.InOut1:
return Easing.InOutCubic;
case AnimationStyle.InOut2:
return Easing.InOutQuint;
case AnimationStyle.Accelerate1:
return Easing.In;
@ -64,9 +73,6 @@ namespace osu.Game.Rulesets.Osu.Mods
case AnimationStyle.Accelerate3:
return Easing.InQuint;
case AnimationStyle.Gravity:
return Easing.InBack;
case AnimationStyle.Decelerate1:
return Easing.Out;
@ -76,16 +82,14 @@ namespace osu.Game.Rulesets.Osu.Mods
case AnimationStyle.Decelerate3:
return Easing.OutQuint;
case AnimationStyle.InOut1:
return Easing.InOutCubic;
case AnimationStyle.InOut2:
return Easing.InOutQuint;
default:
throw new ArgumentOutOfRangeException(nameof(style), style, @"Unsupported animation style");
}
}
public enum AnimationStyle
{
Linear,
Gravity,
InOut1,
InOut2,

View File

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

View File

@ -2,14 +2,20 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
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.Taiko;
using osu.Game.Scoring;
@ -21,6 +27,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
[TestFixture]
public class LegacyScoreDecoderTest
{
private CultureInfo originalCulture;
[SetUp]
public void SetUp()
{
originalCulture = CultureInfo.CurrentCulture;
}
[Test]
public void TestDecodeManiaReplay()
{
@ -30,7 +44,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
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(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 static readonly Dictionary<int, Ruleset> rulesets = new Ruleset[]

View File

@ -584,7 +584,7 @@ namespace osu.Game.Tests.Beatmaps.IO
{
OnlineID = 1,
Metadata = metadata,
Beatmaps = new List<BeatmapInfo>
Beatmaps =
{
new BeatmapInfo
{
@ -596,7 +596,7 @@ namespace osu.Game.Tests.Beatmaps.IO
{
OnlineID = 2,
Metadata = metadata,
Status = BeatmapSetOnlineStatus.Loved,
Status = BeatmapOnlineStatus.Loved,
BaseDifficulty = difficulty
}
}
@ -1050,7 +1050,7 @@ namespace osu.Game.Tests.Beatmaps.IO
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)

View File

@ -25,9 +25,6 @@ namespace osu.Game.Tests.Beatmaps
private BeatmapSetInfo importedSet;
[Resolved]
private BeatmapManager beatmaps { get; set; }
private TestBeatmapDifficultyCache difficultyCache;
private IBindable<StarDifficulty?> starDifficultyBindable;
@ -100,8 +97,8 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestKeyEqualsWithDifferentModInstances()
{
var key1 = 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 { 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 { OnlineID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
Assert.That(key1, Is.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
@ -110,8 +107,8 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestKeyEqualsWithDifferentModOrder()
{
var key1 = 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 { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
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 { OnlineID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
Assert.That(key1, Is.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
@ -120,8 +117,8 @@ namespace osu.Game.Tests.Beatmaps
[Test]
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 key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } });
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 { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } });
Assert.That(key1, Is.Not.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.Not.EqualTo(key2.GetHashCode()));
@ -130,8 +127,8 @@ namespace osu.Game.Tests.Beatmaps
[Test]
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 key2 = 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 { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
Assert.That(key1, Is.EqualTo(key2));
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.ObjectExtensions;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Extensions;
@ -474,7 +475,7 @@ namespace osu.Game.Tests.Database
}
[Test]
public void TestImportThenDeleteThenImport()
public void TestImportThenDeleteThenImportOptimisedPath()
{
RunTestWithRealmAsync(async (realmFactory, storage) =>
{
@ -485,11 +486,39 @@ namespace osu.Game.Tests.Database
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);
});
}
[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)
{
OnlineID = 2,
Status = BeatmapSetOnlineStatus.Loved,
Status = BeatmapOnlineStatus.Loved,
}
}
};
@ -823,7 +852,11 @@ namespace osu.Game.Tests.Database
{
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);
// ensure we were stored to beatmap database backing...
@ -836,16 +869,16 @@ namespace osu.Game.Tests.Database
// ReSharper disable once PossibleUnintendedReferenceComparison
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);
waitForOrAssert(() => queryBeatmapSets().Count() == 1, @"BeatmapSet did not import to the database in allocated time", timeout);
Assert.AreEqual(12, queryBeatmaps().Count(), @"Beatmap count was not correct");
Assert.AreEqual(1, queryBeatmapSets().Count(), @"Beatmapset count was not correct");
int countBeatmapSetBeatmaps = 0;
int countBeatmaps = 0;
int countBeatmapSetBeatmaps;
int countBeatmaps;
waitForOrAssert(() =>
(countBeatmapSetBeatmaps = queryBeatmapSets().First().Beatmaps.Count) ==
(countBeatmaps = queryBeatmaps().Count()),
$@"Incorrect database beatmap count post-import ({countBeatmaps} but should be {countBeatmapSetBeatmaps}).", timeout);
Assert.AreEqual(
countBeatmapSetBeatmaps = queryBeatmapSets().First().Beatmaps.Count,
countBeatmaps = queryBeatmaps().Count(),
$@"Incorrect database beatmap count post-import ({countBeatmaps} but should be {countBeatmapSetBeatmaps}).");
foreach (RealmBeatmap b in set.Beatmaps)
Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineID == b.OnlineID));
@ -867,5 +900,15 @@ namespace osu.Game.Tests.Database
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.Tasks;
using NUnit.Framework;
using osu.Game.Database;
using osu.Game.Models;
#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]
public void TestBlockOperationsWithContention()
{

View File

@ -6,7 +6,6 @@ using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Models;
using Realms;
@ -18,61 +17,57 @@ namespace osu.Game.Tests.Database
public class RealmLiveTests : RealmTest
{
[Test]
public void TestLiveCastability()
public void TestLiveEquality()
{
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]
public void TestValueAccessWithOpenContext()
public void TestAccessAfterAttach()
{
RunTestWithRealm((realmFactory, _) =>
{
RealmLive<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())));
var beatmap = new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata());
liveBeatmap = beatmap.ToLive();
}
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait();
var liveBeatmap = beatmap.ToLive();
Debug.Assert(liveBeatmap != null);
using (var context = realmFactory.CreateContext())
context.Write(r => r.Add(beatmap));
Task.Factory.StartNew(() =>
{
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();
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
});
}
[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]
public void TestScopedReadWithoutContext()
{
RunTestWithRealm((realmFactory, _) =>
{
RealmLive<RealmBeatmap>? liveBeatmap = null;
ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() =>
{
using (var threadContext = realmFactory.CreateContext())
@ -101,7 +96,7 @@ namespace osu.Game.Tests.Database
{
RunTestWithRealm((realmFactory, _) =>
{
RealmLive<RealmBeatmap>? liveBeatmap = null;
ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() =>
{
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]
public void TestValueAccessWithoutOpenContextFails()
{
RunTestWithRealm((realmFactory, _) =>
{
RealmLive<RealmBeatmap>? liveBeatmap = null;
ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() =>
{
using (var threadContext = realmFactory.CreateContext())
@ -159,8 +208,8 @@ namespace osu.Game.Tests.Database
using (var updateThreadContext = realmFactory.CreateContext())
{
updateThreadContext.All<RealmBeatmap>().SubscribeForNotifications(gotChange);
RealmLive<RealmBeatmap>? liveBeatmap = null;
updateThreadContext.All<RealmBeatmap>().QueryAsyncWithNotifications(gotChange);
ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() =>
{
@ -183,23 +232,22 @@ namespace osu.Game.Tests.Database
Assert.AreEqual(0, updateThreadContext.All<RealmBeatmap>().Count());
Assert.AreEqual(0, changesTriggered);
var resolved = liveBeatmap.Value;
// 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 =>
liveBeatmap.PerformRead(resolved =>
{
// can use with the main context.
r.Remove(resolved);
// retrieval causes an implicit refresh. even changes that aren't related to the retrieval are fired at this point.
// 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);
Assert.IsTrue((rulesets.AvailableRulesets.First() as RealmRuleset)?.IsManaged == false);
Assert.IsTrue((rulesets.GetRuleset(0) as RealmRuleset)?.IsManaged == false);
Assert.IsTrue((rulesets.GetRuleset("mania") as RealmRuleset)?.IsManaged == false);
Assert.IsFalse(rulesets.AvailableRulesets.First().IsManaged);
Assert.IsFalse(rulesets.GetRuleset(0)?.IsManaged);
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.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Moq;
@ -13,7 +12,6 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Storyboards;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Resources;
using FileInfo = osu.Game.IO.FileInfo;
namespace osu.Game.Tests.Editing.Checks
{
@ -33,14 +31,10 @@ namespace osu.Game.Tests.Editing.Checks
{
BeatmapSet = new BeatmapSetInfo
{
Files = new List<BeatmapSetFileInfo>(new[]
Files =
{
new BeatmapSetFileInfo
{
Filename = "abc123.mp4",
FileInfo = new FileInfo { Hash = "abcdef" }
}
})
CheckTestHelpers.CreateMockFile("mp4"),
}
}
}
};

View File

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

View File

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

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.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ManagedBass;
@ -14,7 +13,6 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Resources;
using osuTK.Audio;
using FileInfo = osu.Game.IO.FileInfo;
namespace osu.Game.Tests.Editing.Checks
{
@ -34,14 +32,7 @@ namespace osu.Game.Tests.Editing.Checks
{
BeatmapSet = new BeatmapSetInfo
{
Files = new List<BeatmapSetFileInfo>(new[]
{
new BeatmapSetFileInfo
{
Filename = "abc123.wav",
FileInfo = new FileInfo { Hash = "abcdef" }
}
})
Files = { CheckTestHelpers.CreateMockFile("wav") }
}
}
};
@ -55,11 +46,7 @@ namespace osu.Game.Tests.Editing.Checks
public void TestDifferentExtension()
{
beatmap.BeatmapInfo.BeatmapSet.Files.Clear();
beatmap.BeatmapInfo.BeatmapSet.Files.Add(new BeatmapSetFileInfo
{
Filename = "abc123.jpg",
FileInfo = new FileInfo { Hash = "abcdef" }
});
beatmap.BeatmapInfo.BeatmapSet.Files.Add(CheckTestHelpers.CreateMockFile("jpg"));
// 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)));

View File

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

View File

@ -167,7 +167,7 @@ namespace osu.Game.Tests.Gameplay
private class TestSkin : LegacySkin
{
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]
private FrameworkConfigManager frameworkConfigManager { get; set; }
[Resolved]
private OsuConfigManager osuConfigManager { get; set; }
[TestCase(WindowMode.Windowed)]
[TestCase(WindowMode.Borderless)]
public void TestDisableConfining(WindowMode windowMode)

View File

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

View File

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

View File

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

View File

@ -162,9 +162,9 @@ namespace osu.Game.Tests.NonVisual.Filtering
FilterQueryParser.ApplyQueries(filterCriteria, query);
Assert.AreEqual("I want the pp", filterCriteria.SearchText.Trim());
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.AreEqual(BeatmapSetOnlineStatus.Ranked, filterCriteria.OnlineStatus.Max);
Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Max);
Assert.IsTrue(filterCriteria.OnlineStatus.IsUpperInclusive);
}

View File

@ -16,6 +16,27 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
[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);
}
[Test]
public void TestPlayingUserTracking()
{

View File

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

View File

@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -33,6 +33,7 @@ namespace osu.Game.Tests.Online
{
private RulesetStore rulesets;
private TestBeatmapManager beatmaps;
private TestBeatmapModelDownloader beatmapDownloader;
private string testBeatmapFile;
private BeatmapInfo testBeatmapInfo;
@ -46,6 +47,7 @@ namespace osu.Game.Tests.Online
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
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]
@ -80,13 +82,13 @@ namespace osu.Game.Tests.Online
AddAssert("ensure beatmap unavailable", () => !beatmaps.IsAvailableLocally(testBeatmapSet));
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));
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));
AddStep("finish download", () => ((TestDownloadRequest)beatmaps.GetExistingDownload(testBeatmapSet)).TriggerSuccess(testBeatmapFile));
AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet)).TriggerSuccess(testBeatmapFile));
addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing);
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
@ -143,7 +145,10 @@ namespace osu.Game.Tests.Online
var beatmap = decoder.Decode(reader);
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.MD5Hash = stream.ComputeMD5Hash();
info.Hash = stream.ComputeSHA2Hash();
@ -168,22 +173,6 @@ namespace osu.Game.Tests.Online
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
{
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>
{
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.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using NUnit.Framework;
using osu.Framework.Extensions;
using osu.Framework.IO.Stores;
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
{
@ -56,5 +64,78 @@ namespace osu.Game.Tests.Resources
}
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>();
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);
Assert.That(secondImport, Is.Null);

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.IO;
using osu.Game.IO.Archives;
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);
});
[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
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));
Assert.That(import1.Creator, Is.EqualTo(creator));
import1.PerformRead(i =>
{
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.
var instance = import1.CreateInstance((IStorageResourceProvider)osu.Dependencies.Get(typeof(SkinManager)));
// for extra safety let's reconstruct the skin, reading from the skin.ini.
var instance = i.CreateInstance((IStorageResourceProvider)osu.Dependencies.Get(typeof(SkinManager)));
Assert.That(instance.Configuration.SkinInfo.Name, Is.EqualTo(name));
Assert.That(instance.Configuration.SkinInfo.Creator, Is.EqualTo(creator));
Assert.That(instance.Configuration.SkinInfo.Name, Is.EqualTo(name));
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));
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)));
import1.PerformRead(i1 => import2.PerformRead(i2 =>
{
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));
Assert.That(import2.Hash, Is.EqualTo(import1.Hash));
Assert.That(import2.Files.Select(f => f.FileInfoID), Is.EquivalentTo(import1.Files.Select(f => f.FileInfoID)));
import1.PerformRead(i1 => import2.PerformRead(i2 =>
{
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()
@ -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>();
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()
{
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]

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tests.Testing
{
// temporary ID to let RulesetConfigCache pass our
// config manager to the ruleset dependencies.
RulesetInfo.ID = -1;
RulesetInfo.OnlineID = -1;
}
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.Testing;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
@ -122,7 +123,7 @@ namespace osu.Game.Tests.Visual.Background
private void setCustomSkin()
{
// 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());

View File

@ -23,6 +23,11 @@ namespace osu.Game.Tests.Visual.Beatmaps
{
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 APIBeatmapSet[] testCases;
@ -38,11 +43,10 @@ namespace osu.Game.Tests.Visual.Beatmaps
var normal = CreateAPIBeatmapSet(Ruleset.Value);
normal.HasVideo = true;
normal.HasStoryboard = true;
normal.OnlineID = 241526;
var withStatistics = CreateAPIBeatmapSet(Ruleset.Value);
withStatistics.Title = withStatistics.TitleUnicode = "play favourite stats";
withStatistics.Status = BeatmapSetOnlineStatus.Approved;
withStatistics.Status = BeatmapOnlineStatus.Approved;
withStatistics.FavouriteCount = 284_239;
withStatistics.PlayCount = 999_001;
withStatistics.Ranked = DateTimeOffset.Now.AddDays(-45);
@ -63,7 +67,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
var someDifficulties = getManyDifficultiesBeatmapSet(11);
someDifficulties.Title = someDifficulties.TitleUnicode = "favourited";
someDifficulties.Title = someDifficulties.TitleUnicode = "some difficulties";
someDifficulties.Status = BeatmapSetOnlineStatus.Qualified;
someDifficulties.Status = BeatmapOnlineStatus.Qualified;
someDifficulties.HasFavourited = true;
someDifficulties.FavouriteCount = 1;
someDifficulties.NominationStatus = new BeatmapSetNominationStatus
@ -73,7 +77,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
};
var manyDifficulties = getManyDifficultiesBeatmapSet(100);
manyDifficulties.Status = BeatmapSetOnlineStatus.Pending;
manyDifficulties.Status = BeatmapOnlineStatus.Pending;
var explicitMap = CreateAPIBeatmapSet(Ruleset.Value);
explicitMap.Title = someDifficulties.TitleUnicode = "explicit beatmap";
@ -106,6 +110,9 @@ namespace osu.Game.Tests.Visual.Beatmaps
explicitFeaturedMap,
longName
};
foreach (var testCase in testCases)
testCase.OnlineID = online_id;
}
private APIBeatmapSet getUndownloadableBeatmapSet() => new APIBeatmapSet
@ -191,9 +198,9 @@ namespace osu.Game.Tests.Visual.Beatmaps
private void ensureSoleilyRemoved()
{
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);
});

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,
Direction = FillDirection.Vertical,
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,
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);
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);
AddStep("Open options", () => InputManager.Key(Key.F3));

View File

@ -12,6 +12,7 @@ using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
@ -41,7 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
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));
AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value));
}
@ -52,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddStep("setup skins", () =>
{
skinManager.CurrentSkinInfo.Value = gameCurrentSkin;
skinManager.CurrentSkinInfo.Value = gameCurrentSkin.ToLive();
currentBeatmapSkin = getBeatmapSkin();
});
});

View File

@ -233,7 +233,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
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);

View File

@ -43,83 +43,88 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached]
private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty<Mod>());
[SetUp]
public void SetUp() => Schedule(() =>
[SetUpSteps]
public void SetUpSteps()
{
replay = new Replay();
AddStep("Reset recorder state", cleanUpState);
Add(new GridContainer
AddStep("Setup containers", () =>
{
RelativeSizeAxes = Axes.Both,
Content = new[]
replay = new Replay();
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,
ScoreInfo = { BeatmapInfo = gameplayState.Beatmap.BeatmapInfo }
})
{
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
Recorder = recorder = new TestReplayRecorder(new Score
{
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,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Text = "Recording",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestInputConsumer()
}
},
}
},
new Drawable[]
{
playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
new Box
{
Colour = Color4.Brown,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Text = "Recording",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestInputConsumer()
}
},
}
},
new Drawable[]
{
ReplayInputHandler = new TestFramedReplayInputHandler(replay)
playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
ReplayInputHandler = new TestFramedReplayInputHandler(replay)
{
new Box
GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
Colour = Color4.DarkBlue,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Text = "Playback",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestInputConsumer()
}
},
new Box
{
Colour = Color4.DarkBlue,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Text = "Playback",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestInputConsumer()
}
},
}
}
}
}
});
});
});
}
[Test]
public void TestBasic()
@ -184,7 +189,14 @@ namespace osu.Game.Tests.Visual.Gameplay
[TearDownSteps]
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>

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.
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Skinning;
using osu.Game.Skinning.Editor;
namespace osu.Game.Tests.Visual.Gameplay
@ -16,9 +14,6 @@ namespace osu.Game.Tests.Visual.Gameplay
{
private SkinEditor skinEditor;
[Resolved]
private SkinManager skinManager { get; set; }
protected override bool Autoplay => true;
[SetUpSteps]

View File

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

View File

@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestClientSendsCorrectRuleset()
{
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()

View File

@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private TestReplayRecorder recorder;
private readonly ManualClock manualClock = new ManualClock();
private ManualClock manualClock;
private OsuSpriteText latencyDisplay;
@ -66,113 +66,121 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached]
private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty<Mod>());
[SetUp]
public void SetUp() => Schedule(() =>
[SetUpSteps]
public void SetUpSteps()
{
replay = new Replay();
AddStep("Reset recorder state", cleanUpState);
users.BindTo(spectatorClient.PlayingUsers);
users.BindCollectionChanged((obj, args) =>
AddStep("Setup containers", () =>
{
switch (args.Action)
replay = new Replay();
manualClock = new ManualClock();
spectatorClient.OnNewFrames += onNewFrames;
users.BindTo(spectatorClient.PlayingUsers);
users.BindCollectionChanged((obj, args) =>
{
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);
spectatorClient.OnNewFrames += onNewFrames;
Add(new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
switch (args.Action)
{
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),
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
new Box
Recorder = recorder = new TestReplayRecorder
{
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
},
Child = new Container
{
Colour = Color4.Brown,
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[]
{
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()
}
},
}
}
}
latencyDisplay = new OsuSpriteText()
};
});
Add(latencyDisplay = new OsuSpriteText());
});
}
private void onNewFrames(int userId, FrameDataBundle frames)
{
@ -189,6 +197,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestBasic()
{
AddStep("Wait for user input", () => { });
}
private double latency = SpectatorClient.TIME_BETWEEN_SENDS;
@ -232,11 +241,15 @@ namespace osu.Game.Tests.Visual.Gameplay
[TearDownSteps]
public void TearDown()
{
AddStep("stop recorder", () =>
{
recorder.Expire();
spectatorClient.OnNewFrames -= onNewFrames;
});
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;
spectatorClient.OnNewFrames -= onNewFrames;
}
public class TestFramedReplayInputHandler : FramedReplayInputHandler<TestReplayFrame>

View File

@ -30,25 +30,10 @@ namespace osu.Game.Tests.Visual.Menus
[Test]
public void TestMusicNavigationActions()
{
int importId = 0;
Queue<(IWorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null;
// 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
{
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty(),
}
},
Metadata = new BeatmapMetadata
{
Artist = $"a test map {importId++}",
Title = "title",
}
}).Wait(), 5);
AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(TestResources.CreateTestBeatmapSetInfo()).Wait(), 5);
AddStep("import beatmap with track", () =>
{

View File

@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Menus
private TestToolbar toolbar;
[Resolved]
private RulesetStore rulesets { get; set; }
private IRulesetStore rulesets { get; set; }
[SetUp]
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);
}
[Test]
public void TestCorrectItemSelectedAfterNewItemAdded()
{
addItem(() => OtherBeatmap);
AddAssert("selected beatmap is initial beatmap", () => Beatmap.Value.BeatmapInfo.OnlineID == InitialBeatmap.OnlineID);
}
private void addItem(Func<BeatmapInfo> beatmap)
{
AddStep("click edit button", () =>
@ -86,7 +93,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
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()));
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.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Overlays.BeatmapListing.Panels;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.OnlinePlay;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users.Drawables;
using osuTK;
using osuTK.Input;
@ -34,6 +35,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
private BeatmapManager manager;
private RulesetStore rulesets;
[Cached(typeof(UserLookupCache))]
private readonly TestUserLookupCache userLookupCache = new TestUserLookupCache();
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
@ -231,7 +235,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
assertDownloadButtonVisible(false);
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]
@ -245,7 +249,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
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]
@ -304,6 +308,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
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)
=> 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",
() => (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", () =>
{
Child = playlist = new TestPlaylist(allowEdit, allowSelection)
Child = playlist = new TestPlaylist(allowEdit, allowSelection, showItemOwner)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -343,6 +356,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
playlist.Items.Add(new PlaylistItem
{
ID = i,
OwnerID = 2,
Beatmap =
{
Value = i % 2 == 1
@ -390,6 +404,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
playlist.Items.Add(new PlaylistItem
{
ID = index++,
OwnerID = 2,
Beatmap = { Value = b },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RequiredMods =
@ -409,8 +424,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public new IReadOnlyDictionary<PlaylistItem, RearrangeableListItem<PlaylistItem>> ItemMap => base.ItemMap;
public TestPlaylist(bool allowEdit, bool allowSelection)
: base(allowEdit, allowSelection)
public TestPlaylist(bool allowEdit, bool allowSelection, bool showItemOwner = false)
: base(allowEdit, allowSelection, showItemOwner: showItemOwner)
{
}
}

View File

@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
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;
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.
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.OnlinePlay.Components;
@ -18,12 +15,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMatchBeatmapDetailArea : OnlinePlayTestScene
{
[Resolved]
private BeatmapManager beatmapManager { get; set; }
[Resolved]
private RulesetStore rulesetStore { get; set; }
[SetUp]
public new void Setup() => Schedule(() =>
{

View File

@ -204,7 +204,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
// edit playlist item
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
AddStep("Press select", () => InputManager.Key(Key.Enter));
@ -253,7 +253,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Ruleset = { Value = new OsuRuleset().RulesetInfo },
}
}
});
}, API.LocalUser.Value);
});
AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter());
@ -283,7 +283,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Ruleset = { Value = new OsuRuleset().RulesetInfo },
}
}
});
}, API.LocalUser.Value);
});
AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter());
@ -336,7 +336,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Ruleset = { Value = new OsuRuleset().RulesetInfo },
}
}
});
}, API.LocalUser.Value);
});
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);
}
[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]
public void TestLocalPlayDoesNotStartWhileSpectatingWithNoBeatmap()
{
@ -597,7 +635,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Ruleset = { Value = new OsuRuleset().RulesetInfo },
}
}
});
}, API.LocalUser.Value);
});
AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter());

View File

@ -47,6 +47,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
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)
{
int beatmapId = 10 * 10 + i;
@ -54,29 +69,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
beatmaps.Add(new BeatmapInfo
var beatmap = new BeatmapInfo
{
Ruleset = rulesets.GetRuleset(i % 4),
OnlineID = beatmapId,
Length = length,
BPM = bpm,
Metadata = metadata,
BaseDifficulty = new BeatmapDifficulty()
});
};
beatmaps.Add(beatmap);
beatmapSetInfo.Beatmaps.Add(beatmap);
}
manager.Import(new BeatmapSetInfo
{
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();
manager.Import(beatmapSetInfo).Wait();
}
public override void SetUpSteps()
@ -91,7 +98,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
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]
@ -137,7 +144,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("set mods", () => SelectedMods.Value = new[] { new TaikoModDoubleTime() });
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("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
{
BeatmapID = beatmapInfo.ID,
BeatmapID = beatmapInfo.OnlineID ?? -1,
};
Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem));

View File

@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
PlaylistItem playlistItem = new PlaylistItem
{
BeatmapID = beatmapInfo.ID,
BeatmapID = beatmapInfo.OnlineID ?? -1,
};
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.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Extensions;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Utils;
@ -21,15 +17,13 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Tests.Resources;
using osu.Game.Tests.Visual.OnlinePlay;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestScenePlaylistsSongSelect : OnlinePlayTestScene
{
[Resolved]
private BeatmapManager beatmapManager { get; set; }
private BeatmapManager manager;
private RulesetStore rulesets;
@ -42,43 +36,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
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++)
{
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();
manager.Import(beatmapSet).Wait();
}
public override void SetUpSteps()
@ -94,7 +54,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
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]

View File

@ -11,6 +11,7 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
using osu.Game.Online.Rooms;
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);
}
[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]
public void TestChangeTypeViaMatchSettings()
{
@ -152,6 +180,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
AddWaitStep("wait for transition", 2);
AddUntilStep("create room button enabled", () => this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
AddStep("create room", () =>
{
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]
private OsuGameBase gameBase { get; set; }
[Resolved]
private GameHost host { get; set; }
[Test]
public void TestNullRulesetHandled()
{

View File

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

View File

@ -66,7 +66,9 @@ namespace osu.Game.Tests.Visual.Navigation
{
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());
@ -98,7 +100,9 @@ namespace osu.Game.Tests.Visual.Navigation
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());
@ -130,7 +134,9 @@ namespace osu.Game.Tests.Visual.Navigation
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());
@ -257,7 +263,7 @@ namespace osu.Game.Tests.Visual.Navigation
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);
}
@ -278,7 +284,7 @@ namespace osu.Game.Tests.Visual.Navigation
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);
}

View File

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

View File

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

View File

@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Online
}
[Resolved]
private RulesetStore rulesets { get; set; }
private IRulesetStore rulesets { get; set; }
[Test]
public void TestLoading()
@ -200,7 +200,7 @@ namespace osu.Game.Tests.Visual.Online
{
OnlineID = i * 10,
DifficultyName = $"Test #{i}",
RulesetID = Ruleset.Value.ID ?? -1,
RulesetID = Ruleset.Value.OnlineID,
StarRating = 2 + i * 0.1,
OverallDifficulty = 3.5f,
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(),
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.UserInterface;
using osu.Framework.Input;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
@ -47,9 +46,6 @@ namespace osu.Game.Tests.Visual.Online
[CanBeNull]
private Func<Channel, List<Message>> onGetMessages;
[Resolved]
private GameHost host { get; set; }
public TestSceneChatOverlay()
{
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)
{
BeatmapInfo = { OnlineID = hasOnlineId ? 1234 : (int?)null }
BeatmapInfo = { OnlineID = hasOnlineId ? 1234 : -1 }
});
AddStep("Run command", () => Add(new NowPlayingCommand()));

View File

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

View File

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

View File

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

View File

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

View File

@ -3,15 +3,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets;
@ -19,6 +15,7 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
using osu.Game.Tests.Resources;
using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect
@ -152,7 +149,7 @@ namespace osu.Game.Tests.Visual.SongSelect
const int total_set_count = 200;
for (int i = 0; i < total_set_count; i++)
sets.Add(createTestBeatmapSet(i + 1));
sets.Add(TestResources.CreateTestBeatmapSetInfo());
loadBeatmaps(sets);
@ -183,7 +180,7 @@ namespace osu.Game.Tests.Visual.SongSelect
const int total_set_count = 20;
for (int i = 0; i < total_set_count; i++)
sets.Add(createTestBeatmapSet(i + 1));
sets.Add(TestResources.CreateTestBeatmapSetInfo(3));
loadBeatmaps(sets);
@ -228,10 +225,9 @@ namespace osu.Game.Tests.Visual.SongSelect
loadBeatmaps();
// basic filtering
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: true, count: 3);
waitForSelection(3, 1);
@ -275,16 +271,20 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestFilterRange()
{
string searchText = null;
loadBeatmaps();
// buffer the selection
setSelected(3, 2);
AddStep("get search text", () => searchText = carousel.SelectedBeatmapSet.Metadata.Title);
setSelected(1, 3);
AddStep("Apply a range filter", () => carousel.Filter(new FilterCriteria
{
SearchText = "#3",
SearchText = searchText,
StarDifficulty = new FilterCriteria.OptionalRange<double>
{
Min = 2,
@ -327,7 +327,7 @@ namespace osu.Game.Tests.Visual.SongSelect
nextRandom();
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));
checkInvisibleDifficultiesUnselectable();
checkInvisibleDifficultiesUnselectable();
@ -345,18 +345,21 @@ namespace osu.Game.Tests.Visual.SongSelect
{
loadBeatmaps();
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 1)));
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 2)));
var firstAdded = TestResources.CreateTestBeatmapSetInfo();
var secondAdded = TestResources.CreateTestBeatmapSetInfo();
AddStep("Add new set", () => carousel.UpdateBeatmapSet(firstAdded));
AddStep("Add new set", () => carousel.UpdateBeatmapSet(secondAdded));
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);
setSelected(set_count + 1, 1);
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 1)));
AddStep("Remove set", () => carousel.RemoveBeatmapSet(secondAdded));
checkVisibleItemCount(false, set_count);
@ -372,7 +375,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
sets.Clear();
var rulesetBeatmapSet = createTestBeatmapSet(1);
var rulesetBeatmapSet = TestResources.CreateTestBeatmapSetInfo(1);
var taikoRuleset = rulesets.AvailableRulesets.ElementAt(1);
rulesetBeatmapSet.Beatmaps.ForEach(b =>
{
@ -397,12 +400,29 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
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));
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));
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]
@ -412,20 +432,21 @@ namespace osu.Game.Tests.Visual.SongSelect
for (int i = 0; i < 20; i++)
{
// index + 1 because we are using OnlineID which should never be zero.
var set = createTestBeatmapSet(i + 1);
var set = TestResources.CreateTestBeatmapSetInfo();
set.Metadata.Artist = "same artist";
set.Metadata.Title = "same title";
sets.Add(set);
}
int idOffset = sets.First().OnlineID ?? 0;
loadBeatmaps(sets);
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));
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]
@ -435,7 +456,7 @@ namespace osu.Game.Tests.Visual.SongSelect
for (int i = 0; i < 3; i++)
{
var set = createTestBeatmapSet(i);
var set = TestResources.CreateTestBeatmapSetInfo(3);
set.Beatmaps[0].StarRating = 3 - i;
set.Beatmaps[2].StarRating = 6 + i;
sets.Add(set);
@ -504,7 +525,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("create hidden set", () =>
{
hidingSet = createTestBeatmapSet(1);
hidingSet = TestResources.CreateTestBeatmapSetInfo(3);
hidingSet.Beatmaps[1].Hidden = true;
hiddenList.Clear();
@ -551,7 +572,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("add mixed ruleset beatmapset", () =>
{
testMixed = createTestBeatmapSet(set_count + 1);
testMixed = TestResources.CreateTestBeatmapSetInfo(3);
for (int i = 0; i <= 2; i++)
{
@ -574,7 +595,7 @@ namespace osu.Game.Tests.Visual.SongSelect
BeatmapSetInfo testSingle = null;
AddStep("add single ruleset beatmapset", () =>
{
testSingle = createTestBeatmapSet(set_count + 2);
testSingle = TestResources.CreateTestBeatmapSetInfo(3);
testSingle.Beatmaps.ForEach(b =>
{
b.Ruleset = rulesets.AvailableRulesets.ElementAt(1);
@ -594,7 +615,7 @@ namespace osu.Game.Tests.Visual.SongSelect
List<BeatmapSetInfo> manySets = new List<BeatmapSetInfo>();
for (int i = 1; i <= 50; i++)
manySets.Add(createTestBeatmapSet(i));
manySets.Add(TestResources.CreateTestBeatmapSetInfo(3));
loadBeatmaps(manySets);
@ -627,18 +648,11 @@ namespace osu.Game.Tests.Visual.SongSelect
{
for (int i = 0; i < 10; i++)
{
var set = createTestBeatmapSet(i);
foreach (var b in set.Beatmaps)
manySets.Add(TestResources.CreateTestBeatmapSetInfo(3, new[]
{
// all taiko except for first
int ruleset = i > 0 ? 1 : 0;
b.Ruleset = rulesets.GetRuleset(ruleset);
b.RulesetID = ruleset;
}
manySets.Add(set);
rulesets.GetRuleset(i > 0 ? 1 : 0)
}));
}
});
@ -660,7 +674,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("Restore different ruleset filter", () =>
{
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()));
@ -678,7 +692,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("add mixed difficulty set", () =>
{
set = createTestBeatmapSet(1);
set = TestResources.CreateTestBeatmapSetInfo(1);
set.Beatmaps.Clear();
for (int i = 1; i <= 15; i++)
@ -730,7 +744,11 @@ namespace osu.Game.Tests.Visual.SongSelect
beatmapSets = new List<BeatmapSetInfo>();
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());
@ -766,7 +784,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () =>
{
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);
});
@ -834,88 +852,6 @@ namespace osu.Game.Tests.Visual.SongSelect
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
{
public bool PendingFilterTask => PendingFilter != null;

View File

@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.SongSelect
beatmaps.Add(testBeatmap);
AddStep("set ruleset", () => Ruleset.Value = rulesetInfo);
setRuleset(rulesetInfo);
selectBeatmap(testBeatmap);
@ -167,6 +167,22 @@ namespace osu.Game.Tests.Visual.SongSelect
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)
{
Container containerBefore = null;
@ -220,7 +236,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Title = "Verrrrry long Title"
},
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(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;
}
@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestBeatmapStates()
{
foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus)))
foreach (BeatmapOnlineStatus status in Enum.GetValues(typeof(BeatmapOnlineStatus)))
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
{

View File

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

View File

@ -3,15 +3,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Utils;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
@ -31,6 +27,7 @@ using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
using osu.Game.Tests.Resources;
using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect
@ -258,8 +255,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
manager.Import(TestResources.CreateTestBeatmapSetInfo(rulesets: usableRulesets)).Wait();
});
}
else
@ -354,7 +351,7 @@ namespace osu.Game.Tests.Visual.SongSelect
target = manager.GetAllUsableBeatmapSets()
.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);
});
@ -385,12 +382,12 @@ namespace osu.Game.Tests.Visual.SongSelect
.Last(b => b.Beatmaps.Any(bi => bi.RulesetID == 0)).Beatmaps.Last();
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 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
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.
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);
@ -665,8 +662,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
manager.Import(TestResources.CreateTestBeatmapSetInfo(3, usableRulesets)).Wait();
});
int previousSetID = 0;
@ -676,11 +673,11 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("record set ID", () => previousSetID = ((IBeatmapSetInfo)Beatmap.Value.BeatmapSetInfo).OnlineID);
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);
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);
@ -705,8 +702,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
manager.Import(TestResources.CreateTestBeatmapSetInfo(3, usableRulesets)).Wait();
});
DrawableCarouselBeatmapSet set = null;
@ -720,11 +717,11 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("Find an icon for different ruleset", () =>
{
difficultyIcon = set.ChildrenOfType<FilterableDifficultyIcon>()
.FirstOrDefault(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3);
.FirstOrDefault(icon => icon.Item.BeatmapInfo.Ruleset.OnlineID == 3);
return difficultyIcon != null;
});
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
AddAssert("Check ruleset is osu!", () => Ruleset.Value.OnlineID == 0);
int previousSetID = 0;
@ -737,7 +734,7 @@ namespace osu.Game.Tests.Visual.SongSelect
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 is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.OnlineID == 3);
@ -754,8 +751,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import huge difficulty count map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
imported = manager.Import(createTestBeatmapSet(usableRulesets, 50)).Result.Value;
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
imported = manager.Import(TestResources.CreateTestBeatmapSetInfo(50, usableRulesets)).Result.Value;
});
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", () =>
{
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", () =>
{
@ -783,7 +780,7 @@ namespace osu.Game.Tests.Visual.SongSelect
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));
}
@ -817,7 +814,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen());
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]
@ -849,7 +846,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen());
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()
@ -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 importForRuleset(int id) => manager.Import(createTestBeatmapSet(rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
private static int importId;
private int getImportId() => ++importId;
private void importForRuleset(int id) => manager.Import(TestResources.CreateTestBeatmapSetInfo(3, rulesets.AvailableRulesets.Where(r => r.OnlineID == id).ToArray())).Wait();
private void checkMusicPlaying(bool 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 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()
{
@ -893,59 +886,13 @@ namespace osu.Game.Tests.Visual.SongSelect
{
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)
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)
{
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));
}
[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 const int flash_layer_height = 150;
@ -145,6 +174,12 @@ namespace osu.Game.Tests.Visual.UserInterface
set => base.AllowMistimedEventFiring = value;
}
public new double EarlyActivationMilliseconds
{
get => base.EarlyActivationMilliseconds;
set => base.EarlyActivationMilliseconds = value;
}
private readonly InfoString timingPointCount;
private readonly InfoString currentTimingPoint;
private readonly InfoString beatCount;

View File

@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.UserInterface
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(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];

View File

@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface
[Test]
public void TestLabelledEnumDropdown()
=> AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown<BeatmapSetOnlineStatus>
=> AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown<BeatmapOnlineStatus>
{
Label = @"Beatmap status",
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();
}

View File

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

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