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

Merge branch 'master' into fl-opacity

This commit is contained in:
Dan Balasescu 2021-11-30 10:00:44 +09:00
commit e6e6e2d951
431 changed files with 6784 additions and 3825 deletions

View File

@ -0,0 +1 @@
osu.iOS

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.jetbrains.rider.android.RiderAndroidMiscFileCreationComponent">
<option name="ENSURE_MISC_FILE_EXISTS" value="true" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RiderProjectSettingsUpdater">
<option name="vcsConfiguration" value="2" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -8,4 +8,5 @@ M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900)
T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods.
T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods.
M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast<T>() instead.
T:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods.
M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast<T>() instead.

View File

@ -12,7 +12,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<ItemGroup>

View File

@ -13,14 +13,14 @@ namespace osu.Game.Rulesets.EmptyFreeform
{
public class EmptyFreeformDifficultyCalculator : DifficultyCalculator
{
public EmptyFreeformDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
public EmptyFreeformDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
return new DifficultyAttributes(mods, skills, 0);
return new DifficultyAttributes(mods, 0);
}
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();

View File

@ -30,8 +30,8 @@ namespace osu.Game.Rulesets.EmptyFreeform
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) =>
new EmptyFreeformBeatmapConverter(beatmap, this);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) =>
new EmptyFreeformDifficultyCalculator(this, beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) =>
new EmptyFreeformDifficultyCalculator(RulesetInfo, beatmap);
public override IEnumerable<Mod> GetModsFor(ModType type)
{

View File

@ -12,7 +12,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<ItemGroup>

View File

@ -13,14 +13,14 @@ namespace osu.Game.Rulesets.Pippidon
{
public class PippidonDifficultyCalculator : DifficultyCalculator
{
public PippidonDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
public PippidonDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
return new DifficultyAttributes(mods, skills, 0);
return new DifficultyAttributes(mods, 0);
}
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();

View File

@ -26,8 +26,8 @@ namespace osu.Game.Rulesets.Pippidon
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) =>
new PippidonBeatmapConverter(beatmap, this);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) =>
new PippidonDifficultyCalculator(this, beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) =>
new PippidonDifficultyCalculator(RulesetInfo, beatmap);
public override IEnumerable<Mod> GetModsFor(ModType type)
{

View File

@ -12,7 +12,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<ItemGroup>

View File

@ -13,14 +13,14 @@ namespace osu.Game.Rulesets.EmptyScrolling
{
public class EmptyScrollingDifficultyCalculator : DifficultyCalculator
{
public EmptyScrollingDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
public EmptyScrollingDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
return new DifficultyAttributes(mods, skills, 0);
return new DifficultyAttributes(mods, 0);
}
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.EmptyScrolling
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new EmptyScrollingBeatmapConverter(beatmap, this);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new EmptyScrollingDifficultyCalculator(this, beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new EmptyScrollingDifficultyCalculator(RulesetInfo, beatmap);
public override IEnumerable<Mod> GetModsFor(ModType type)
{

View File

@ -12,7 +12,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<ItemGroup>

View File

@ -13,14 +13,14 @@ namespace osu.Game.Rulesets.Pippidon
{
public class PippidonDifficultyCalculator : DifficultyCalculator
{
public PippidonDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
public PippidonDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
return new DifficultyAttributes(mods, skills, 0);
return new DifficultyAttributes(mods, 0);
}
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Pippidon
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new PippidonBeatmapConverter(beatmap, this);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new PippidonDifficultyCalculator(this, beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new PippidonDifficultyCalculator(RulesetInfo, beatmap);
public override IEnumerable<Mod> GetModsFor(ModType type)
{

View File

@ -51,11 +51,11 @@
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1026.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1108.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1112.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1127.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. -->
<PackageReference Include="Realm" Version="10.6.0" />
<PackageReference Include="Realm" Version="10.7.1" />
</ItemGroup>
</Project>

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

@ -93,6 +93,11 @@ namespace osu.Desktop
protected override UpdateManager CreateUpdateManager()
{
string packageManaged = Environment.GetEnvironmentVariable("OSU_EXTERNAL_UPDATE_PROVIDER");
if (!string.IsNullOrEmpty(packageManaged))
return new NoActionUpdateManager();
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.Windows:

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!");
}
}
@ -183,6 +186,19 @@ namespace osu.Desktop.Updater
}
});
}
public override void Close()
{
// cancelling updates is not currently supported by the underlying updater.
// only allow dismissing for now.
switch (State)
{
case ProgressNotificationState.Cancelled:
base.Close();
break;
}
}
}
private class SquirrelLogger : Splat.ILogger, IDisposable

View File

@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
</ItemGroup>
<ItemGroup>

View File

@ -1,6 +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 osu.Framework.iOS;
using UIKit;
namespace osu.Game.Rulesets.Catch.Tests.iOS
@ -9,7 +10,7 @@ namespace osu.Game.Rulesets.Catch.Tests.iOS
{
public static void Main(string[] args)
{
UIApplication.Main(args, "GameUIApplication", "AppDelegate");
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
}
}
}

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new CatchModDoubleTime());
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset(), beatmap);
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset().RulesetInfo, beatmap);
protected override Ruleset CreateRuleset() => new CatchRuleset();
}

View File

@ -168,6 +168,28 @@ namespace osu.Game.Rulesets.Catch.Tests
checkHyperDash(false);
}
[Test]
public void TestLastBananaShouldClearPlateOnMiss()
{
AddStep("catch fruit", () => attemptCatch(new Fruit()));
checkPlate(1);
AddStep("miss banana", () => attemptCatch(new Banana { X = 100 }));
checkPlate(1);
AddStep("miss last banana", () => attemptCatch(new Banana { LastInCombo = true, X = 100 }));
checkPlate(0);
}
[Test]
public void TestLastBananaShouldClearPlateOnCatch()
{
AddStep("catch fruit", () => attemptCatch(new Fruit()));
checkPlate(1);
AddStep("catch banana", () => attemptCatch(new Banana()));
checkPlate(2);
AddStep("catch last banana", () => attemptCatch(new Banana { LastInCombo = true }));
checkPlate(0);
}
[Test]
public void TestCatcherRandomStacking()
{

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Tests
var controlPointInfo = new ControlPointInfo();
controlPointInfo.Add(0, new TimingControlPoint());
WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
IWorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
{
HitObjects = new List<HitObject> { new Fruit() },
BeatmapInfo = new BeatmapInfo

View File

@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">

View File

@ -178,7 +178,7 @@ namespace osu.Game.Rulesets.Catch
return base.GetDisplayNameForHitResult(result);
}
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(RulesetInfo, beatmap);
public override ISkin CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => new CatchLegacySkinTransformer(skin);

View File

@ -1,12 +1,35 @@
// 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 Newtonsoft.Json;
using osu.Game.Rulesets.Difficulty;
namespace osu.Game.Rulesets.Catch.Difficulty
{
public class CatchDifficultyAttributes : DifficultyAttributes
{
[JsonProperty("approach_rate")]
public double ApproachRate { get; set; }
public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes()
{
foreach (var v in base.ToDatabaseAttributes())
yield return v;
// Todo: osu!catch should not output star rating in the 'aim' attribute.
yield return (ATTRIB_ID_AIM, StarRating);
yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate);
yield return (ATTRIB_ID_MAX_COMBO, MaxCombo);
}
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values)
{
base.FromDatabaseAttributes(values);
StarRating = values[ATTRIB_ID_AIM];
ApproachRate = values[ATTRIB_ID_APPROACH_RATE];
MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO];
}
}
}

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
private float halfCatcherWidth;
public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
public CatchDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
return new CatchDifficultyAttributes { Mods = mods, Skills = skills };
return new CatchDifficultyAttributes { Mods = mods };
// this is the same as osu!, so there's potential to share the implementation... maybe
double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
@ -42,7 +42,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty
Mods = mods,
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType<JuiceStream>().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)),
Skills = skills
};
}

View File

@ -210,6 +210,7 @@ namespace osu.Game.Rulesets.Catch.UI
catchResult.CatcherAnimationState = CurrentState;
catchResult.CatcherHyperDash = HyperDashing;
// Ignore JuiceStreams and BananaShowers
if (!(drawableObject is DrawablePalpableCatchHitObject palpableObject)) return;
var hitObject = palpableObject.HitObject;
@ -244,6 +245,14 @@ namespace osu.Game.Rulesets.Catch.UI
CurrentState = hitObject.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle;
else if (!(hitObject is Banana))
CurrentState = CatcherAnimationState.Fail;
if (palpableObject.HitObject.LastInCombo)
{
if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result))
Explode();
else
Drop();
}
}
public void OnRevertResult(DrawableCatchHitObject drawableObject, JudgementResult result)

View File

@ -6,11 +6,9 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osuTK;
@ -72,18 +70,6 @@ namespace osu.Game.Rulesets.Catch.UI
public void OnNewResult(DrawableCatchHitObject hitObject, JudgementResult result)
{
Catcher.OnNewResult(hitObject, result);
if (!result.Type.IsScorable())
return;
if (hitObject.HitObject.LastInCombo)
{
if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result))
Catcher.Explode();
else
Catcher.Drop();
}
comboDisplay.OnNewResult(hitObject, result);
}

View File

@ -1,6 +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 osu.Framework.iOS;
using UIKit;
namespace osu.Game.Rulesets.Mania.Tests.iOS
@ -9,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.Tests.iOS
{
public static void Main(string[] args)
{
UIApplication.Main(args, "GameUIApplication", "AppDelegate");
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
}
}
}

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Tests
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new ManiaModDoubleTime());
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset(), beatmap);
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset().RulesetInfo, beatmap);
protected override Ruleset CreateRuleset() => new ManiaRuleset();
}

View File

@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">

View File

@ -1,13 +1,38 @@
// 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 Newtonsoft.Json;
using osu.Game.Rulesets.Difficulty;
namespace osu.Game.Rulesets.Mania.Difficulty
{
public class ManiaDifficultyAttributes : DifficultyAttributes
{
[JsonProperty("great_hit_window")]
public double GreatHitWindow { get; set; }
[JsonProperty("score_multiplier")]
public double ScoreMultiplier { get; set; }
public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes()
{
foreach (var v in base.ToDatabaseAttributes())
yield return v;
// Todo: osu!mania doesn't output MaxCombo attribute for some reason.
yield return (ATTRIB_ID_STRAIN, StarRating);
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier);
}
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values)
{
base.FromDatabaseAttributes(values);
StarRating = values[ATTRIB_ID_STRAIN];
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER];
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Extensions;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
@ -28,17 +29,17 @@ namespace osu.Game.Rulesets.Mania.Difficulty
private readonly bool isForCurrentRuleset;
private readonly double originalOverallDifficulty;
public ManiaDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
public ManiaDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
originalOverallDifficulty = beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty;
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.MatchesOnlineID(ruleset);
originalOverallDifficulty = beatmap.BeatmapInfo.Difficulty.OverallDifficulty;
}
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
return new ManiaDifficultyAttributes { Mods = mods, Skills = skills };
return new ManiaDifficultyAttributes { Mods = mods };
HitWindows hitWindows = new ManiaHitWindows();
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
@ -50,7 +51,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
GreatHitWindow = Math.Ceiling(getHitWindow300(mods) / clockRate),
ScoreMultiplier = getScoreMultiplier(mods),
MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1),
Skills = skills
};
}

View File

@ -272,7 +272,7 @@ namespace osu.Game.Rulesets.Mania
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania };
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(RulesetInfo, beatmap);
public int LegacyID => 3;

View File

@ -1,6 +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 osu.Framework.iOS;
using UIKit;
namespace osu.Game.Rulesets.Osu.Tests.iOS
@ -9,7 +10,7 @@ namespace osu.Game.Rulesets.Osu.Tests.iOS
{
public static void Main(string[] args)
{
UIApplication.Main(args, "GameUIApplication", "AppDelegate");
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
}
}
}

View File

@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new OsuModDoubleTime());
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap);
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap);
protected override Ruleset CreateRuleset() => new OsuRuleset();
}

View File

@ -5,7 +5,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">

View File

@ -1,21 +1,77 @@
// 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 JetBrains.Annotations;
using Newtonsoft.Json;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Difficulty
{
public class OsuDifficultyAttributes : DifficultyAttributes
{
[JsonProperty("aim_strain")]
public double AimStrain { get; set; }
[JsonProperty("speed_strain")]
public double SpeedStrain { get; set; }
[JsonProperty("flashlight_rating")]
public double FlashlightRating { get; set; }
[JsonProperty("slider_factor")]
public double SliderFactor { get; set; }
[JsonProperty("approach_rate")]
public double ApproachRate { get; set; }
[JsonProperty("overall_difficulty")]
public double OverallDifficulty { get; set; }
public double DrainRate { get; set; }
public int HitCircleCount { get; set; }
public int SliderCount { get; set; }
public int SpinnerCount { get; set; }
public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes()
{
foreach (var v in base.ToDatabaseAttributes())
yield return v;
yield return (ATTRIB_ID_AIM, AimStrain);
yield return (ATTRIB_ID_SPEED, SpeedStrain);
yield return (ATTRIB_ID_OVERALL_DIFFICULTY, OverallDifficulty);
yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate);
yield return (ATTRIB_ID_MAX_COMBO, MaxCombo);
yield return (ATTRIB_ID_STRAIN, StarRating);
if (ShouldSerializeFlashlightRating())
yield return (ATTRIB_ID_FLASHLIGHT, FlashlightRating);
yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor);
}
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values)
{
base.FromDatabaseAttributes(values);
AimStrain = values[ATTRIB_ID_AIM];
SpeedStrain = values[ATTRIB_ID_SPEED];
OverallDifficulty = values[ATTRIB_ID_OVERALL_DIFFICULTY];
ApproachRate = values[ATTRIB_ID_APPROACH_RATE];
MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO];
StarRating = values[ATTRIB_ID_STRAIN];
FlashlightRating = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT);
SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR];
}
// Used implicitly by Newtonsoft.Json to not serialize flashlight property in some cases.
[UsedImplicitly]
public bool ShouldSerializeFlashlightRating() => Mods.Any(m => m is ModFlashlight);
}
}

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private const double difficulty_multiplier = 0.0675;
private double hitWindowGreat;
public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
public OsuDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
return new OsuDifficultyAttributes { Mods = mods, Skills = skills };
return new OsuDifficultyAttributes { Mods = mods };
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
@ -85,7 +85,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
HitCircleCount = hitCirclesCount,
SliderCount = sliderCount,
SpinnerCount = spinnerCount,
Skills = skills
};
}

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

@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Osu
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetOsu };
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(RulesetInfo, beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => new OsuPerformanceCalculator(this, attributes, score);

View File

@ -1,6 +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 osu.Framework.iOS;
using UIKit;
namespace osu.Game.Rulesets.Taiko.Tests.iOS
@ -9,7 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.iOS
{
public static void Main(string[] args)
{
UIApplication.Main(args, "GameUIApplication", "AppDelegate");
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
}
}
}

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
var controlPointInfo = new ControlPointInfo();
controlPointInfo.Add(0, new TimingControlPoint());
WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
IWorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
{
HitObjects = new List<HitObject> { new Hit { Type = HitType.Centre } },
BeatmapInfo = new BeatmapInfo

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new TaikoModDoubleTime());
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap);
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset().RulesetInfo, beatmap);
protected override Ruleset CreateRuleset() => new TaikoRuleset();
}

View File

@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">

View File

@ -1,16 +1,46 @@
// 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 Newtonsoft.Json;
using osu.Game.Rulesets.Difficulty;
namespace osu.Game.Rulesets.Taiko.Difficulty
{
public class TaikoDifficultyAttributes : DifficultyAttributes
{
[JsonProperty("stamina_strain")]
public double StaminaStrain { get; set; }
[JsonProperty("rhythm_strain")]
public double RhythmStrain { get; set; }
[JsonProperty("colour_strain")]
public double ColourStrain { get; set; }
[JsonProperty("approach_rate")]
public double ApproachRate { get; set; }
[JsonProperty("great_hit_window")]
public double GreatHitWindow { get; set; }
public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes()
{
foreach (var v in base.ToDatabaseAttributes())
yield return v;
yield return (ATTRIB_ID_MAX_COMBO, MaxCombo);
yield return (ATTRIB_ID_STRAIN, StarRating);
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
}
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values)
{
base.FromDatabaseAttributes(values);
MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO];
StarRating = values[ATTRIB_ID_STRAIN];
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
}
}
}

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private const double colour_skill_multiplier = 0.01;
private const double stamina_skill_multiplier = 0.02;
public TaikoDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
return new TaikoDifficultyAttributes { Mods = mods, Skills = skills };
return new TaikoDifficultyAttributes { Mods = mods };
var colour = (Colour)skills[0];
var rhythm = (Rhythm)skills[1];
@ -96,7 +96,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
ColourStrain = colourRating,
GreatHitWindow = hitWindows.WindowFor(HitResult.Great) / clockRate,
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
Skills = skills
};
}

View File

@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.Taiko
public override HitObjectComposer CreateHitObjectComposer() => new TaikoHitObjectComposer(this);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(RulesetInfo, beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => new TaikoPerformanceCalculator(this, attributes, score);

View File

@ -1,6 +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 osu.Framework.iOS;
using UIKit;
namespace osu.Game.Tests.iOS
@ -9,7 +10,7 @@ namespace osu.Game.Tests.iOS
{
public static void Main(string[] args)
{
UIApplication.Main(args, "GameUIApplication", "AppDelegate");
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
}
}
}

View File

@ -116,8 +116,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("Insane", beatmapInfo.DifficultyName);
Assert.AreEqual(string.Empty, metadata.Source);
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", metadata.Tags);
Assert.AreEqual(557821, beatmapInfo.OnlineBeatmapID);
Assert.AreEqual(241526, beatmapInfo.BeatmapSet.OnlineBeatmapSetID);
Assert.AreEqual(557821, beatmapInfo.OnlineID);
Assert.AreEqual(241526, beatmapInfo.BeatmapSet.OnlineID);
}
}

View File

@ -30,7 +30,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]);

View File

@ -31,7 +31,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
var beatmap = decodeAsJson(normal);
var meta = beatmap.BeatmapInfo.Metadata;
Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID);
Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineID);
Assert.AreEqual("Soleily", meta.Artist);
Assert.AreEqual("Soleily", meta.ArtistUnicode);
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);

View File

@ -16,6 +16,7 @@ using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.IO;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Notifications;
@ -363,15 +364,15 @@ namespace osu.Game.Tests.Beatmaps.IO
var files = osu.Dependencies.Get<FileStore>();
long originalLength;
using (var stream = files.Storage.GetStream(firstFile.FileInfo.StoragePath))
using (var stream = files.Storage.GetStream(firstFile.FileInfo.GetStoragePath()))
originalLength = stream.Length;
using (var stream = files.Storage.GetStream(firstFile.FileInfo.StoragePath, FileAccess.Write, FileMode.Create))
using (var stream = files.Storage.GetStream(firstFile.FileInfo.GetStoragePath(), FileAccess.Write, FileMode.Create))
stream.WriteByte(0);
var importedSecondTime = await LoadOszIntoOsu(osu);
using (var stream = files.Storage.GetStream(firstFile.FileInfo.StoragePath))
using (var stream = files.Storage.GetStream(firstFile.FileInfo.GetStoragePath()))
Assert.AreEqual(stream.Length, originalLength, "Corruption was not fixed on second import");
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
@ -542,7 +543,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var imported = await LoadOszIntoOsu(osu);
foreach (var b in imported.Beatmaps)
b.OnlineBeatmapID = null;
b.OnlineID = null;
osu.Dependencies.Get<BeatmapManager>().Update(imported);
@ -581,21 +582,21 @@ namespace osu.Game.Tests.Beatmaps.IO
var toImport = new BeatmapSetInfo
{
OnlineBeatmapSetID = 1,
OnlineID = 1,
Metadata = metadata,
Beatmaps = new List<BeatmapInfo>
Beatmaps =
{
new BeatmapInfo
{
OnlineBeatmapID = 2,
OnlineID = 2,
Metadata = metadata,
BaseDifficulty = difficulty
},
new BeatmapInfo
{
OnlineBeatmapID = 2,
OnlineID = 2,
Metadata = metadata,
Status = BeatmapSetOnlineStatus.Loved,
Status = BeatmapOnlineStatus.Loved,
BaseDifficulty = difficulty
}
}
@ -606,8 +607,8 @@ namespace osu.Game.Tests.Beatmaps.IO
var imported = await manager.Import(toImport);
Assert.NotNull(imported);
Assert.AreEqual(null, imported.Value.Beatmaps[0].OnlineBeatmapID);
Assert.AreEqual(null, imported.Value.Beatmaps[1].OnlineBeatmapID);
Assert.AreEqual(null, imported.Value.Beatmaps[0].OnlineID);
Assert.AreEqual(null, imported.Value.Beatmaps[1].OnlineID);
}
finally
{
@ -1049,20 +1050,20 @@ 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)
{
IEnumerable<BeatmapSetInfo> resultSets = null;
var store = osu.Dependencies.Get<BeatmapManager>();
waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any(),
waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineID == 241526)).Any(),
@"BeatmapSet did not import to the database in allocated time.", timeout);
// ensure we were stored to beatmap database backing...
Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1).");
IEnumerable<BeatmapInfo> queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0);
IEnumerable<BeatmapSetInfo> queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526);
IEnumerable<BeatmapInfo> queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineID == 241526 && s.BaseDifficultyID > 0);
IEnumerable<BeatmapSetInfo> queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineID == 241526);
// if we don't re-check here, the set will be inserted but the beatmaps won't be present yet.
waitForOrAssert(() => queryBeatmaps().Count() == 12,
@ -1078,7 +1079,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var set = queryBeatmapSets().First();
foreach (BeatmapInfo b in set.Beatmaps)
Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID));
Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineID == b.OnlineID));
Assert.IsTrue(set.Beatmaps.Count > 0);
var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Any() == true);

View File

@ -56,7 +56,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var meta = beatmap.Metadata;
Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID);
Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineID);
Assert.AreEqual("Soleily", meta.Artist);
Assert.AreEqual("Soleily", meta.ArtistUnicode);
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);

View File

@ -100,8 +100,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 +110,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 +120,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 +130,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()));
@ -164,9 +164,9 @@ namespace osu.Game.Tests.Beatmaps
{
public Func<DifficultyCacheLookup, StarDifficulty> ComputeDifficulty { get; set; }
protected override Task<StarDifficulty> ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default)
protected override Task<StarDifficulty?> ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default)
{
return Task.FromResult(ComputeDifficulty?.Invoke(lookup) ?? new StarDifficulty(BASE_STARS, 0));
return Task.FromResult<StarDifficulty?>(ComputeDifficulty?.Invoke(lookup) ?? new StarDifficulty(BASE_STARS, 0));
}
}
}

View File

@ -0,0 +1,115 @@
// 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 System.Threading;
using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
namespace osu.Game.Tests.Beatmaps
{
[TestFixture]
public class WorkingBeatmapTest
{
[Test]
public void TestGetPlayableSuccess()
{
var working = new TestNeverLoadsWorkingBeatmap();
working.ResetEvent.Set();
Assert.NotNull(working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo));
}
[Test]
public void TestGetPlayableCancellationToken()
{
var working = new TestNeverLoadsWorkingBeatmap();
var cts = new CancellationTokenSource();
var loadStarted = new ManualResetEventSlim();
var loadCompleted = new ManualResetEventSlim();
Task.Factory.StartNew(() =>
{
loadStarted.Set();
Assert.Throws<OperationCanceledException>(() => working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>(), cts.Token));
loadCompleted.Set();
}, TaskCreationOptions.LongRunning);
Assert.IsTrue(loadStarted.Wait(10000));
cts.Cancel();
Assert.IsTrue(loadCompleted.Wait(10000));
working.ResetEvent.Set();
}
[Test]
public void TestGetPlayableDefaultTimeout()
{
var working = new TestNeverLoadsWorkingBeatmap();
Assert.Throws(Is.InstanceOf<TimeoutException>(), () => working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo));
working.ResetEvent.Set();
}
[Test]
public void TestGetPlayableRulesetLoadFailure()
{
var working = new TestWorkingBeatmap(new Beatmap());
// by default mocks return nulls if not set up, which is actually desired here to simulate a ruleset load failure scenario.
var ruleset = new Mock<IRulesetInfo>();
Assert.Throws<RulesetLoadException>(() => working.GetPlayableBeatmap(ruleset.Object));
}
public class TestNeverLoadsWorkingBeatmap : TestWorkingBeatmap
{
public ManualResetEventSlim ResetEvent = new ManualResetEventSlim();
public TestNeverLoadsWorkingBeatmap()
: base(new Beatmap())
{
}
protected override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => new TestConverter(beatmap, ResetEvent);
public class TestConverter : IBeatmapConverter
{
private readonly ManualResetEventSlim resetEvent;
public TestConverter(IBeatmap beatmap, ManualResetEventSlim resetEvent)
{
this.resetEvent = resetEvent;
Beatmap = beatmap;
}
public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
protected virtual void OnObjectConverted(HitObject arg1, IEnumerable<HitObject> arg2) => ObjectConverted?.Invoke(arg1, arg2);
public IBeatmap Beatmap { get; }
public bool CanConvert() => true;
public IBeatmap Convert(CancellationToken cancellationToken = default)
{
resetEvent.Wait(cancellationToken);
return new OsuBeatmap();
}
}
}
}
}

View File

@ -12,8 +12,10 @@ 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;
using osu.Game.IO.Archives;
using osu.Game.Models;
using osu.Game.Overlays.Notifications;
@ -73,6 +75,24 @@ namespace osu.Game.Tests.Database
});
}
[Test]
public void TestAccessFileAfterImport()
{
RunTestWithRealmAsync(async (realmFactory, storage) =>
{
using var importer = new BeatmapImporter(realmFactory, storage);
using var store = new RealmRulesetStore(realmFactory, storage);
var imported = await LoadOszIntoStore(importer, realmFactory.Context);
var beatmap = imported.Beatmaps.First();
var file = beatmap.File;
Assert.NotNull(file);
Assert.AreEqual(beatmap.Hash, file!.File.Hash);
});
}
[Test]
public void TestImportThenDelete()
{
@ -348,15 +368,15 @@ namespace osu.Game.Tests.Database
var firstFile = imported.Files.First();
long originalLength;
using (var stream = storage.GetStream(firstFile.File.StoragePath))
using (var stream = storage.GetStream(firstFile.File.GetStoragePath()))
originalLength = stream.Length;
using (var stream = storage.GetStream(firstFile.File.StoragePath, FileAccess.Write, FileMode.Create))
using (var stream = storage.GetStream(firstFile.File.GetStoragePath(), FileAccess.Write, FileMode.Create))
stream.WriteByte(0);
var importedSecondTime = await LoadOszIntoStore(importer, realmFactory.Context);
using (var stream = storage.GetStream(firstFile.File.StoragePath))
using (var stream = storage.GetStream(firstFile.File.GetStoragePath()))
Assert.AreEqual(stream.Length, originalLength, "Corruption was not fixed on second import");
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
@ -455,7 +475,7 @@ namespace osu.Game.Tests.Database
}
[Test]
public void TestImportThenDeleteThenImport()
public void TestImportThenDeleteThenImportOptimisedPath()
{
RunTestWithRealmAsync(async (realmFactory, storage) =>
{
@ -466,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);
});
}
@ -531,7 +579,7 @@ namespace osu.Game.Tests.Database
new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), metadata)
{
OnlineID = 2,
Status = BeatmapSetOnlineStatus.Loved,
Status = BeatmapOnlineStatus.Loved,
}
}
};
@ -848,5 +896,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

@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Logging;
using osu.Game.Extensions;
using osu.Game.Models;
using osu.Game.Stores;
@ -28,7 +29,7 @@ namespace osu.Game.Tests.Database
realm.Write(() => files.Add(testData, realm));
Assert.True(files.Storage.Exists("0/05/054edec1d0211f624fed0cbca9d4f9400b0e491c43742af2c5b0abebf0c990d8"));
Assert.True(files.Storage.Exists(realm.All<RealmFile>().First().StoragePath));
Assert.True(files.Storage.Exists(realm.All<RealmFile>().First().GetStoragePath()));
});
}
@ -74,7 +75,7 @@ namespace osu.Game.Tests.Database
Logger.Log($"Import complete at {timer.ElapsedMilliseconds}");
string path = file.StoragePath;
string path = file.GetStoragePath();
Assert.True(realm.All<RealmFile>().Any());
Assert.True(files.Storage.Exists(path));
@ -98,7 +99,7 @@ namespace osu.Game.Tests.Database
var file = realm.Write(() => files.Add(new MemoryStream(new byte[] { 0, 1, 2, 3 }), realm));
string path = file.StoragePath;
string path = file.GetStoragePath();
Assert.True(realm.All<RealmFile>().Any());
Assert.True(files.Storage.Exists(path));

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,24 +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 TestAccessAfterAttach()
{
RunTestWithRealm((realmFactory, _) =>
{
var beatmap = new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata());
var liveBeatmap = beatmap.ToLive();
using (var context = realmFactory.CreateContext())
context.Write(r => r.Add(beatmap));
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 TestValueAccessWithOpenContext()
{
RunTestWithRealm((realmFactory, _) =>
{
RealmLive<RealmBeatmap>? liveBeatmap = null;
ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() =>
{
using (var threadContext = realmFactory.CreateContext())
@ -72,7 +104,7 @@ namespace osu.Game.Tests.Database
{
RunTestWithRealm((realmFactory, _) =>
{
RealmLive<RealmBeatmap>? liveBeatmap = null;
ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() =>
{
using (var threadContext = realmFactory.CreateContext())
@ -101,7 +133,7 @@ namespace osu.Game.Tests.Database
{
RunTestWithRealm((realmFactory, _) =>
{
RealmLive<RealmBeatmap>? liveBeatmap = null;
ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() =>
{
using (var threadContext = realmFactory.CreateContext())
@ -127,7 +159,7 @@ namespace osu.Game.Tests.Database
{
RunTestWithRealm((realmFactory, _) =>
{
RealmLive<RealmBeatmap>? liveBeatmap = null;
ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() =>
{
using (var threadContext = realmFactory.CreateContext())
@ -160,7 +192,7 @@ namespace osu.Game.Tests.Database
using (var updateThreadContext = realmFactory.CreateContext())
{
updateThreadContext.All<RealmBeatmap>().SubscribeForNotifications(gotChange);
RealmLive<RealmBeatmap>? liveBeatmap = null;
ILive<RealmBeatmap>? liveBeatmap = null;
Task.Factory.StartNew(() =>
{

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

@ -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

@ -156,7 +156,7 @@ namespace osu.Game.Tests.Mods
throw new System.NotImplementedException();
}
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap)
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap)
{
throw new System.NotImplementedException();
}

View File

@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Extensions;
namespace osu.Game.Tests.NonVisual
{
@ -12,10 +13,11 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestOnlineWithOnline()
{
var ourInfo = new BeatmapSetInfo { OnlineBeatmapSetID = 123 };
var otherInfo = new BeatmapSetInfo { OnlineBeatmapSetID = 123 };
var ourInfo = new BeatmapSetInfo { OnlineID = 123 };
var otherInfo = new BeatmapSetInfo { OnlineID = 123 };
Assert.AreEqual(ourInfo, otherInfo);
Assert.AreNotEqual(ourInfo, otherInfo);
Assert.IsTrue(ourInfo.MatchesOnlineID(otherInfo));
}
[Test]
@ -30,10 +32,11 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestDatabasedWithOnline()
{
var ourInfo = new BeatmapSetInfo { ID = 123, OnlineBeatmapSetID = 12 };
var otherInfo = new BeatmapSetInfo { OnlineBeatmapSetID = 12 };
var ourInfo = new BeatmapSetInfo { ID = 123, OnlineID = 12 };
var otherInfo = new BeatmapSetInfo { OnlineID = 12 };
Assert.AreEqual(ourInfo, otherInfo);
Assert.AreNotEqual(ourInfo, otherInfo);
Assert.IsTrue(ourInfo.MatchesOnlineID(otherInfo));
}
[Test]

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
};
@ -207,8 +207,8 @@ namespace osu.Game.Tests.NonVisual.Filtering
public void TestCriteriaMatchingBeatmapIDs(string query, bool filtered)
{
var beatmap = getExampleBeatmap();
beatmap.OnlineBeatmapID = 20201010;
beatmap.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = 1535 };
beatmap.OnlineID = 20201010;
beatmap.BeatmapSet = new BeatmapSetInfo { OnlineID = 1535 };
var criteria = new FilterCriteria { SearchText = query };
var carouselItem = new CarouselBeatmap(beatmap);

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

@ -72,7 +72,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
RoomManager.CreateRoom(newRoom);
});
AddUntilStep("wait for room join", () => Client.Room != null);
AddUntilStep("wait for room join", () => RoomJoined);
checkPlayingUserCount(1);
}

View File

@ -128,7 +128,7 @@ namespace osu.Game.Tests.Online
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException();
public override string Description { get; } = string.Empty;
public override string ShortName { get; } = string.Empty;

View File

@ -90,7 +90,7 @@ namespace osu.Game.Tests.Online
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new System.NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new System.NotImplementedException();
public override string Description { get; } = string.Empty;
public override string ShortName { get; } = string.Empty;

View File

@ -12,14 +12,14 @@ 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
{
OnlineBeatmapSetID = 1,
OnlineID = 1,
Metadata = new BeatmapMetadata
{
Artist = "test author",
@ -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]
@ -58,7 +60,7 @@ namespace osu.Game.Tests.Online
testBeatmapInfo = getTestBeatmapInfo(testBeatmapFile);
testBeatmapSet = testBeatmapInfo.BeatmapSet;
var existing = beatmaps.QueryBeatmapSet(s => s.OnlineBeatmapSetID == testBeatmapSet.OnlineBeatmapSetID);
var existing = beatmaps.QueryBeatmapSet(s => s.OnlineID == testBeatmapSet.OnlineID);
if (existing != null)
beatmaps.Delete(existing);
@ -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));
@ -101,10 +103,10 @@ namespace osu.Game.Tests.Online
AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).Wait());
addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable);
AddStep("delete beatmap", () => beatmaps.Delete(beatmaps.QueryBeatmapSet(b => b.OnlineBeatmapSetID == testBeatmapSet.OnlineBeatmapSetID)));
AddStep("delete beatmap", () => beatmaps.Delete(beatmaps.QueryBeatmapSet(b => b.OnlineID == testBeatmapSet.OnlineID)));
addAvailabilityCheckStep("state not downloaded", BeatmapAvailability.NotDownloaded);
AddStep("undelete beatmap", () => beatmaps.Undelete(beatmaps.QueryBeatmapSet(b => b.OnlineBeatmapSetID == testBeatmapSet.OnlineBeatmapSetID)));
AddStep("undelete beatmap", () => beatmaps.Undelete(beatmaps.QueryBeatmapSet(b => b.OnlineID == testBeatmapSet.OnlineID)));
addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable);
}
@ -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

@ -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

@ -19,7 +19,7 @@ namespace osu.Game.Tests.Skins
[Resolved]
private BeatmapManager beatmaps { get; set; }
private WorkingBeatmap beatmap;
private IWorkingBeatmap beatmap;
[BackgroundDependencyLoader]
private void load()

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");
@ -79,7 +79,7 @@ namespace osu.Game.Tests.Testing
public override IEnumerable<Mod> GetModsFor(ModType type) => Array.Empty<Mod>();
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => null;
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => null;
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => null;
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null;
}
private class TestRulesetConfigManager : IRulesetConfigManager

View File

@ -428,7 +428,7 @@ namespace osu.Game.Tests.Visual.Background
public float CurrentDim => dimmable.DimLevel;
public Vector2 CurrentBlur => Background.BlurSigma;
public Vector2 CurrentBlur => Background?.BlurSigma ?? Vector2.Zero;
private TestDimmableBackground dimmable;

View File

@ -23,10 +23,18 @@ 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;
[Resolved]
private BeatmapManager beatmaps { get; set; }
#region Test case generation
[BackgroundDependencyLoader]
@ -38,7 +46,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
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);
@ -59,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
@ -69,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";
@ -102,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
@ -180,6 +191,19 @@ namespace osu.Game.Tests.Visual.Beatmaps
request.TriggerSuccess();
return true;
});
ensureSoleilyRemoved();
}
private void ensureSoleilyRemoved()
{
AddUntilStep("ensure manager loaded", () => beatmaps != null);
AddStep("remove map", () =>
{
var beatmap = beatmaps.QueryBeatmapSet(b => b.OnlineID == online_id);
if (beatmap != null) beatmaps.Delete(beatmap);
});
}
private Drawable createContent(OverlayColourScheme colourScheme, Func<APIBeatmapSet, Drawable> creationFunc)

View File

@ -0,0 +1,92 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables.Cards.Buttons;
using osu.Game.Configuration;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Rulesets.Osu;
using osuTK;
namespace osu.Game.Tests.Visual.Beatmaps
{
public class TestSceneBeatmapCardDownloadButton : OsuTestScene
{
private DownloadButton downloadButton;
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
[Resolved]
private OsuConfigManager config { get; set; }
[Test]
public void TestDownloadableBeatmapWithVideo()
{
createButton(true, true);
assertDownloadEnabled(true);
AddStep("prefer no video", () => config.SetValue(OsuSetting.PreferNoVideo, true));
AddAssert("tooltip text correct", () => downloadButton.TooltipText == BeatmapsetsStrings.PanelDownloadNoVideo);
AddStep("prefer video", () => config.SetValue(OsuSetting.PreferNoVideo, false));
AddAssert("tooltip text correct", () => downloadButton.TooltipText == BeatmapsetsStrings.PanelDownloadVideo);
}
[Test]
public void TestUndownloadableBeatmap()
{
createButton(false);
assertDownloadEnabled(false);
AddAssert("tooltip text correct", () => downloadButton.TooltipText == BeatmapsetsStrings.AvailabilityDisabled);
}
private void assertDownloadEnabled(bool enabled) => AddAssert($"download {(enabled ? "enabled" : "disabled")}", () => downloadButton.Enabled.Value == enabled);
private void createButton(bool downloadable, bool hasVideo = false)
{
AddStep("create button", () =>
{
Child = downloadButton = new DownloadButton(downloadable ? getDownloadableBeatmapSet(hasVideo) : getUndownloadableBeatmapSet())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(2)
};
});
}
private APIBeatmapSet getDownloadableBeatmapSet(bool hasVideo)
{
var normal = CreateAPIBeatmapSet(new OsuRuleset().RulesetInfo);
normal.HasVideo = hasVideo;
normal.HasStoryboard = true;
return normal;
}
private APIBeatmapSet getUndownloadableBeatmapSet()
{
var beatmap = CreateAPIBeatmapSet(new OsuRuleset().RulesetInfo);
beatmap.Artist = "test";
beatmap.Title = "undownloadable";
beatmap.AuthorString = "test";
beatmap.HasVideo = true;
beatmap.HasStoryboard = true;
beatmap.Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = true,
ExternalLink = "https://osu.ppy.sh",
};
return beatmap;
}
}
}

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

@ -10,9 +10,13 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Game.Screens.Edit.GameplayTest;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps.IO;
using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editing
@ -58,6 +62,42 @@ namespace osu.Game.Tests.Visual.Editing
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
AddStep("exit player", () => editorPlayer.Exit());
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
AddUntilStep("background has correct params", () =>
{
var background = this.ChildrenOfType<BackgroundScreenBeatmap>().Single();
return background.Colour == Color4.DarkGray && background.BlurAmount.Value == 0;
});
}
[Test]
public void TestGameplayTestWhenTrackRunning()
{
AddStep("start track", () => EditorClock.Start());
AddAssert("sample playback enabled", () => !Editor.SamplePlaybackDisabled.Value);
AddStep("click test gameplay button", () =>
{
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();
InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});
EditorPlayer editorPlayer = null;
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
AddAssert("editor track stopped", () => !EditorClock.IsRunning);
AddAssert("sample playback disabled", () => Editor.SamplePlaybackDisabled.Value);
AddStep("exit player", () => editorPlayer.Exit());
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
AddUntilStep("background has correct params", () =>
{
var background = this.ChildrenOfType<BackgroundScreenBeatmap>().Single();
return background.Colour == Color4.DarkGray && background.BlurAmount.Value == 0;
});
AddStep("start track", () => EditorClock.Start());
AddAssert("sample playback re-enabled", () => !Editor.SamplePlaybackDisabled.Value);
}
[Test]
@ -111,6 +151,35 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("track stopped", () => !Beatmap.Value.Track.IsRunning);
}
[Test]
public void TestSharedClockState()
{
AddStep("seek to 00:01:00", () => EditorClock.Seek(60_000));
AddStep("click test gameplay button", () =>
{
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();
InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});
EditorPlayer editorPlayer = null;
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
GameplayClockContainer gameplayClockContainer = null;
AddStep("fetch gameplay clock", () => gameplayClockContainer = editorPlayer.ChildrenOfType<GameplayClockContainer>().First());
AddUntilStep("gameplay clock running", () => gameplayClockContainer.IsRunning);
AddAssert("gameplay time past 00:01:00", () => gameplayClockContainer.CurrentTime >= 60_000);
double timeAtPlayerExit = 0;
AddWaitStep("wait some", 5);
AddStep("store time before exit", () => timeAtPlayerExit = gameplayClockContainer.CurrentTime);
AddStep("exit player", () => editorPlayer.Exit());
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
AddAssert("time is past player exit", () => EditorClock.CurrentTime >= timeAtPlayerExit);
}
public override void TearDownSteps()
{
base.TearDownSteps();

View File

@ -81,11 +81,11 @@ namespace osu.Game.Tests.Visual.Editing
public class EditorBeatmapContainer : Container
{
private readonly WorkingBeatmap working;
private readonly IWorkingBeatmap working;
public EditorBeatmap EditorBeatmap { get; private set; }
public EditorBeatmapContainer(WorkingBeatmap working)
public EditorBeatmapContainer(IWorkingBeatmap working)
{
this.working = working;

View File

@ -0,0 +1,171 @@
// 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 Humanizer;
using NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osu.Game.Screens.Edit.Timing;
using osu.Game.Tests.Beatmaps;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editing
{
public class TestSceneHitObjectDifficultyPointAdjustments : EditorTestScene
{
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("add test objects", () =>
{
EditorBeatmap.Add(new Slider
{
StartTime = 0,
Position = (OsuPlayfield.BASE_SIZE - new Vector2(0, 100)) / 2,
Path = new SliderPath
{
ControlPoints =
{
new PathControlPoint(new Vector2(0, 0)),
new PathControlPoint(new Vector2(0, 100))
}
}
});
EditorBeatmap.Add(new Slider
{
StartTime = 500,
Position = (OsuPlayfield.BASE_SIZE - new Vector2(100, 0)) / 2,
Path = new SliderPath
{
ControlPoints =
{
new PathControlPoint(new Vector2(0, 0)),
new PathControlPoint(new Vector2(100, 0))
}
},
DifficultyControlPoint = new DifficultyControlPoint
{
SliderVelocity = 2
}
});
});
}
[Test]
public void TestSingleSelection()
{
clickDifficultyPiece(0);
velocityPopoverHasSingleValue(1);
dismissPopover();
// select first object to ensure that difficulty pieces for unselected objects
// work independently from selection state.
AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.First()));
clickDifficultyPiece(1);
velocityPopoverHasSingleValue(2);
setVelocityViaPopover(5);
hitObjectHasVelocity(1, 5);
}
[Test]
public void TestMultipleSelectionWithSameSliderVelocity()
{
AddStep("unify slider velocity", () =>
{
foreach (var h in EditorBeatmap.HitObjects)
h.DifficultyControlPoint.SliderVelocity = 1.5;
});
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
clickDifficultyPiece(0);
velocityPopoverHasSingleValue(1.5);
dismissPopover();
clickDifficultyPiece(1);
velocityPopoverHasSingleValue(1.5);
setVelocityViaPopover(5);
hitObjectHasVelocity(0, 5);
hitObjectHasVelocity(1, 5);
}
[Test]
public void TestMultipleSelectionWithDifferentSliderVelocity()
{
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
clickDifficultyPiece(0);
velocityPopoverHasIndeterminateValue();
dismissPopover();
clickDifficultyPiece(1);
velocityPopoverHasIndeterminateValue();
setVelocityViaPopover(3);
hitObjectHasVelocity(0, 3);
hitObjectHasVelocity(1, 3);
}
private void clickDifficultyPiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} difficulty piece", () =>
{
var difficultyPiece = this.ChildrenOfType<DifficultyPointPiece>().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex));
InputManager.MoveMouseTo(difficultyPiece);
InputManager.Click(MouseButton.Left);
});
private void velocityPopoverHasSingleValue(double velocity) => AddUntilStep($"velocity popover has {velocity}", () =>
{
var popover = this.ChildrenOfType<DifficultyPointPiece.DifficultyEditPopover>().SingleOrDefault();
var slider = popover?.ChildrenOfType<IndeterminateSliderWithTextBoxInput<double>>().Single();
return slider?.Current.Value == velocity;
});
private void velocityPopoverHasIndeterminateValue() => AddUntilStep("velocity popover has indeterminate value", () =>
{
var popover = this.ChildrenOfType<DifficultyPointPiece.DifficultyEditPopover>().SingleOrDefault();
var slider = popover?.ChildrenOfType<IndeterminateSliderWithTextBoxInput<double>>().Single();
return slider != null && slider.Current.Value == null;
});
private void dismissPopover()
{
AddStep("dismiss popover", () => InputManager.Key(Key.Escape));
AddUntilStep("wait for dismiss", () => !this.ChildrenOfType<DifficultyPointPiece.DifficultyEditPopover>().Any(popover => popover.IsPresent));
}
private void setVelocityViaPopover(double velocity) => AddStep($"set {velocity} via popover", () =>
{
var popover = this.ChildrenOfType<DifficultyPointPiece.DifficultyEditPopover>().Single();
var slider = popover.ChildrenOfType<IndeterminateSliderWithTextBoxInput<double>>().Single();
slider.Current.Value = velocity;
});
private void hitObjectHasVelocity(int objectIndex, double velocity) => AddAssert($"{objectIndex.ToOrdinalWords()} has velocity {velocity}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);
return h.DifficultyControlPoint.SliderVelocity == velocity;
});
}
}

View File

@ -0,0 +1,252 @@
// 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 Humanizer;
using NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osu.Game.Screens.Edit.Timing;
using osu.Game.Tests.Beatmaps;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editing
{
public class TestSceneHitObjectSamplePointAdjustments : EditorTestScene
{
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("add test objects", () =>
{
EditorBeatmap.Add(new HitCircle
{
StartTime = 0,
Position = (OsuPlayfield.BASE_SIZE - new Vector2(100, 0)) / 2,
SampleControlPoint = new SampleControlPoint
{
SampleBank = "normal",
SampleVolume = 80
}
});
EditorBeatmap.Add(new HitCircle
{
StartTime = 500,
Position = (OsuPlayfield.BASE_SIZE + new Vector2(100, 0)) / 2,
SampleControlPoint = new SampleControlPoint
{
SampleBank = "soft",
SampleVolume = 60
}
});
});
}
[Test]
public void TestSingleSelection()
{
clickSamplePiece(0);
samplePopoverHasSingleBank("normal");
samplePopoverHasSingleVolume(80);
dismissPopover();
// select first object to ensure that sample pieces for unselected objects
// work independently from selection state.
AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.First()));
clickSamplePiece(1);
samplePopoverHasSingleBank("soft");
samplePopoverHasSingleVolume(60);
setVolumeViaPopover(90);
hitObjectHasSampleVolume(1, 90);
setBankViaPopover("drum");
hitObjectHasSampleBank(1, "drum");
}
[Test]
public void TestMultipleSelectionWithSameSampleVolume()
{
AddStep("unify sample volume", () =>
{
foreach (var h in EditorBeatmap.HitObjects)
h.SampleControlPoint.SampleVolume = 50;
});
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
clickSamplePiece(0);
samplePopoverHasSingleVolume(50);
dismissPopover();
clickSamplePiece(1);
samplePopoverHasSingleVolume(50);
setVolumeViaPopover(75);
hitObjectHasSampleVolume(0, 75);
hitObjectHasSampleVolume(1, 75);
}
[Test]
public void TestMultipleSelectionWithDifferentSampleVolume()
{
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
clickSamplePiece(0);
samplePopoverHasIndeterminateVolume();
dismissPopover();
clickSamplePiece(1);
samplePopoverHasIndeterminateVolume();
setVolumeViaPopover(30);
hitObjectHasSampleVolume(0, 30);
hitObjectHasSampleVolume(1, 30);
}
[Test]
public void TestMultipleSelectionWithSameSampleBank()
{
AddStep("unify sample bank", () =>
{
foreach (var h in EditorBeatmap.HitObjects)
h.SampleControlPoint.SampleBank = "soft";
});
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
clickSamplePiece(0);
samplePopoverHasSingleBank("soft");
dismissPopover();
clickSamplePiece(1);
samplePopoverHasSingleBank("soft");
setBankViaPopover(string.Empty);
hitObjectHasSampleBank(0, "soft");
hitObjectHasSampleBank(1, "soft");
samplePopoverHasSingleBank("soft");
setBankViaPopover("drum");
hitObjectHasSampleBank(0, "drum");
hitObjectHasSampleBank(1, "drum");
samplePopoverHasSingleBank("drum");
}
[Test]
public void TestMultipleSelectionWithDifferentSampleBank()
{
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
clickSamplePiece(0);
samplePopoverHasIndeterminateBank();
dismissPopover();
clickSamplePiece(1);
samplePopoverHasIndeterminateBank();
setBankViaPopover(string.Empty);
hitObjectHasSampleBank(0, "normal");
hitObjectHasSampleBank(1, "soft");
samplePopoverHasIndeterminateBank();
setBankViaPopover("normal");
hitObjectHasSampleBank(0, "normal");
hitObjectHasSampleBank(1, "normal");
samplePopoverHasSingleBank("normal");
}
private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} difficulty piece", () =>
{
var difficultyPiece = this.ChildrenOfType<SamplePointPiece>().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex));
InputManager.MoveMouseTo(difficultyPiece);
InputManager.Click(MouseButton.Left);
});
private void samplePopoverHasSingleVolume(int volume) => AddUntilStep($"sample popover has volume {volume}", () =>
{
var popover = this.ChildrenOfType<SamplePointPiece.SampleEditPopover>().SingleOrDefault();
var slider = popover?.ChildrenOfType<IndeterminateSliderWithTextBoxInput<int>>().Single();
return slider?.Current.Value == volume;
});
private void samplePopoverHasIndeterminateVolume() => AddUntilStep("sample popover has indeterminate volume", () =>
{
var popover = this.ChildrenOfType<SamplePointPiece.SampleEditPopover>().SingleOrDefault();
var slider = popover?.ChildrenOfType<IndeterminateSliderWithTextBoxInput<int>>().Single();
return slider != null && slider.Current.Value == null;
});
private void samplePopoverHasSingleBank(string bank) => AddUntilStep($"sample popover has bank {bank}", () =>
{
var popover = this.ChildrenOfType<SamplePointPiece.SampleEditPopover>().SingleOrDefault();
var textBox = popover?.ChildrenOfType<OsuTextBox>().First();
return textBox?.Current.Value == bank && string.IsNullOrEmpty(textBox?.PlaceholderText.ToString());
});
private void samplePopoverHasIndeterminateBank() => AddUntilStep("sample popover has indeterminate bank", () =>
{
var popover = this.ChildrenOfType<SamplePointPiece.SampleEditPopover>().SingleOrDefault();
var textBox = popover?.ChildrenOfType<OsuTextBox>().First();
return textBox != null && string.IsNullOrEmpty(textBox.Current.Value) && !string.IsNullOrEmpty(textBox.PlaceholderText.ToString());
});
private void dismissPopover()
{
AddStep("dismiss popover", () => InputManager.Key(Key.Escape));
AddUntilStep("wait for dismiss", () => !this.ChildrenOfType<DifficultyPointPiece.DifficultyEditPopover>().Any(popover => popover.IsPresent));
}
private void setVolumeViaPopover(int volume) => AddStep($"set volume {volume} via popover", () =>
{
var popover = this.ChildrenOfType<SamplePointPiece.SampleEditPopover>().Single();
var slider = popover.ChildrenOfType<IndeterminateSliderWithTextBoxInput<int>>().Single();
slider.Current.Value = volume;
});
private void hitObjectHasSampleVolume(int objectIndex, int volume) => AddAssert($"{objectIndex.ToOrdinalWords()} has volume {volume}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);
return h.SampleControlPoint.SampleVolume == volume;
});
private void setBankViaPopover(string bank) => AddStep($"set bank {bank} via popover", () =>
{
var popover = this.ChildrenOfType<SamplePointPiece.SampleEditPopover>().Single();
var textBox = popover.ChildrenOfType<LabelledTextBox>().First();
textBox.Current.Value = bank;
// force a commit via keyboard.
// this is needed when testing attempting to set empty bank - which should revert to the previous value, but only on commit.
InputManager.ChangeFocus(textBox);
InputManager.Key(Key.Enter);
});
private void hitObjectHasSampleBank(int objectIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} has bank {bank}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);
return h.SampleControlPoint.SampleBank == bank;
});
}
}

View File

@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Editing
[TestFixture]
public class TestSceneWaveform : OsuTestScene
{
private WorkingBeatmap waveformBeatmap;
private IWorkingBeatmap waveformBeatmap;
[BackgroundDependencyLoader]
private void load(AudioManager audio)

View File

@ -293,7 +293,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap, null);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException();
public override string Description { get; } = string.Empty;

View File

@ -31,12 +31,18 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddUntilStep("wait for fail", () => Player.HasFailed);
AddUntilStep("wait for fail overlay", () => ((FailPlayer)Player).FailOverlay.State.Value == Visibility.Visible);
// The pause screen and fail animation both ramp frequency.
// This tests to ensure that it doesn't reset during that handoff.
AddAssert("frequency only ever decreased", () => !((FailPlayer)Player).FrequencyIncreased);
}
private class FailPlayer : TestPlayer
{
public new FailOverlay FailOverlay => base.FailOverlay;
public bool FrequencyIncreased { get; private set; }
public FailPlayer()
: base(false, false)
{
@ -47,6 +53,19 @@ namespace osu.Game.Tests.Visual.Gameplay
base.LoadComplete();
HealthProcessor.FailConditions += (_, __) => true;
}
private double lastFrequency = double.MaxValue;
protected override void Update()
{
base.Update();
double freq = Beatmap.Value.Track.AggregateFrequency.Value;
FrequencyIncreased |= freq > lastFrequency;
lastFrequency = freq;
}
}
}
}

View File

@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) =>
throw new System.NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) =>
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) =>
throw new System.NotImplementedException();
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
@ -83,6 +83,9 @@ namespace osu.Game.Tests.Visual.Gameplay
public bool OnPressed(KeyBindingPressEvent<TestAction> e)
{
if (e.Repeat)
return false;
ReceivedAction = e.Action == TestAction.Down;
return true;
}

View File

@ -95,8 +95,9 @@ namespace osu.Game.Tests.Visual.Gameplay
private void prepareBeatmap()
{
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
Beatmap.Value.BeatmapInfo.EpilepsyWarning = epilepsyWarning;
var workingBeatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
workingBeatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning;
Beatmap.Value = workingBeatmap;
foreach (var mod in SelectedMods.Value.OfType<IApplicableToTrack>())
mod.ApplyToTrack(Beatmap.Value.Track);

View File

@ -12,7 +12,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public class TestScenePlayerReferenceLeaking : TestSceneAllRulesetPlayers
{
private readonly WeakList<WorkingBeatmap> workingWeakReferences = new WeakList<WorkingBeatmap>();
private readonly WeakList<IWorkingBeatmap> workingWeakReferences = new WeakList<IWorkingBeatmap>();
private readonly WeakList<Player> playerWeakReferences = new WeakList<Player>();

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