1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-18 06:21:22 +08:00

Compare commits

..

272 Commits

277 changed files with 3449 additions and 1874 deletions
+4 -1
View File
@@ -114,7 +114,10 @@ jobs:
dotnet-version: "8.0.x"
- name: Install .NET workloads
run: dotnet workload install android
# since windows image 20241113.3.0, not specifying a version here
# installs the .NET 7 version of android workload for very unknown reasons.
# revisit once we upgrade to .NET 9, it's probably fixed there.
run: dotnet workload install android --version (dotnet --version)
- name: Compile
run: dotnet build -c Debug osu.Android.slnf
-57
View File
@@ -1,57 +0,0 @@
# .NET Code Style
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
# IDE0001: Simplify names
dotnet_diagnostic.IDE0001.severity = warning
# IDE0002: Simplify member access
dotnet_diagnostic.IDE0002.severity = warning
# IDE0003: Remove qualification
dotnet_diagnostic.IDE0003.severity = warning
# IDE0004: Remove unnecessary cast
dotnet_diagnostic.IDE0004.severity = warning
# IDE0005: Remove unnecessary imports
dotnet_diagnostic.IDE0005.severity = warning
# IDE0034: Simplify default literal
dotnet_diagnostic.IDE0034.severity = warning
# IDE0036: Sort modifiers
dotnet_diagnostic.IDE0036.severity = warning
# IDE0040: Add accessibility modifier
dotnet_diagnostic.IDE0040.severity = warning
# IDE0049: Use keyword for type name
dotnet_diagnostic.IDE0040.severity = warning
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = warning
# IDE0051: Private method is unused
dotnet_diagnostic.IDE0051.severity = silent
# IDE0052: Private member is unused
dotnet_diagnostic.IDE0052.severity = silent
# IDE0073: File header
dotnet_diagnostic.IDE0073.severity = warning
# IDE0130: Namespace mismatch with folder
dotnet_diagnostic.IDE0130.severity = warning
# IDE1006: Naming style
dotnet_diagnostic.IDE1006.severity = warning
#Disable operator overloads requiring alternate named methods
dotnet_diagnostic.CA2225.severity = none
# Banned APIs
dotnet_diagnostic.RS0030.severity = error
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
# See: https://github.com/ppy/osu/pull/19677
dotnet_diagnostic.OSUF001.severity = none
-4
View File
@@ -14,10 +14,6 @@ M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Gen
M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks.
P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks.
M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever.
M:System.Char.ToLower(System.Char);char.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
M:System.Char.ToUpper(System.Char);char.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToPascalCase() instead.
M:Humanizer.InflectorExtensions.Camelize(System.String);Humanizer's .Camelize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToCamelCase() instead.
M:Humanizer.InflectorExtensions.Underscore(System.String);Humanizer's .Underscore() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToSnakeCase() instead.
+109
View File
@@ -0,0 +1,109 @@
# .NET Code Style
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
is_global = true
# IDE0001: Simplify names
dotnet_diagnostic.IDE0001.severity = warning
# IDE0002: Simplify member access
dotnet_diagnostic.IDE0002.severity = warning
# IDE0003: Remove qualification
dotnet_diagnostic.IDE0003.severity = warning
# IDE0004: Remove unnecessary cast
dotnet_diagnostic.IDE0004.severity = warning
# IDE0005: Remove unnecessary imports
dotnet_diagnostic.IDE0005.severity = warning
# IDE0034: Simplify default literal
dotnet_diagnostic.IDE0034.severity = warning
# IDE0036: Sort modifiers
dotnet_diagnostic.IDE0036.severity = warning
# IDE0040: Add accessibility modifier
dotnet_diagnostic.IDE0040.severity = warning
# IDE0049: Use keyword for type name
dotnet_diagnostic.IDE0040.severity = warning
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = warning
# IDE0051: Private method is unused
dotnet_diagnostic.IDE0051.severity = silent
# IDE0052: Private member is unused
dotnet_diagnostic.IDE0052.severity = silent
# IDE0073: File header
dotnet_diagnostic.IDE0073.severity = warning
# IDE0130: Namespace mismatch with folder
dotnet_diagnostic.IDE0130.severity = warning
# IDE1006: Naming style
dotnet_diagnostic.IDE1006.severity = warning
# CA1305: Specify IFormatProvider
# Too many noisy warnings for parsing/formatting numbers
dotnet_diagnostic.CA1305.severity = none
# CA1507: Use nameof to express symbol names
# Flaggs serialization name attributes
dotnet_diagnostic.CA1507.severity = suggestion
# CA1806: Do not ignore method results
# The usages for numeric parsing are explicitly optional
dotnet_diagnostic.CA1806.severity = suggestion
# CA1822: Mark members as static
# Potential false positive around reflection/too much noise
dotnet_diagnostic.CA1822.severity = none
# CA1826: Do not use Enumerable method on indexable collections
dotnet_diagnostic.CA1826.severity = suggestion
# CA1859: Use concrete types when possible for improved performance
# Involves design considerations
dotnet_diagnostic.CA1859.severity = suggestion
# CA1860: Avoid using 'Enumerable.Any()' extension method
dotnet_diagnostic.CA1860.severity = suggestion
# CA1861: Avoid constant arrays as arguments
# Outdated with collection expressions
dotnet_diagnostic.CA1861.severity = suggestion
# CA2007: Consider calling ConfigureAwait on the awaited task
dotnet_diagnostic.CA2007.severity = warning
# CA2016: Forward the 'CancellationToken' parameter to methods
# Some overloads are having special handling for debugger
dotnet_diagnostic.CA2016.severity = suggestion
# CA2021: Do not call Enumerable.Cast<T> or Enumerable.OfType<T> with incompatible types
# Causing a lot of false positives with generics
dotnet_diagnostic.CA2021.severity = none
# CA2101: Specify marshaling for P/Invoke string arguments
# Reports warning for all non-UTF16 usages on DllImport; consider migrating to LibraryImport
dotnet_diagnostic.CA2101.severity = none
# CA2201: Do not raise reserved exception types
dotnet_diagnostic.CA2201.severity = warning
# CA2208: Instantiate argument exceptions correctly
dotnet_diagnostic.CA2208.severity = suggestion
# CA2242: Test for NaN correctly
dotnet_diagnostic.CA2242.severity = warning
# Banned APIs
dotnet_diagnostic.RS0030.severity = error
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
# See: https://github.com/ppy/osu/pull/19677
dotnet_diagnostic.OSUF001.severity = none
-58
View File
@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="osu! Rule Set" Description=" " ToolsVersion="16.0">
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA1016" Action="None" />
<Rule Id="CA1028" Action="None" />
<Rule Id="CA1031" Action="None" />
<Rule Id="CA1034" Action="None" />
<Rule Id="CA1036" Action="None" />
<Rule Id="CA1040" Action="None" />
<Rule Id="CA1044" Action="None" />
<Rule Id="CA1051" Action="None" />
<Rule Id="CA1054" Action="None" />
<Rule Id="CA1056" Action="None" />
<Rule Id="CA1062" Action="None" />
<Rule Id="CA1063" Action="None" />
<Rule Id="CA1067" Action="None" />
<Rule Id="CA1707" Action="None" />
<Rule Id="CA1710" Action="None" />
<Rule Id="CA1714" Action="None" />
<Rule Id="CA1716" Action="None" />
<Rule Id="CA1717" Action="None" />
<Rule Id="CA1720" Action="None" />
<Rule Id="CA1721" Action="None" />
<Rule Id="CA1724" Action="None" />
<Rule Id="CA1801" Action="None" />
<Rule Id="CA1806" Action="None" />
<Rule Id="CA1812" Action="None" />
<Rule Id="CA1814" Action="None" />
<Rule Id="CA1815" Action="None" />
<Rule Id="CA1819" Action="None" />
<Rule Id="CA1822" Action="None" />
<Rule Id="CA1823" Action="None" />
<Rule Id="CA2007" Action="Warning" />
<Rule Id="CA2214" Action="None" />
<Rule Id="CA2227" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeQuality.CSharp.Analyzers" RuleNamespace="Microsoft.CodeQuality.CSharp.Analyzers">
<Rule Id="CA1001" Action="None" />
<Rule Id="CA1032" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
<Rule Id="CA1303" Action="None" />
<Rule Id="CA1304" Action="None" />
<Rule Id="CA1305" Action="None" />
<Rule Id="CA1307" Action="None" />
<Rule Id="CA1308" Action="None" />
<Rule Id="CA1816" Action="None" />
<Rule Id="CA1826" Action="None" />
<Rule Id="CA2000" Action="None" />
<Rule Id="CA2008" Action="None" />
<Rule Id="CA2213" Action="None" />
<Rule Id="CA2235" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.CSharp.Analyzers" RuleNamespace="Microsoft.NetCore.CSharp.Analyzers">
<Rule Id="CA1309" Action="Warning" />
<Rule Id="CA2201" Action="Warning" />
</Rules>
</RuleSet>
+13 -1
View File
@@ -18,9 +18,21 @@
<ItemGroup Label="Code Analysis">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" PrivateAssets="All" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
<!-- Rider compatibility: .globalconfig needs to be explicitly referenced instead of using the global file name. -->
<GlobalAnalyzerConfigFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\osu.globalconfig" />
</ItemGroup>
<PropertyGroup Label="Code Analysis">
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
<AnalysisMode>Default</AnalysisMode>
<AnalysisModeDesign>Default</AnalysisModeDesign>
<AnalysisModeDocumentation>Recommended</AnalysisModeDocumentation>
<AnalysisModeGlobalization>Recommended</AnalysisModeGlobalization>
<AnalysisModeInteroperability>Recommended</AnalysisModeInteroperability>
<AnalysisModeMaintainability>Recommended</AnalysisModeMaintainability>
<AnalysisModeNaming>Default</AnalysisModeNaming>
<AnalysisModePerformance>Minimum</AnalysisModePerformance>
<AnalysisModeReliability>Recommended</AnalysisModeReliability>
<AnalysisModeSecurity>Default</AnalysisModeSecurity>
<AnalysisModeUsage>Default</AnalysisModeUsage>
</PropertyGroup>
<PropertyGroup Label="Documentation">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
+1 -1
View File
@@ -10,7 +10,7 @@
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1118.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1206.0" />
</ItemGroup>
<PropertyGroup>
<!-- Fody does not handle Android build well, and warns when unchanged.
+14 -19
View File
@@ -15,6 +15,7 @@ using osu.Framework.Threading;
using osu.Game;
using osu.Game.Configuration;
using osu.Game.Extensions;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
@@ -47,6 +48,9 @@ namespace osu.Desktop
[Resolved]
private MultiplayerClient multiplayerClient { get; set; } = null!;
[Resolved]
private LocalUserStatisticsProvider statisticsProvider { get; set; } = null!;
[Resolved]
private OsuConfigManager config { get; set; } = null!;
@@ -117,7 +121,9 @@ namespace osu.Desktop
status.BindValueChanged(_ => schedulePresenceUpdate());
activity.BindValueChanged(_ => schedulePresenceUpdate());
privacyMode.BindValueChanged(_ => schedulePresenceUpdate());
multiplayerClient.RoomUpdated += onRoomUpdated;
statisticsProvider.StatisticsUpdated += onStatisticsUpdated;
}
private void onReady(object _, ReadyMessage __)
@@ -133,6 +139,8 @@ namespace osu.Desktop
private void onRoomUpdated() => schedulePresenceUpdate();
private void onStatisticsUpdated(UserStatisticsUpdate _) => schedulePresenceUpdate();
private ScheduledDelegate? presenceUpdateDelegate;
private void schedulePresenceUpdate()
@@ -167,7 +175,7 @@ namespace osu.Desktop
presence.State = clampLength(activity.Value.GetStatus(hideIdentifiableInformation));
presence.Details = clampLength(activity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty);
if (getBeatmapID(activity.Value) is int beatmapId && beatmapId > 0)
if (activity.Value.GetBeatmapID(hideIdentifiableInformation) is int beatmapId && beatmapId > 0)
{
presence.Buttons = new[]
{
@@ -229,10 +237,8 @@ namespace osu.Desktop
presence.Assets.LargeImageText = string.Empty;
else
{
if (user.Value.RulesetsStatistics != null && user.Value.RulesetsStatistics.TryGetValue(ruleset.Value.ShortName, out UserStatistics? statistics))
presence.Assets.LargeImageText = $"{user.Value.Username}" + (statistics.GlobalRank > 0 ? $" (rank #{statistics.GlobalRank:N0})" : string.Empty);
else
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.GlobalRank > 0 ? $" (rank #{user.Value.Statistics.GlobalRank:N0})" : string.Empty);
var statistics = statisticsProvider.GetStatisticsFor(ruleset.Value);
presence.Assets.LargeImageText = $"{user.Value.Username}" + (statistics?.GlobalRank > 0 ? $" (rank #{statistics.GlobalRank:N0})" : string.Empty);
}
// small image
@@ -327,25 +333,14 @@ namespace osu.Desktop
return true;
}
private static int? getBeatmapID(UserActivity activity)
{
switch (activity)
{
case UserActivity.InGame game:
return game.BeatmapID;
case UserActivity.EditingBeatmap edit:
return edit.BeatmapID;
}
return null;
}
protected override void Dispose(bool isDisposing)
{
if (multiplayerClient.IsNotNull())
multiplayerClient.RoomUpdated -= onRoomUpdated;
if (statisticsProvider.IsNotNull())
statisticsProvider.StatisticsUpdated -= onStatisticsUpdated;
client.Dispose();
base.Dispose(isDisposing);
}
+1 -1
View File
@@ -99,7 +99,7 @@ namespace osu.Desktop
var hostOptions = new HostOptions
{
IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null,
IPCPipeName = !tournamentClient ? OsuGame.IPC_PIPE_NAME : null,
FriendlyGameName = OsuGameBase.GAME_NAME,
};
@@ -148,15 +148,7 @@ namespace osu.Desktop.Windows
foreach (var association in uri_associations)
association.UpdateDescription(getLocalisedString(association.Description));
string getLocalisedString(LocalisableString s)
{
if (localisation == null)
return s.ToString();
var b = localisation.GetLocalisedBindableString(s);
b.UnbindAll();
return b.Value;
}
string getLocalisedString(LocalisableString s) => localisation?.GetLocalisedString(s) ?? s.ToString();
}
#region Native interop
+32 -6
View File
@@ -4,28 +4,54 @@
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Benchmarks
{
public class BenchmarkUnstableRate : BenchmarkTest
{
private List<HitEvent> events = null!;
private readonly List<List<HitEvent>> incrementalEventLists = new List<List<HitEvent>>();
public override void SetUp()
{
base.SetUp();
events = new List<HitEvent>();
for (int i = 0; i < 1000; i++)
events.Add(new HitEvent(RNG.NextDouble(-200.0, 200.0), RNG.NextDouble(1.0, 2.0), HitResult.Great, new HitObject(), null, null));
var events = new List<HitEvent>();
for (int i = 0; i < 2048; i++)
{
// Ensure the object has hit windows populated.
var hitObject = new HitCircle();
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
events.Add(new HitEvent(RNG.NextDouble(-200.0, 200.0), RNG.NextDouble(1.0, 2.0), HitResult.Great, hitObject, null, null));
incrementalEventLists.Add(new List<HitEvent>(events));
}
}
[Benchmark]
public void CalculateUnstableRate()
{
_ = events.CalculateUnstableRate();
for (int i = 0; i < 2048; i++)
{
var events = incrementalEventLists[i];
_ = events.CalculateUnstableRate();
}
}
[Benchmark]
public void CalculateUnstableRateUsingIncrementalCalculation()
{
HitEventExtensions.UnstableRateCalculationResult? last = null;
for (int i = 0; i < 2048; i++)
{
var events = incrementalEventLists[i];
last = events.CalculateUnstableRate(last);
}
}
}
}
@@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Catch.Edit
{
public partial class CatchDistanceSnapProvider : ComposerDistanceSnapProvider
{
protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after)
public override double ReadCurrentDistanceSnap(HitObject before, HitObject after)
{
// osu!catch's distance snap implementation is limited, in that a custom spacing cannot be specified.
// Therefore this functionality is not currently used.
@@ -70,6 +70,8 @@ namespace osu.Game.Rulesets.Catch.Edit
}));
}
protected override Drawable CreateHitObjectInspector() => new CatchHitObjectInspector(DistanceSnapProvider);
protected override IEnumerable<TernaryButton> CreateTernaryButtons()
=> base.CreateTernaryButtons()
.Concat(DistanceSnapProvider.CreateTernaryButtons());
@@ -0,0 +1,41 @@
// 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 osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Screens.Edit.Compose.Components;
namespace osu.Game.Rulesets.Catch.Edit
{
public partial class CatchHitObjectInspector(CatchDistanceSnapProvider snapProvider) : HitObjectInspector
{
protected override void AddInspectorValues(HitObject[] objects)
{
base.AddInspectorValues(objects);
if (objects.Length > 0)
{
HitObject firstSelectedHitObject = objects.MinBy(ho => ho.StartTime)!;
HitObject lastSelectedHitObject = objects.MaxBy(ho => ho.GetEndTime())!;
HitObject? precedingObject = EditorBeatmap.HitObjects.LastOrDefault(ho => ho.GetEndTime() < firstSelectedHitObject.StartTime);
HitObject? nextObject = EditorBeatmap.HitObjects.FirstOrDefault(ho => ho.StartTime > lastSelectedHitObject.GetEndTime());
if (precedingObject != null && precedingObject is not BananaShower)
{
double previousSnap = snapProvider.ReadCurrentDistanceSnap(precedingObject, firstSelectedHitObject);
AddHeader("To previous");
AddValue($"{previousSnap:#,0.##}x");
}
if (nextObject != null && nextObject is not BananaShower)
{
double nextSnap = snapProvider.ReadCurrentDistanceSnap(lastSelectedHitObject, nextObject);
AddHeader("To next");
AddValue($"{nextSnap:#,0.##}x");
}
}
}
}
}
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
{
base.InitialiseDefaults();
SetDefault(ManiaRulesetSetting.ScrollSpeed, 8, 1, 40);
SetDefault(ManiaRulesetSetting.ScrollSpeed, 8.0, 1.0, 40.0, 0.1);
SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
SetDefault(ManiaRulesetSetting.TimingBasedNoteColouring, false);
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
if (Get<double?>(ManiaRulesetSetting.ScrollTime) is double scrollTime)
{
SetValue(ManiaRulesetSetting.ScrollSpeed, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / scrollTime));
SetValue(ManiaRulesetSetting.ScrollSpeed, Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / scrollTime));
SetValue<double?>(ManiaRulesetSetting.ScrollTime, null);
}
#pragma warning restore CS0618
@@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
{
new TrackedSetting<int>(ManiaRulesetSetting.ScrollSpeed,
new TrackedSetting<double>(ManiaRulesetSetting.ScrollSpeed,
speed => new SettingDescription(
rawValue: speed,
name: RulesetSettingsStrings.ScrollSpeed,
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mania.Edit
protected override void Update()
{
TargetTimeRange = TimelineTimeRange == null || ShowSpeedChanges.Value ? ComputeScrollTime(Config.Get<int>(ManiaRulesetSetting.ScrollSpeed)) : TimelineTimeRange.Value;
TargetTimeRange = TimelineTimeRange == null || ShowSpeedChanges.Value ? ComputeScrollTime(Config.Get<double>(ManiaRulesetSetting.ScrollSpeed)) : TimelineTimeRange.Value;
base.Update();
}
}
@@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
{
Caption = "Use special (N+1) style",
HintText = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 6K (5+1) or 8K (7+1) configurations.",
Current = { Value = Beatmap.BeatmapInfo.SpecialStyle }
Current = { Value = Beatmap.SpecialStyle }
},
healthDrainSlider = new FormSliderBar<float>
{
@@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
// for now, update these on commit rather than making BeatmapMetadata bindables.
// after switching database engines we can reconsider if switching to bindables is a good direction.
Beatmap.Difficulty.CircleSize = keyCountSlider.Current.Value;
Beatmap.BeatmapInfo.SpecialStyle = specialStyle.Current.Value;
Beatmap.SpecialStyle = specialStyle.Current.Value;
Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value;
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
@@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Mania
LabelText = RulesetSettingsStrings.ScrollingDirection,
Current = config.GetBindable<ManiaScrollingDirection>(ManiaRulesetSetting.ScrollDirection)
},
new SettingsSlider<int, ManiaScrollSlider>
new SettingsSlider<double, ManiaScrollSlider>
{
LabelText = RulesetSettingsStrings.ScrollSpeed,
Current = config.GetBindable<int>(ManiaRulesetSetting.ScrollSpeed),
KeyboardStep = 5
Current = config.GetBindable<double>(ManiaRulesetSetting.ScrollSpeed),
KeyboardStep = 1
},
new SettingsCheckbox
{
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania
};
}
private partial class ManiaScrollSlider : RoundedSliderBar<int>
private partial class ManiaScrollSlider : RoundedSliderBar<double>
{
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip((int)DrawableManiaRuleset.ComputeScrollTime(Current.Value), Current.Value);
}
@@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 1: return colour_cyan;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 3:
@@ -176,7 +176,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 2: return colour_cyan;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 4:
@@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 3: return colour_purple;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 5:
@@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 4: return colour_cyan;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 6:
@@ -224,7 +224,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 5: return colour_pink;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 7:
@@ -244,7 +244,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 6: return colour_pink;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 8:
@@ -266,7 +266,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 7: return colour_purple;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 9:
@@ -290,7 +290,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 8: return colour_purple;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 10:
@@ -316,7 +316,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 9: return colour_purple;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
}
@@ -339,7 +339,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 5: return colour_green;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
}
}
@@ -164,10 +164,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
private Drawable getResult(HitResult result)
{
if (!hit_result_mapping.ContainsKey(result))
if (!hit_result_mapping.TryGetValue(result, out var value))
return null;
string filename = this.GetManiaSkinConfig<string>(hit_result_mapping[result])?.Value
string filename = this.GetManiaSkinConfig<string>(value)?.Value
?? default_hit_result_skin_filenames[result];
var animation = this.GetAnimation(filename, true, true, frameLength: 1000 / 20d);
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
private readonly BindableInt configScrollSpeed = new BindableInt();
private readonly BindableDouble configScrollSpeed = new BindableDouble();
private double currentTimeRange;
protected double TargetTimeRange;
@@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Mania.UI
/// </summary>
/// <param name="scrollSpeed">The scroll speed.</param>
/// <returns>The scroll time.</returns>
public static double ComputeScrollTime(int scrollSpeed) => MAX_TIME_RANGE / scrollSpeed;
public static double ComputeScrollTime(double scrollSpeed) => MAX_TIME_RANGE / scrollSpeed;
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer();
@@ -231,6 +231,36 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddAssert("slider still has 2 anchors", () => secondSlider.Path.ControlPoints.Count, () => Is.EqualTo(2));
}
[Test]
public void TestControlClickDoesNotDiscardExistingSelectionEvenIfNothingHit()
{
var firstSlider = new Slider
{
StartTime = 0,
Position = new Vector2(0, 0),
Path = new SliderPath
{
ControlPoints =
{
new PathControlPoint(),
new PathControlPoint(new Vector2(100))
}
}
};
AddStep("add object", () => EditorBeatmap.AddRange([firstSlider]));
AddStep("select first slider", () => EditorBeatmap.SelectedHitObjects.AddRange([firstSlider]));
AddStep("move mouse to middle of playfield", () => InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre));
AddStep("control-click left mouse", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Click(MouseButton.Left);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddAssert("selection preserved", () => EditorBeatmap.SelectedHitObjects.Count, () => Is.EqualTo(1));
}
private ComposeBlueprintContainer blueprintContainer
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();
@@ -191,7 +191,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
private void gridSizeIs(int size)
=> AddAssert($"grid size is {size}", () => this.ChildrenOfType<RectangularPositionSnapGrid>().Single().Spacing.Value == new Vector2(size)
&& EditorBeatmap.BeatmapInfo.GridSize == size);
&& EditorBeatmap.GridSize == size);
[Test]
public void TestGridTypeToggling()
@@ -83,10 +83,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
})
}
},
BeatmapInfo =
{
StackLeniency = 0,
}
StackLeniency = 0,
},
ReplayFrames = new List<ReplayFrame>
{
@@ -74,12 +74,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
{
BeatmapInfo = new BeatmapInfo
{
StackLeniency = 0,
Difficulty = new BeatmapDifficulty
{
ApproachRate = 8.5f
}
},
StackLeniency = 0,
ControlPointInfo = controlPointInfo
};
@@ -465,7 +465,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private void performTest(List<ReplayFrame> frames, Beatmap<OsuHitObject> beatmap)
{
beatmap.BeatmapInfo.Ruleset = new OsuRuleset().RulesetInfo;
beatmap.BeatmapInfo.StackLeniency = 0;
beatmap.StackLeniency = 0;
beatmap.BeatmapInfo.Difficulty = new BeatmapDifficulty
{
SliderMultiplier = 4,
@@ -56,13 +56,13 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
h.StackHeight = 0;
if (beatmap.BeatmapInfo.BeatmapVersion >= 6)
applyStacking(beatmap.BeatmapInfo, hitObjects, 0, hitObjects.Count - 1);
applyStacking(beatmap, hitObjects, 0, hitObjects.Count - 1);
else
applyStackingOld(beatmap.BeatmapInfo, hitObjects);
applyStackingOld(beatmap, hitObjects);
}
}
private static void applyStacking(BeatmapInfo beatmapInfo, List<OsuHitObject> hitObjects, int startIndex, int endIndex)
private static void applyStacking(IBeatmap beatmap, List<OsuHitObject> hitObjects, int startIndex, int endIndex)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex, endIndex);
ArgumentOutOfRangeException.ThrowIfNegative(startIndex);
@@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
continue;
double endTime = stackBaseObject.GetEndTime();
double stackThreshold = objectN.TimePreempt * beatmapInfo.StackLeniency;
double stackThreshold = objectN.TimePreempt * beatmap.StackLeniency;
if (objectN.StartTime - endTime > stackThreshold)
// We are no longer within stacking range of the next object.
@@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
OsuHitObject objectI = hitObjects[i];
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
double stackThreshold = objectI.TimePreempt * beatmapInfo.StackLeniency;
double stackThreshold = objectI.TimePreempt * beatmap.StackLeniency;
/* If this object is a hitcircle, then we enter this "special" case.
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
@@ -214,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
}
}
private static void applyStackingOld(BeatmapInfo beatmapInfo, List<OsuHitObject> hitObjects)
private static void applyStackingOld(IBeatmap beatmap, List<OsuHitObject> hitObjects)
{
for (int i = 0; i < hitObjects.Count; i++)
{
@@ -228,7 +228,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
for (int j = i + 1; j < hitObjects.Count; j++)
{
double stackThreshold = hitObjects[i].TimePreempt * beatmapInfo.StackLeniency;
double stackThreshold = hitObjects[i].TimePreempt * beatmap.StackLeniency;
if (hitObjects[j].StartTime - stackThreshold > startTime)
break;
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
public partial class OsuDistanceSnapProvider : ComposerDistanceSnapProvider
{
protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after)
public override double ReadCurrentDistanceSnap(HitObject before, HitObject after)
{
float expectedDistance = DurationToDistance(before, after.StartTime - before.GetEndTime());
float actualDistance = Vector2.Distance(((OsuHitObject)before).EndPosition, ((OsuHitObject)after).Position);
@@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
MinValue = 0f,
MaxValue = OsuPlayfield.BASE_SIZE.X,
Precision = 0.01f,
};
/// <summary>
@@ -47,6 +48,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
MinValue = 0f,
MaxValue = OsuPlayfield.BASE_SIZE.Y,
Precision = 0.01f,
};
/// <summary>
@@ -56,6 +58,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
MinValue = 4f,
MaxValue = 128f,
Precision = 0.01f,
};
/// <summary>
@@ -65,6 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
MinValue = -180f,
MaxValue = 180f,
Precision = 0.01f,
};
/// <summary>
@@ -166,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Edit
},
};
Spacing.Value = editorBeatmap.BeatmapInfo.GridSize;
Spacing.Value = editorBeatmap.GridSize;
}
protected override void LoadComplete()
@@ -200,7 +204,7 @@ namespace osu.Game.Rulesets.Osu.Edit
spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:#,0.##}";
spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:#,0.##}";
SpacingVector.Value = new Vector2(spacing.NewValue);
editorBeatmap.BeatmapInfo.GridSize = (int)spacing.NewValue;
editorBeatmap.GridSize = (int)spacing.NewValue;
}, true);
GridLinesRotation.BindValueChanged(rotation =>
@@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Setup
{
Caption = "Stack Leniency",
HintText = "In play mode, osu! automatically stacks notes which occur at the same location. Increasing this value means it is more likely to snap notes of further time-distance.",
Current = new BindableFloat(Beatmap.BeatmapInfo.StackLeniency)
Current = new BindableFloat(Beatmap.StackLeniency)
{
Default = 0.7f,
MinValue = 0,
@@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Setup
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
Beatmap.Difficulty.SliderTickRate = tickRateSlider.Current.Value;
Beatmap.BeatmapInfo.StackLeniency = stackLeniency.Current.Value;
Beatmap.StackLeniency = stackLeniency.Current.Value;
Beatmap.UpdateAllHitObjects();
Beatmap.SaveState();
@@ -63,18 +63,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
Origin = Anchor.Centre,
Texture = source.GetTexture("spinner-top"),
},
fixedMiddle = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = source.GetTexture("spinner-middle"),
},
spinningMiddle = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = source.GetTexture("spinner-middle2"),
},
fixedMiddle = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = source.GetTexture("spinner-middle"),
},
}
});
@@ -144,6 +144,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
foreach (var nested in hitObject.NestedHitObjects)
simulateHit(nested, ref attributes);
return;
case StrongNestedHitObject:
// we never need to deal with these directly.
// the only thing strong hits do in terms of scoring is double their object's score increase,
// which is already handled at the parent object level via the `strongable.IsStrong` check lower down in this method.
// not handling these here can lead to them falsely being counted as combo-increasing when handling strong drum rolls!
return;
}
if (hitObject is DrumRollTick tick)
@@ -80,16 +80,16 @@ namespace osu.Game.Tests.Beatmaps.Formats
var metadata = beatmap.Metadata;
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile);
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
Assert.AreEqual(0, beatmap.AudioLeadIn);
Assert.AreEqual(164471, metadata.PreviewTime);
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
Assert.AreEqual(0.7f, beatmap.StackLeniency);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
Assert.IsFalse(beatmapInfo.LetterboxInBreaks);
Assert.IsFalse(beatmapInfo.SpecialStyle);
Assert.IsFalse(beatmapInfo.WidescreenStoryboard);
Assert.IsFalse(beatmapInfo.SamplesMatchPlaybackRate);
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
Assert.IsFalse(beatmap.LetterboxInBreaks);
Assert.IsFalse(beatmap.SpecialStyle);
Assert.IsFalse(beatmap.WidescreenStoryboard);
Assert.IsFalse(beatmap.SamplesMatchPlaybackRate);
Assert.AreEqual(CountdownType.None, beatmap.Countdown);
Assert.AreEqual(0, beatmap.CountdownOffset);
}
}
@@ -101,7 +101,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new LineBufferedReader(resStream))
{
var beatmapInfo = decoder.Decode(stream).BeatmapInfo;
var beatmap = decoder.Decode(stream);
int[] expectedBookmarks =
{
@@ -109,13 +109,13 @@ namespace osu.Game.Tests.Beatmaps.Formats
95901, 106450, 116999, 119637, 130186, 140735, 151285,
161834, 164471, 175020, 185570, 196119, 206669, 209306
};
Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
Assert.AreEqual(expectedBookmarks.Length, beatmap.BeatmapInfo.Bookmarks.Length);
for (int i = 0; i < expectedBookmarks.Length; i++)
Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing);
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
Assert.AreEqual(4, beatmapInfo.GridSize);
Assert.AreEqual(2, beatmapInfo.TimelineZoom);
Assert.AreEqual(expectedBookmarks[i], beatmap.BeatmapInfo.Bookmarks[i]);
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
Assert.AreEqual(4, beatmap.BeatmapInfo.BeatDivisor);
Assert.AreEqual(4, beatmap.GridSize);
Assert.AreEqual(2, beatmap.TimelineZoom);
}
}
@@ -993,15 +993,15 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.Multiple(() =>
{
Assert.That(decoded.BeatmapInfo.AudioLeadIn, Is.EqualTo(0));
Assert.That(decoded.BeatmapInfo.StackLeniency, Is.EqualTo(0.7f));
Assert.That(decoded.BeatmapInfo.SpecialStyle, Is.False);
Assert.That(decoded.BeatmapInfo.LetterboxInBreaks, Is.False);
Assert.That(decoded.BeatmapInfo.WidescreenStoryboard, Is.False);
Assert.That(decoded.BeatmapInfo.EpilepsyWarning, Is.False);
Assert.That(decoded.BeatmapInfo.SamplesMatchPlaybackRate, Is.False);
Assert.That(decoded.BeatmapInfo.Countdown, Is.EqualTo(CountdownType.Normal));
Assert.That(decoded.BeatmapInfo.CountdownOffset, Is.EqualTo(0));
Assert.That(decoded.AudioLeadIn, Is.EqualTo(0));
Assert.That(decoded.StackLeniency, Is.EqualTo(0.7f));
Assert.That(decoded.SpecialStyle, Is.False);
Assert.That(decoded.LetterboxInBreaks, Is.False);
Assert.That(decoded.WidescreenStoryboard, Is.False);
Assert.That(decoded.EpilepsyWarning, Is.False);
Assert.That(decoded.SamplesMatchPlaybackRate, Is.False);
Assert.That(decoded.Countdown, Is.EqualTo(CountdownType.None));
Assert.That(decoded.CountdownOffset, Is.EqualTo(0));
Assert.That(decoded.BeatmapInfo.Metadata.PreviewTime, Is.EqualTo(-1));
Assert.That(decoded.BeatmapInfo.Ruleset.OnlineID, Is.EqualTo(0));
});
@@ -51,14 +51,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
var beatmap = decodeAsJson(normal);
var beatmapInfo = beatmap.BeatmapInfo;
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
Assert.AreEqual(false, beatmapInfo.SpecialStyle);
Assert.AreEqual(0, beatmap.AudioLeadIn);
Assert.AreEqual(0.7f, beatmap.StackLeniency);
Assert.AreEqual(false, beatmap.SpecialStyle);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
Assert.AreEqual(false, beatmap.LetterboxInBreaks);
Assert.AreEqual(false, beatmap.WidescreenStoryboard);
Assert.AreEqual(CountdownType.None, beatmap.Countdown);
Assert.AreEqual(0, beatmap.CountdownOffset);
}
[Test]
@@ -76,10 +76,10 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
for (int i = 0; i < expectedBookmarks.Length; i++)
Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing);
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
Assert.AreEqual(4, beatmapInfo.GridSize);
Assert.AreEqual(2, beatmapInfo.TimelineZoom);
Assert.AreEqual(4, beatmap.GridSize);
Assert.AreEqual(2, beatmap.TimelineZoom);
}
[Test]
@@ -0,0 +1,7 @@
# Higher global_level has higher priority, the default global_level
# is 100 for root .globalconfig and 0 for others
# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/configuration-files#precedence
is_global = true
global_level = 101
dotnet_diagnostic.CA2007.severity = none
@@ -41,7 +41,7 @@ namespace osu.Game.Tests.Database
Assert.That(lastChanges?.ModifiedIndices, Is.Empty);
Assert.That(lastChanges?.NewModifiedIndices, Is.Empty);
realm.Write(r => r.All<BeatmapSetInfo>().First().Beatmaps.First().CountdownOffset = 5);
realm.Write(r => r.All<BeatmapSetInfo>().First().Beatmaps.First().EditorTimestamp = 5);
realm.Run(r => r.Refresh());
Assert.That(collectionChanges, Is.EqualTo(1));
+5
View File
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
@@ -64,6 +65,10 @@ namespace osu.Game.Tests
// Beatmap must be imported before the collection manager is loaded.
if (withBeatmap)
BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely();
// the logic for setting the initial ruleset exists in OsuGame rather than OsuGameBase.
// the ruleset bindable is not meant to be nullable, so assign any ruleset in here.
Ruleset.Value = RulesetStore.AvailableRulesets.First();
}
}
}
@@ -73,7 +73,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
AddStep("create room initially in gameplay", () =>
{
var newRoom = new Room();
newRoom.CopyFrom(SelectedRoom.Value);
newRoom.CopyFrom(SelectedRoom.Value!);
newRoom.RoomID = null;
MultiplayerClient.RoomSetupAction = room =>
@@ -20,12 +20,53 @@ namespace osu.Game.Tests.NonVisual.Ranking
public void TestDistributedHits()
{
var events = Enumerable.Range(-5, 11)
.Select(t => new HitEvent(t - 5, 1.0, HitResult.Great, new HitObject(), null, null));
.Select(t => new HitEvent(t - 5, 1.0, HitResult.Great, new HitObject(), null, null))
.ToList();
var unstableRate = new UnstableRate(events);
Assert.IsNotNull(unstableRate.Value);
Assert.IsTrue(Precision.AlmostEquals(unstableRate.Value.Value, 10 * Math.Sqrt(10)));
Assert.AreEqual(unstableRate.Value.Value, 10 * Math.Sqrt(10), Precision.DOUBLE_EPSILON);
}
[Test]
public void TestDistributedHitsIncrementalRewind()
{
var events = Enumerable.Range(-5, 11)
.Select(t => new HitEvent(t - 5, 1.0, HitResult.Great, new HitObject(), null, null))
.ToList();
HitEventExtensions.UnstableRateCalculationResult result = null;
for (int i = 0; i < events.Count; i++)
{
result = events.GetRange(0, i + 1)
.CalculateUnstableRate(result);
}
result = events.GetRange(0, 2).CalculateUnstableRate(result);
Assert.IsNotNull(result!.Result);
Assert.AreEqual(5, result.Result, Precision.DOUBLE_EPSILON);
}
[Test]
public void TestDistributedHitsIncremental()
{
var events = Enumerable.Range(-5, 11)
.Select(t => new HitEvent(t - 5, 1.0, HitResult.Great, new HitObject(), null, null))
.ToList();
HitEventExtensions.UnstableRateCalculationResult result = null;
for (int i = 0; i < events.Count; i++)
{
result = events.GetRange(0, i + 1)
.CalculateUnstableRate(result);
}
Assert.IsNotNull(result!.Result);
Assert.AreEqual(10 * Math.Sqrt(10), result.Result, Precision.DOUBLE_EPSILON);
}
[Test]
@@ -1,8 +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.
#nullable disable
using System;
using System.Collections.Generic;
using NUnit.Framework;
@@ -21,12 +19,12 @@ namespace osu.Game.Tests.OnlinePlay
[HeadlessTest]
public partial class TestSceneCatchUpSyncManager : OsuTestScene
{
private GameplayClockContainer master;
private SpectatorSyncManager syncManager;
private GameplayClockContainer master = null!;
private SpectatorSyncManager syncManager = null!;
private Dictionary<SpectatorPlayerClock, int> clocksById;
private SpectatorPlayerClock player1;
private SpectatorPlayerClock player2;
private Dictionary<SpectatorPlayerClock, int> clocksById = null!;
private SpectatorPlayerClock player1 = null!;
private SpectatorPlayerClock player2 = null!;
[SetUp]
public void Setup()
@@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Editing
{
AddStep("turn countdown off", () => designSection.EnableCountdown.Current.Value = false);
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.None);
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.None);
AddUntilStep("other controls hidden", () => !designSection.CountdownSettings.IsPresent);
}
@@ -65,12 +65,12 @@ namespace osu.Game.Tests.Visual.Editing
{
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal);
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.Normal);
AddUntilStep("other controls shown", () => designSection.CountdownSettings.IsPresent);
AddStep("change countdown speed", () => designSection.CountdownSpeed.Current.Value = CountdownType.DoubleSpeed);
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.DoubleSpeed);
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.DoubleSpeed);
AddUntilStep("other controls still shown", () => designSection.CountdownSettings.IsPresent);
}
@@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Editing
{
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal);
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.Normal);
checkOffsetAfter("1", 1);
checkOffsetAfter(string.Empty, 0);
@@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("commit text", () => InputManager.Key(Key.Enter));
AddAssert($"displayed value is {expectedFinalValue}", () => designSection.CountdownOffset.Current.Value == expectedFinalValue.ToString(CultureInfo.InvariantCulture));
AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.BeatmapInfo.CountdownOffset == expectedFinalValue);
AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.CountdownOffset == expectedFinalValue);
}
private partial class TestDesignSection : DesignSection
@@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("Set beat divisor", () => Editor.Dependencies.Get<BindableBeatDivisor>().Value = 16);
AddStep("Set timeline zoom", () =>
{
originalTimelineZoom = EditorBeatmap.BeatmapInfo.TimelineZoom;
originalTimelineZoom = EditorBeatmap.TimelineZoom;
var timeline = Editor.ChildrenOfType<Timeline>().Single();
InputManager.MoveMouseTo(timeline);
@@ -81,19 +81,19 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("Ensure timeline zoom changed", () =>
{
changedTimelineZoom = EditorBeatmap.BeatmapInfo.TimelineZoom;
changedTimelineZoom = EditorBeatmap.TimelineZoom;
return !Precision.AlmostEquals(changedTimelineZoom, originalTimelineZoom);
});
SaveEditor();
AddAssert("Beatmap has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
AddAssert("Beatmap has correct timeline zoom", () => EditorBeatmap.BeatmapInfo.TimelineZoom == changedTimelineZoom);
AddAssert("Beatmap has correct timeline zoom", () => EditorBeatmap.TimelineZoom == changedTimelineZoom);
ReloadEditorToSameBeatmap();
AddAssert("Beatmap still has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
AddAssert("Beatmap still has correct timeline zoom", () => EditorBeatmap.BeatmapInfo.TimelineZoom == changedTimelineZoom);
AddAssert("Beatmap still has correct timeline zoom", () => EditorBeatmap.TimelineZoom == changedTimelineZoom);
}
[Test]
@@ -205,7 +205,7 @@ namespace osu.Game.Tests.Visual.Editing
{
double originalSpacing = 0;
AddStep("retrieve original spacing", () => originalSpacing = editorBeatmap.BeatmapInfo.DistanceSpacing);
AddStep("retrieve original spacing", () => originalSpacing = editorBeatmap.DistanceSpacing);
AddStep("hold ctrl", () => InputManager.PressKey(Key.LControl));
AddStep("hold alt", () => InputManager.PressKey(Key.LAlt));
@@ -215,7 +215,7 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt));
AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl));
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5);
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.DistanceSpacing == originalSpacing + 0.5);
}
public partial class EditorBeatmapContainer : PopoverContainer
@@ -527,8 +527,11 @@ namespace osu.Game.Tests.Visual.Editing
checkPlacementSampleBank(HitSampleInfo.BANK_NORMAL);
checkPlacementSampleAdditionBank(HitSampleInfo.BANK_NORMAL);
void checkPlacementSampleBank(string expected) => AddAssert($"Placement sample is {expected}", () => EditorBeatmap.PlacementObject.Value.Samples.First(s => s.Name == HitSampleInfo.HIT_NORMAL).Bank, () => Is.EqualTo(expected));
void checkPlacementSampleAdditionBank(string expected) => AddAssert($"Placement sample addition is {expected}", () => EditorBeatmap.PlacementObject.Value.Samples.First(s => s.Name != HitSampleInfo.HIT_NORMAL).Bank, () => Is.EqualTo(expected));
void checkPlacementSampleBank(string expected) => AddAssert($"Placement sample is {expected}",
() => EditorBeatmap.PlacementObject.Value.Samples.First(s => s.Name == HitSampleInfo.HIT_NORMAL).Bank, () => Is.EqualTo(expected));
void checkPlacementSampleAdditionBank(string expected) => AddAssert($"Placement sample addition is {expected}",
() => EditorBeatmap.PlacementObject.Value.Samples.First(s => s.Name != HitSampleInfo.HIT_NORMAL).Bank, () => Is.EqualTo(expected));
}
[Test]
@@ -781,15 +784,39 @@ namespace osu.Game.Tests.Visual.Editing
setAdditionBankViaPopover(HitSampleInfo.BANK_SOFT);
dismissPopover();
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH);
hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL);
hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_SOFT);
assertNoChanges();
AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]));
AddStep("select first object", () =>
{
EditorBeatmap.SelectedHitObjects.Clear();
EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]);
});
assertNoChanges();
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH);
hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL);
hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_SOFT);
AddStep("select second object", () =>
{
EditorBeatmap.SelectedHitObjects.Clear();
EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[1]);
});
assertNoChanges();
AddStep("select first object", () =>
{
EditorBeatmap.SelectedHitObjects.Clear();
EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]);
});
assertNoChanges();
void assertNoChanges()
{
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH);
hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL);
hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_SOFT);
hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL);
hitObjectHasSampleNormalBank(1, HitSampleInfo.BANK_SOFT);
hitObjectHasSampleAdditionBank(1, HitSampleInfo.BANK_SOFT);
}
}
private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () =>
@@ -883,11 +910,12 @@ namespace osu.Game.Tests.Visual.Editing
return h.Samples.All(o => o.Volume == volume);
});
private void hitObjectNodeHasSampleVolume(int objectIndex, int nodeIndex, int volume) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has volume {volume}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
return h is not null && h.NodeSamples[nodeIndex].All(o => o.Volume == volume);
});
private void hitObjectNodeHasSampleVolume(int objectIndex, int nodeIndex, int volume) => AddAssert(
$"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has volume {volume}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
return h is not null && h.NodeSamples[nodeIndex].All(o => o.Volume == volume);
});
private void setBankViaPopover(string bank) => AddStep($"set bank {bank} via popover", () =>
{
@@ -944,29 +972,33 @@ namespace osu.Game.Tests.Visual.Editing
return h.Samples.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
});
private void hitObjectNodeHasSamples(int objectIndex, int nodeIndex, params string[] samples) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has samples {string.Join(',', samples)}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
return h is not null && h.NodeSamples[nodeIndex].Select(s => s.Name).SequenceEqual(samples);
});
private void hitObjectNodeHasSamples(int objectIndex, int nodeIndex, params string[] samples) => AddAssert(
$"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has samples {string.Join(',', samples)}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
return h is not null && h.NodeSamples[nodeIndex].Select(s => s.Name).SequenceEqual(samples);
});
private void hitObjectNodeHasSampleBank(int objectIndex, int nodeIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has bank {bank}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
return h is not null && h.NodeSamples[nodeIndex].All(o => o.Bank == bank);
});
private void hitObjectNodeHasSampleBank(int objectIndex, int nodeIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has bank {bank}",
() =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
return h is not null && h.NodeSamples[nodeIndex].All(o => o.Bank == bank);
});
private void hitObjectNodeHasSampleNormalBank(int objectIndex, int nodeIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has normal bank {bank}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
return h is not null && h.NodeSamples[nodeIndex].Where(o => o.Name == HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
});
private void hitObjectNodeHasSampleNormalBank(int objectIndex, int nodeIndex, string bank) => AddAssert(
$"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has normal bank {bank}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
return h is not null && h.NodeSamples[nodeIndex].Where(o => o.Name == HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
});
private void hitObjectNodeHasSampleAdditionBank(int objectIndex, int nodeIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has addition bank {bank}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
return h is not null && h.NodeSamples[nodeIndex].Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
});
private void hitObjectNodeHasSampleAdditionBank(int objectIndex, int nodeIndex, string bank) => AddAssert(
$"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has addition bank {bank}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
return h is not null && h.NodeSamples[nodeIndex].Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
});
private void editorTimeIs(double time) => AddAssert($"editor time is {time}", () => Precision.AlmostEquals(EditorClock.CurrentTimeAccurate, time, 1));
}
@@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
{
BeatmapInfo = { AudioLeadIn = leadIn }
AudioLeadIn = leadIn
});
checkFirstFrameTime(expectedStartTime);
@@ -133,7 +133,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
Text = $"GameplayStartTime: {DrawableRuleset.GameplayStartTime} "
+ $"FirstHitObjectTime: {FirstHitObjectTime} "
+ $"LeadInTime: {Beatmap.Value.BeatmapInfo.AudioLeadIn} "
+ $"LeadInTime: {Beatmap.Value.Beatmap.AudioLeadIn} "
+ $"FirstFrameClockTime: {FirstFrameClockTime}"
});
}
@@ -18,6 +18,7 @@ using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets.Mods;
@@ -135,10 +136,10 @@ namespace osu.Game.Tests.Visual.Gameplay
var workingBeatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
// Add intro time to test quick retry skipping (TestQuickRetry).
workingBeatmap.BeatmapInfo.AudioLeadIn = 60000;
workingBeatmap.Beatmap.AudioLeadIn = 60000;
// Set up data for testing disclaimer display.
workingBeatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning ?? false;
workingBeatmap.Beatmap.EpilepsyWarning = epilepsyWarning ?? false;
workingBeatmap.BeatmapInfo.Status = onlineStatus ?? BeatmapOnlineStatus.Ranked;
Beatmap.Value = workingBeatmap;
@@ -207,7 +208,25 @@ namespace osu.Game.Tests.Visual.Gameplay
}
[Test]
public void TestBlockLoadViaFocus()
public void TestLoadNotBlockedViaArbitraryFocus()
{
AddStep("load dummy beatmap", () => resetPlayer(false));
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
AddUntilStep("click settings slider", () =>
{
InputManager.MoveMouseTo(loader.ChildrenOfType<OsuSliderBar<float>>().First());
InputManager.Click(MouseButton.Left);
return InputManager.FocusedDrawable is OsuSliderBar<float>;
});
AddUntilStep("wait for load ready", () => player?.LoadState == LoadState.Ready);
AddUntilStep("loads", () => !loader.IsCurrentScreen());
}
[Test]
public void TestBlockLoadViaOverlayFocus()
{
AddStep("load dummy beatmap", () => resetPlayer(false));
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
@@ -221,7 +221,7 @@ namespace osu.Game.Tests.Visual.Gameplay
string? filePath = null;
// Files starting with _ are temporary, created by CreateFileSafely call.
AddUntilStep("wait for export file", () => filePath = LocalStorage.GetFiles("exports").SingleOrDefault(f => !Path.GetFileName(f).StartsWith("_", StringComparison.Ordinal)), () => Is.Not.Null);
AddUntilStep("wait for export file", () => filePath = LocalStorage.GetFiles("exports").SingleOrDefault(f => !Path.GetFileName(f).StartsWith('_')), () => Is.Not.Null);
AddUntilStep("filesize is non-zero", () =>
{
try
@@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
{
BeatmapInfo = { AudioLeadIn = 60000 }
AudioLeadIn = 60000
});
AddUntilStep("wait for skip overlay", () => Player.ChildrenOfType<SkipOverlay>().First().IsButtonVisible);
@@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("load storyboard with only video", () =>
{
// LegacyStoryboardDecoder doesn't parse WidescreenStoryboard, so it is set manually
loadStoryboard("storyboard_only_video.osu", s => s.BeatmapInfo.WidescreenStoryboard = false);
loadStoryboard("storyboard_only_video.osu", s => s.Beatmap.WidescreenStoryboard = false);
});
AddAssert("storyboard is correct width", () => Precision.AlmostEquals(storyboard?.Width ?? 0f, 480 * 16 / 9f));
@@ -10,11 +10,13 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays;
using osu.Game.Overlays.Login;
using osu.Game.Overlays.Settings;
using osu.Game.Tests.Visual.Online;
using osu.Game.Users;
using osu.Game.Users.Drawables;
using osuTK.Input;
@@ -31,6 +33,9 @@ namespace osu.Game.Tests.Visual.Menus
[Resolved]
private OsuConfigManager configManager { get; set; } = null!;
[Cached(typeof(LocalUserStatisticsProvider))]
private readonly TestSceneUserPanel.TestUserStatisticsProvider statisticsProvider = new TestSceneUserPanel.TestUserStatisticsProvider();
[BackgroundDependencyLoader]
private void load()
{
@@ -170,6 +175,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("enter code", () => loginOverlay.ChildrenOfType<OsuTextBox>().First().Text = "88800088");
assertAPIState(APIState.Online);
AddStep("feed statistics", () => statisticsProvider.UpdateStatistics(new UserStatistics(), Ruleset.Value));
AddStep("click on flag", () =>
{
InputManager.MoveMouseTo(loginOverlay.ChildrenOfType<UpdateableFlag>().First());
@@ -3,8 +3,10 @@
using System.Linq;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Utils;
using osu.Game.Configuration;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
@@ -73,5 +75,57 @@ namespace osu.Game.Tests.Visual.Menus
((StarFountain)Children[1]).Shoot(-1);
});
}
[Test]
public void TestGameplayStarFountainsSetting()
{
Bindable<bool> starFountainsEnabled = null!;
AddStep("load configuration", () =>
{
var config = new OsuConfigManager(LocalStorage);
starFountainsEnabled = config.GetBindable<bool>(OsuSetting.StarFountains);
});
AddStep("make fountains", () =>
{
Children = new Drawable[]
{
new KiaiGameplayFountains.GameplayStarFountain
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
X = 75,
},
new KiaiGameplayFountains.GameplayStarFountain
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
X = -75,
},
};
});
AddStep("enable KiaiStarEffects", () => starFountainsEnabled.Value = true);
AddRepeatStep("activate fountains (enabled)", () =>
{
((KiaiGameplayFountains.GameplayStarFountain)Children[0]).Shoot(1);
((KiaiGameplayFountains.GameplayStarFountain)Children[1]).Shoot(-1);
}, 100);
AddStep("disable KiaiStarEffects", () => starFountainsEnabled.Value = false);
AddRepeatStep("attempt to activate fountains (disabled)", () =>
{
((KiaiGameplayFountains.GameplayStarFountain)Children[0]).Shoot(1);
((KiaiGameplayFountains.GameplayStarFountain)Children[1]).Shoot(-1);
}, 100);
AddStep("re-enable KiaiStarEffects", () => starFountainsEnabled.Value = true);
AddRepeatStep("activate fountains (re-enabled)", () =>
{
((KiaiGameplayFountains.GameplayStarFountain)Children[0]).Shoot(1);
((KiaiGameplayFountains.GameplayStarFountain)Children[1]).Shoot(-1);
}, 100);
}
}
}
@@ -101,7 +101,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("Gain", () =>
{
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
new ScoreInfo(),
new UserStatistics
{
@@ -118,7 +118,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("Loss", () =>
{
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
new ScoreInfo(),
new UserStatistics
{
@@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("Tiny increase in PP", () =>
{
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
new ScoreInfo(),
new UserStatistics
{
@@ -153,7 +153,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("No change 1", () =>
{
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
new ScoreInfo(),
new UserStatistics
{
@@ -170,7 +170,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("Was null", () =>
{
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
new ScoreInfo(),
new UserStatistics
{
@@ -187,7 +187,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("Became null", () =>
{
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
new ScoreInfo(),
new UserStatistics
{
@@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Mods
protected override TestPlayer CreateModPlayer(Ruleset ruleset)
{
var player = base.CreateModPlayer(ruleset);
player.RestartRequested = _ => restartRequested = true;
player.PrepareLoaderForRestart = _ => restartRequested = true;
return player;
}
@@ -1,8 +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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
@@ -31,7 +29,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
protected readonly BindableList<MultiplayerRoomUser> MultiplayerUsers = new BindableList<MultiplayerRoomUser>();
protected MultiplayerGameplayLeaderboard Leaderboard { get; private set; }
protected MultiplayerGameplayLeaderboard? Leaderboard { get; private set; }
protected virtual MultiplayerRoomUser CreateUser(int userId) => new MultiplayerRoomUser(userId);
@@ -40,7 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private readonly BindableList<int> multiplayerUserIds = new BindableList<int>();
private readonly BindableDictionary<int, SpectatorState> watchedUserStates = new BindableDictionary<int, SpectatorState>();
private OsuConfigManager config;
private OsuConfigManager config = null!;
private readonly Mock<SpectatorClient> spectatorClient = new Mock<SpectatorClient>();
private readonly Mock<MultiplayerClient> multiplayerClient = new Mock<MultiplayerClient>();
@@ -133,7 +131,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
LoadComponentAsync(Leaderboard = CreateLeaderboard(), Add);
});
AddUntilStep("wait for load", () => Leaderboard.IsLoaded);
AddUntilStep("wait for load", () => Leaderboard!.IsLoaded);
AddStep("check watch requests were sent", () =>
{
@@ -146,7 +144,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestScoreUpdates()
{
AddRepeatStep("update state", UpdateUserStatesRandomly, 100);
AddToggleStep("switch compact mode", expanded => Leaderboard.Expanded.Value = expanded);
AddToggleStep("switch compact mode", expanded => Leaderboard!.Expanded.Value = expanded);
}
[Test]
@@ -1,8 +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.
#nullable disable
using System;
using NUnit.Framework;
using osu.Framework.Graphics;
@@ -12,7 +10,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneCreateMultiplayerMatchButton : MultiplayerTestScene
{
private CreateMultiplayerMatchButton button;
private CreateMultiplayerMatchButton button = null!;
public override void SetUpSteps()
{
@@ -29,7 +27,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestButtonEnableStateChanges()
{
IDisposable joiningRoomOperation = null;
IDisposable joiningRoomOperation = null!;
assertButtonEnableState(true);
@@ -119,7 +119,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("4 circles displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 4);
AddAssert("46 hidden users", () => list.ChildrenOfType<DrawableRoomParticipantsList.HiddenUserCount>().Single().Count == 46);
AddStep("remove from end", () => removeUserAt(SelectedRoom.Value.RecentParticipants.Count - 1));
AddStep("remove from end", () => removeUserAt(SelectedRoom.Value!.RecentParticipants.Count - 1));
AddAssert("4 circles displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 4);
AddAssert("45 hidden users", () => list.ChildrenOfType<DrawableRoomParticipantsList.HiddenUserCount>().Single().Count == 45);
@@ -138,18 +138,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void addUser(int id)
{
SelectedRoom.Value.RecentParticipants = SelectedRoom.Value.RecentParticipants.Append(new APIUser
SelectedRoom.Value!.RecentParticipants = SelectedRoom.Value!.RecentParticipants.Append(new APIUser
{
Id = id,
Username = $"User {id}"
}).ToArray();
SelectedRoom.Value.ParticipantCount++;
SelectedRoom.Value!.ParticipantCount++;
}
private void removeUserAt(int index)
{
SelectedRoom.Value.RecentParticipants = SelectedRoom.Value.RecentParticipants.Where(u => !u.Equals(SelectedRoom.Value.RecentParticipants[index])).ToArray();
SelectedRoom.Value.ParticipantCount--;
SelectedRoom.Value!.RecentParticipants = SelectedRoom.Value!.RecentParticipants.Where(u => !u.Equals(SelectedRoom.Value!.RecentParticipants[index])).ToArray();
SelectedRoom.Value!.ParticipantCount--;
}
}
}
@@ -1,8 +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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -39,9 +37,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneDrawableRoomPlaylist : MultiplayerTestScene
{
private TestPlaylist playlist;
private BeatmapManager manager;
private TestPlaylist playlist = null!;
private BeatmapManager manager = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -199,14 +196,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestDownloadButtonHiddenWhenBeatmapExists()
{
Live<BeatmapSetInfo> imported = null;
Live<BeatmapSetInfo> imported = null!;
AddStep("import beatmap", () =>
{
var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo;
Debug.Assert(beatmap.BeatmapSet != null);
imported = manager.Import(beatmap.BeatmapSet);
imported = manager.Import(beatmap.BeatmapSet)!;
});
createPlaylistWithBeatmaps(() => imported.PerformRead(s => s.Beatmaps.Detach()));
@@ -378,7 +375,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
});
private void createPlaylist(Action<TestPlaylist> setupPlaylist = null)
private void createPlaylist(Action<TestPlaylist>? setupPlaylist = null)
{
AddStep("create playlist", () =>
{
@@ -1,8 +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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@@ -26,9 +24,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneFreeModSelectOverlay : MultiplayerTestScene
{
private FreeModSelectOverlay freeModSelectOverlay;
private FooterButtonFreeMods footerButtonFreeMods;
private ScreenFooter footer;
private FreeModSelectOverlay freeModSelectOverlay = null!;
private FooterButtonFreeMods footerButtonFreeMods = null!;
private ScreenFooter footer = null!;
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> availableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
[BackgroundDependencyLoader]
@@ -49,8 +47,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddToggleStep("toggle visibility", visible =>
{
if (freeModSelectOverlay != null)
freeModSelectOverlay.State.Value = visible ? Visibility.Visible : Visibility.Hidden;
freeModSelectOverlay.State.Value = visible ? Visibility.Visible : Visibility.Hidden;
});
}
@@ -1,8 +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.
#nullable disable
using System.Linq;
using Moq;
using NUnit.Framework;
@@ -20,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneGameplayChatDisplay : OsuManualInputManagerTestScene
{
private GameplayChatDisplay chatDisplay;
private GameplayChatDisplay chatDisplay = null!;
[Cached(typeof(ILocalUserPlayInfo))]
private ILocalUserPlayInfo localUserInfo;
@@ -1,8 +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.
#nullable disable
using System;
using System.Linq;
using NUnit.Framework;
@@ -70,13 +68,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
RunGameplay();
IBeatmapInfo firstBeatmap = null;
AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.ServerAPIRoom?.Playlist[0].Beatmap);
IBeatmapInfo firstBeatmap = null!;
AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.ServerAPIRoom!.Playlist[0].Beatmap);
selectNewItem(() => OtherBeatmap);
AddUntilStep("first playlist item hasn't changed", () => MultiplayerClient.ServerAPIRoom?.Playlist[0].Beatmap == firstBeatmap);
AddUntilStep("second playlist item changed", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Beatmap != firstBeatmap);
AddUntilStep("first playlist item hasn't changed", () => MultiplayerClient.ServerAPIRoom!.Playlist[0].Beatmap == firstBeatmap);
AddUntilStep("second playlist item changed", () => MultiplayerClient.ClientAPIRoom!.Playlist[1].Beatmap != firstBeatmap);
}
[Test]
@@ -103,7 +101,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded);
BeatmapInfo otherBeatmap = null;
BeatmapInfo otherBeatmap = null!;
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(otherBeatmap = beatmap()));
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
@@ -1,8 +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.
#nullable disable
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
@@ -23,7 +21,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
private RoomsContainer container;
private RoomsContainer container = null!;
public override void SetUpSteps()
{
@@ -65,7 +63,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("select first room", () => container.Rooms.First().TriggerClick());
AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight)));
AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID)));
AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID)!));
AddAssert("first spotlight still selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight)));
AddStep("remove spotlight room", () => RoomManager.RemoveRoom(RoomManager.Rooms.Single(r => r.Category == RoomCategory.Spotlight)));
@@ -157,7 +155,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("add rooms", () => RoomManager.AddRooms(3, new CatchRuleset().RulesetInfo));
// Todo: What even is this case...?
AddStep("set empty filter criteria", () => container.Filter.Value = null);
AddStep("set empty filter criteria", () => container.Filter.Value = new FilterCriteria());
AddUntilStep("5 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 5);
AddStep("filter osu! rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo });
@@ -195,9 +193,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("add rooms", () => RoomManager.AddRooms(3, withPassword: true));
}
private bool checkRoomSelected(Room room) => SelectedRoom.Value == room;
private bool checkRoomSelected(Room? room) => SelectedRoom.Value == room;
private Room getRoomInFlow(int index) =>
private Room? getRoomInFlow(int index) =>
(container.ChildrenOfType<FillFlowContainer<DrawableLoungeRoom>>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room;
}
}
@@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void createNewItem()
{
SelectedRoom.Value.Playlist = SelectedRoom.Value.Playlist.Append(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
SelectedRoom.Value!.Playlist = SelectedRoom.Value.Playlist.Append(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
ID = SelectedRoom.Value.Playlist.Count,
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
@@ -1,8 +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.
#nullable disable
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
@@ -18,8 +16,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiSpectatorLeaderboard : MultiplayerTestScene
{
private Dictionary<int, ManualClock> clocks;
private MultiSpectatorLeaderboard leaderboard;
private Dictionary<int, ManualClock> clocks = null!;
private MultiSpectatorLeaderboard? leaderboard;
[SetUpSteps]
public override void SetUpSteps()
@@ -55,13 +53,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
}, Add);
});
AddUntilStep("wait for load", () => leaderboard.IsLoaded);
AddUntilStep("wait for load", () => leaderboard!.IsLoaded);
AddUntilStep("wait for user population", () => leaderboard.ChildrenOfType<GameplayLeaderboardScore>().Count() == 2);
AddStep("add clock sources", () =>
{
foreach ((int userId, var clock) in clocks)
leaderboard.AddClock(userId, clock);
leaderboard!.AddClock(userId, clock);
});
}
@@ -406,13 +406,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
/// <summary>
/// Tests spectating with a beatmap that has a high <see cref="BeatmapInfo.AudioLeadIn"/> value.
/// Tests spectating with a beatmap that has a high <see cref="IBeatmap.AudioLeadIn"/> value.
///
/// This test is not intended not to check the correct initial time value, but only to guard against
/// gameplay potentially getting stuck in a stopped state due to lead in time being present.
/// </summary>
[Test]
public void TestAudioLeadIn() => testLeadIn(b => b.BeatmapInfo.AudioLeadIn = 2000);
public void TestAudioLeadIn() => testLeadIn(b => b.Beatmap.AudioLeadIn = 2000);
/// <summary>
/// Tests spectating with a beatmap that has a storyboard element with a negative start time (i.e. intro storyboard element).
@@ -455,7 +455,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
applyToBeatmap?.Invoke(Beatmap.Value);
LoadScreen(spectatorScreen = new MultiSpectatorScreen(SelectedRoom.Value, playingUsers.ToArray()));
LoadScreen(spectatorScreen = new MultiSpectatorScreen(SelectedRoom.Value!, playingUsers.ToArray()));
});
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded));
@@ -37,10 +37,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestPerUserMods()
{
AddStep("first user has no mods", () => Assert.That(((TestLeaderboard)Leaderboard).UserMods[0], Is.Empty));
AddStep("first user has no mods", () => Assert.That(((TestLeaderboard)Leaderboard!).UserMods[0], Is.Empty));
AddStep("last user has NF mod", () =>
{
Assert.That(((TestLeaderboard)Leaderboard).UserMods[TOTAL_USERS - 1], Has.One.Items);
Assert.That(((TestLeaderboard)Leaderboard!).UserMods[TOTAL_USERS - 1], Has.One.Items);
Assert.That(((TestLeaderboard)Leaderboard).UserMods[TOTAL_USERS - 1].Single(), Is.TypeOf<OsuModNoFail>());
});
}
@@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
LoadComponentAsync(new MatchScoreDisplay
{
Team1Score = { BindTarget = Leaderboard.TeamScores[0] },
Team1Score = { BindTarget = Leaderboard!.TeamScores[0] },
Team2Score = { BindTarget = Leaderboard.TeamScores[1] }
}, Add);
@@ -1,8 +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.
#nullable disable
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics.Containers;
@@ -22,10 +20,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
private LoungeSubScreen loungeScreen;
private Room lastJoinedRoom;
private string lastJoinedPassword;
private LoungeSubScreen loungeScreen = null!;
private Room? lastJoinedRoom;
private string? lastJoinedPassword;
public override void SetUpSteps()
{
@@ -87,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestJoinRoomWithIncorrectPasswordViaButton()
{
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
@@ -97,14 +94,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
AddAssert("room not joined", () => loungeScreen.IsCurrentScreen());
AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible);
AddUntilStep("password prompt still visible", () => passwordEntryPopover!.State.Value == Visibility.Visible);
AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox);
}
[Test]
public void TestJoinRoomWithIncorrectPasswordViaEnter()
{
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
@@ -114,14 +111,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("press enter", () => InputManager.Key(Key.Enter));
AddAssert("room not joined", () => loungeScreen.IsCurrentScreen());
AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible);
AddUntilStep("password prompt still visible", () => passwordEntryPopover!.State.Value == Visibility.Visible);
AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox);
}
[Test]
public void TestJoinRoomWithCorrectPassword()
{
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
@@ -137,7 +134,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestJoinRoomWithPasswordViaKeyboardOnly()
{
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
@@ -150,7 +147,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("room join password correct", () => lastJoinedPassword == "password");
}
private void onRoomJoined(Room room, string password)
private void onRoomJoined(Room room, string? password)
{
lastJoinedRoom = room;
lastJoinedPassword = password;
@@ -1,12 +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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@@ -35,17 +32,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiplayerMatchSongSelect : MultiplayerTestScene
{
private BeatmapManager manager;
private RulesetStore rulesets;
private BeatmapManager manager = null!;
private RulesetStore rulesets = null!;
private IList<BeatmapInfo> beatmaps => importedBeatmapSet?.PerformRead(s => s.Beatmaps) ?? new List<BeatmapInfo>();
private IList<BeatmapInfo> beatmaps => importedBeatmapSet.PerformRead(s => s.Beatmaps);
private TestMultiplayerMatchSongSelect songSelect;
private Live<BeatmapSetInfo> importedBeatmapSet;
private TestMultiplayerMatchSongSelect songSelect = null!;
private Live<BeatmapSetInfo> importedBeatmapSet = null!;
[Resolved]
private OsuConfigManager configManager { get; set; }
private OsuConfigManager configManager { get; set; } = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -57,7 +53,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
Dependencies.Cache(Realm);
importedBeatmapSet = manager.Import(TestResources.CreateTestBeatmapSetInfo(8, rulesets.AvailableRulesets.ToArray()));
importedBeatmapSet = manager.Import(TestResources.CreateTestBeatmapSetInfo(8, rulesets.AvailableRulesets.ToArray()))!;
Add(detachedBeatmapStore);
}
@@ -71,7 +67,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
SelectedMods.SetDefault();
});
AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value)));
AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value!)));
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
}
@@ -88,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestBeatmapConfirmed()
{
BeatmapInfo selectedBeatmap = null;
BeatmapInfo selectedBeatmap = null!;
setUp();
@@ -117,8 +113,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
setUp();
AddStep("change ruleset", () => Ruleset.Value = new OsuRuleset().RulesetInfo);
AddStep($"select {allowedMod.ReadableName()} as allowed", () => songSelect.FreeMods.Value = new[] { (Mod)Activator.CreateInstance(allowedMod) });
AddStep($"select {requiredMod.ReadableName()} as required", () => songSelect.Mods.Value = new[] { (Mod)Activator.CreateInstance(requiredMod) });
AddStep($"select {allowedMod.ReadableName()} as allowed", () => songSelect.FreeMods.Value = new[] { (Mod)Activator.CreateInstance(allowedMod)! });
AddStep($"select {requiredMod.ReadableName()} as required", () => songSelect.Mods.Value = new[] { (Mod)Activator.CreateInstance(requiredMod)! });
AddAssert("freemods empty", () => songSelect.FreeMods.Value.Count == 0);
@@ -141,7 +137,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create song select", () =>
{
SelectedRoom.Value.Playlist.Single().RulesetID = 2;
SelectedRoom.Value!.Playlist.Single().RulesetID = 2;
songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value, SelectedRoom.Value.Playlist.Single());
songSelect.OnLoadComplete += _ => Ruleset.Value = new TaikoRuleset().RulesetInfo;
LoadScreen(songSelect);
@@ -171,7 +167,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public new BeatmapCarousel Carousel => base.Carousel;
public TestMultiplayerMatchSongSelect(Room room, [CanBeNull] PlaylistItem itemToEdit = null)
public TestMultiplayerMatchSongSelect(Room room, PlaylistItem? itemToEdit = null)
: base(room, itemToEdit)
{
}
@@ -1,10 +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.
#nullable disable
using System.Linq;
using JetBrains.Annotations;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@@ -42,10 +39,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiplayerMatchSubScreen : MultiplayerTestScene
{
private MultiplayerMatchSubScreen screen;
private BeatmapManager beatmaps;
private BeatmapSetInfo importedSet;
private MultiplayerMatchSubScreen screen = null!;
private BeatmapManager beatmaps = null!;
private BeatmapSetInfo importedSet = null!;
public TestSceneMultiplayerMatchSubScreen()
: base(false)
@@ -70,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("load match", () =>
{
SelectedRoom.Value = new Room { Name = "Test Room" };
LoadScreen(screen = new TestMultiplayerMatchSubScreen(SelectedRoom.Value));
LoadScreen(screen = new TestMultiplayerMatchSubScreen(SelectedRoom.Value!));
});
AddUntilStep("wait for load", () => screen.IsCurrentScreen());
@@ -81,7 +77,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item", () =>
{
SelectedRoom.Value.Playlist =
SelectedRoom.Value!.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -100,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item", () =>
{
SelectedRoom.Value.Playlist =
SelectedRoom.Value!.Playlist =
[
new PlaylistItem(new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo)
{
@@ -125,7 +121,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("set playlist", () =>
{
SelectedRoom.Value.Playlist =
SelectedRoom.Value!.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -142,7 +138,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("set playlist", () =>
{
SelectedRoom.Value.Playlist =
SelectedRoom.Value!.Playlist =
[
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
{
@@ -173,7 +169,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item with allowed mod", () =>
{
SelectedRoom.Value.Playlist =
SelectedRoom.Value!.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -202,7 +198,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item with allowed mod", () =>
{
SelectedRoom.Value.Playlist =
SelectedRoom.Value!.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -226,7 +222,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item with no allowed mods", () =>
{
SelectedRoom.Value.Playlist =
SelectedRoom.Value!.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -249,7 +245,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add two playlist items", () =>
{
SelectedRoom.Value.Playlist =
SelectedRoom.Value!.Playlist =
[
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
{
@@ -285,7 +281,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item", () =>
{
SelectedRoom.Value.Playlist =
SelectedRoom.Value!.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -320,8 +316,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private partial class TestMultiplayerMatchSubScreen : MultiplayerMatchSubScreen
{
[Resolved(canBeNull: true)]
[CanBeNull]
private IDialogOverlay dialogOverlay { get; set; }
private IDialogOverlay? dialogOverlay { get; set; }
public TestMultiplayerMatchSubScreen(Room room)
: base(room)
@@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("make second user host", () => MultiplayerClient.TransferHost(3));
AddUntilStep("kick buttons not visible", () => this.ChildrenOfType<ParticipantPanel.KickButton>().Count(d => d.IsPresent) == 0);
AddUntilStep("kick buttons not visible", () => !this.ChildrenOfType<ParticipantPanel.KickButton>().Any(d => d.IsPresent));
AddStep("make local user host again", () => MultiplayerClient.TransferHost(API.LocalUser.Value.Id));
@@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create list", () =>
{
Child = list = new MultiplayerPlaylist(SelectedRoom.Value)
Child = list = new MultiplayerPlaylist(SelectedRoom.Value!)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create playlist", () =>
{
Child = playlist = new MultiplayerQueueList(SelectedRoom.Value)
Child = playlist = new MultiplayerQueueList(SelectedRoom.Value!)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -1,8 +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.
#nullable disable
using NUnit.Framework;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Osu;
@@ -16,7 +14,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestDisplayResults()
{
MultiplayerResultsScreen screen = null;
MultiplayerResultsScreen screen = null!;
AddStep("show results screen", () =>
{
@@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create button", () =>
{
PlaylistItem item = SelectedRoom.Value.Playlist.First();
PlaylistItem item = SelectedRoom.Value!.Playlist.First();
AvailabilityTracker.SelectedItem.Value = item;
@@ -1,8 +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.
#nullable disable
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
@@ -16,7 +14,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiplayerSpectatorPlayerGrid : OsuManualInputManagerTestScene
{
private PlayerGrid grid;
private PlayerGrid grid = null!;
[SetUp]
public void Setup() => Schedule(() =>
@@ -1,8 +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.
#nullable disable
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Bindables;
@@ -32,7 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[TestCase(1048576, 1048576)]
public void TestDisplayTeamResults(int team1Score, int team2Score)
{
MultiplayerResultsScreen screen = null;
MultiplayerResultsScreen screen = null!;
AddStep("show results screen", () =>
{
@@ -1,8 +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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@@ -28,18 +26,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestScenePlaylistsRoomSettingsPlaylist : OnlinePlayTestScene
{
private TestPlaylist playlist;
private TestPlaylist playlist = null!;
[Test]
public void TestItemRemovedOnDeletion()
{
PlaylistItem selectedItem = null;
PlaylistItem selectedItem = null!;
createPlaylist();
moveToItem(0);
AddStep("click", () => InputManager.Click(MouseButton.Left));
AddStep("retrieve selection", () => selectedItem = playlist.SelectedItem.Value);
AddStep("retrieve selection", () => selectedItem = playlist.SelectedItem.Value!);
moveToDeleteButton(0);
AddStep("click delete button", () => InputManager.Click(MouseButton.Left));
@@ -122,7 +120,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.MoveMouseTo(item.ChildrenOfType<DrawableRoomPlaylistItem.PlaylistRemoveButton>().ElementAt(0), offset);
});
private void createPlaylist(Action<TestPlaylist> setupPlaylist = null)
private void createPlaylist(Action<TestPlaylist>? setupPlaylist = null)
{
AddStep("create playlist", () =>
{
@@ -1,8 +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.
#nullable disable
using System;
using System.Linq;
using NUnit.Framework;
@@ -27,9 +25,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestScenePlaylistsSongSelect : OnlinePlayTestScene
{
private BeatmapManager manager;
private TestPlaylistsSongSelect songSelect;
private BeatmapManager manager = null!;
private TestPlaylistsSongSelect songSelect = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -60,7 +57,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
SelectedMods.Value = Array.Empty<Mod>();
});
AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(SelectedRoom.Value)));
AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(SelectedRoom.Value!)));
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
}
@@ -68,14 +65,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestItemAddedIfEmptyOnStart()
{
AddStep("finalise selection", () => songSelect.FinaliseSelection());
AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
}
[Test]
public void TestItemAddedWhenCreateNewItemClicked()
{
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
}
[Test]
@@ -83,7 +80,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddStep("finalise selection", () => songSelect.FinaliseSelection());
AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
}
[Test]
@@ -91,7 +88,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddAssert("playlist has 2 items", () => SelectedRoom.Value.Playlist.Count == 2);
AddAssert("playlist has 2 items", () => SelectedRoom.Value!.Playlist.Count == 2);
}
[Test]
@@ -99,10 +96,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddStep("rearrange", () => SelectedRoom.Value.Playlist = SelectedRoom.Value.Playlist.Skip(1).Append(SelectedRoom.Value.Playlist[0]).ToArray());
AddStep("rearrange", () => SelectedRoom.Value!.Playlist = SelectedRoom.Value!.Playlist.Skip(1).Append(SelectedRoom.Value!.Playlist[0]).ToArray());
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddAssert("new item has id 2", () => SelectedRoom.Value.Playlist.Last().ID == 2);
AddAssert("new item has id 2", () => SelectedRoom.Value!.Playlist.Last().ID == 2);
}
/// <summary>
@@ -118,13 +115,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("item 1 has rate 1.5", () =>
{
var mod = (OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
var mod = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(1.5, mod.SpeedChange.Value);
});
AddAssert("item 2 has rate 2", () =>
{
var mod = (OsuModDoubleTime)SelectedRoom.Value.Playlist.Last().RequiredMods[0].ToMod(new OsuRuleset());
var mod = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.Last().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(2, mod.SpeedChange.Value);
});
}
@@ -135,7 +132,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestGlobalModInstancesNotRetained()
{
OsuModDoubleTime mod = null;
OsuModDoubleTime mod = null!;
AddStep("set dt mod and store", () =>
{
@@ -150,7 +147,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2);
AddAssert("item has rate 1.5", () =>
{
var m = (OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
var m = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(1.5, m.SpeedChange.Value);
});
}
@@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("set playlist", () =>
{
SelectedRoom.Value.Playlist =
SelectedRoom.Value!.Playlist =
[
new PlaylistItem(new BeatmapInfo { StarRating = min }),
new PlaylistItem(new BeatmapInfo { StarRating = max }),
@@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Navigation
});
AddStep("create IPC sender channels", () =>
{
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { IPCPort = OsuGame.IPC_PORT });
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { IPCPipeName = OsuGame.IPC_PIPE_NAME });
osuSchemeLinkIPCSender = new OsuSchemeLinkIPCChannel(ipcSenderHost);
archiveImportIPCSender = new ArchiveImportIPCChannel(ipcSenderHost);
});
@@ -354,6 +354,23 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("retry count is 1", () => player.RestartCount == 1);
}
[Test]
public void TestLastScoreNullAfterExitingPlayer()
{
AddUntilStep("wait for last play null", getLastPlay, () => Is.Null);
var getOriginalPlayer = playToCompletion();
AddStep("attempt to retry", () => getOriginalPlayer().ChildrenOfType<HotkeyRetryOverlay>().First().Action());
AddUntilStep("wait for last play matches player", getLastPlay, () => Is.EqualTo(getOriginalPlayer().Score.ScoreInfo));
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != getOriginalPlayer() && Game.ScreenStack.CurrentScreen is Player);
AddStep("exit player", () => (Game.ScreenStack.CurrentScreen as Player)?.Exit());
AddUntilStep("wait for last play null", getLastPlay, () => Is.Null);
ScoreInfo getLastPlay() => Game.Dependencies.Get<SessionStatics>().Get<ScoreInfo>(Static.LastLocalUserScore);
}
[Test]
public void TestRetryImmediatelyAfterCompletion()
{
@@ -457,6 +457,61 @@ namespace osu.Game.Tests.Visual.Online
waitForChannel1Visible();
}
[Test]
public void TestPublicChannelsSortedByName()
{
// Intentionally join back to front.
AddStep("Show overlay with channel 2", () =>
{
channelManager.CurrentChannel.Value = channelManager.JoinChannel(testChannel2);
chatOverlay.Show();
});
AddUntilStep("second channel is at top of list", () => getFirstVisiblePublicChannel().Channel == testChannel2);
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
AddUntilStep("first channel is at top of list", () => getFirstVisiblePublicChannel().Channel == testChannel1);
AddStep("message in channel 2", () =>
{
testChannel2.AddNewMessages(new Message(1) { Content = "hi!", Sender = new APIUser { Username = "person" } });
});
AddUntilStep("first channel still at top of list", () => getFirstVisiblePublicChannel().Channel == testChannel1);
ChannelListItem getFirstVisiblePublicChannel() =>
chatOverlay.ChildrenOfType<ChannelList>().Single().PublicChannelGroup.ItemFlow.FlowingChildren.OfType<ChannelListItem>().First(item => item.Channel.Type == ChannelType.Public);
}
[Test]
public void TestPrivateChannelsSortedByRecent()
{
Channel pmChannel1 = createPrivateChannel();
Channel pmChannel2 = createPrivateChannel();
joinChannel(pmChannel1);
joinChannel(pmChannel2);
AddStep("Show overlay", () => chatOverlay.Show());
AddUntilStep("first channel is at top of list", () => getFirstVisiblePMChannel().Channel == pmChannel1);
AddStep("message in channel 2", () =>
{
pmChannel2.AddNewMessages(new Message(1) { Content = "hi!", Sender = new APIUser { Username = "person" } });
});
AddUntilStep("wait for first channel raised to top of list", () => getFirstVisiblePMChannel().Channel == pmChannel2);
AddStep("message in channel 1", () =>
{
pmChannel1.AddNewMessages(new Message(1) { Content = "hi!", Sender = new APIUser { Username = "person" } });
});
AddUntilStep("wait for first channel raised to top of list", () => getFirstVisiblePMChannel().Channel == pmChannel1);
ChannelListItem getFirstVisiblePMChannel() =>
chatOverlay.ChildrenOfType<ChannelList>().Single().PrivateChannelGroup.ItemFlow.FlowingChildren.OfType<ChannelListItem>().First(item => item.Channel.Type == ChannelType.PM);
}
[Test]
public void TestKeyboardNewChannel()
{
@@ -0,0 +1,179 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Online
{
public partial class TestSceneLocalUserStatisticsProvider : OsuTestScene
{
private LocalUserStatisticsProvider statisticsProvider = null!;
private readonly Dictionary<(int userId, string rulesetName), UserStatistics> serverSideStatistics = new Dictionary<(int userId, string rulesetName), UserStatistics>();
[SetUpSteps]
public void SetUpSteps()
{
AddStep("clear statistics", () => serverSideStatistics.Clear());
setUser(1000);
AddStep("setup provider", () =>
{
OsuTextFlowContainer text;
((DummyAPIAccess)API).HandleRequest = r =>
{
switch (r)
{
case GetUserRequest userRequest:
int userId = int.Parse(userRequest.Lookup);
string rulesetName = userRequest.Ruleset!.ShortName;
var response = new APIUser
{
Id = userId,
Statistics = tryGetStatistics(userId, rulesetName)
};
userRequest.TriggerSuccess(response);
return true;
default:
return false;
}
};
Clear();
Add(statisticsProvider = new LocalUserStatisticsProvider());
Add(text = new OsuTextFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
statisticsProvider.StatisticsUpdated += update =>
{
text.Clear();
foreach (var ruleset in Dependencies.Get<RulesetStore>().AvailableRulesets)
{
text.AddText(statisticsProvider.GetStatisticsFor(ruleset) is UserStatistics statistics
? $"{ruleset.Name} statistics: (total score: {statistics.TotalScore})"
: $"{ruleset.Name} statistics: (null)");
text.NewLine();
}
text.AddText($"latest update: {update.Ruleset}"
+ $" ({(update.OldStatistics?.TotalScore.ToString() ?? "null")} -> {update.NewStatistics.TotalScore})");
};
Ruleset.Value = new OsuRuleset().RulesetInfo;
});
}
[Test]
public void TestInitialStatistics()
{
AddAssert("osu statistics populated", () => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(4_000_000));
AddAssert("taiko statistics populated", () => statisticsProvider.GetStatisticsFor(new TaikoRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(3_000_000));
AddAssert("catch statistics populated", () => statisticsProvider.GetStatisticsFor(new CatchRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(2_000_000));
AddAssert("mania statistics populated", () => statisticsProvider.GetStatisticsFor(new ManiaRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(1_000_000));
}
[Test]
public void TestUserChanges()
{
setUser(1001);
AddStep("update statistics for user 1000", () =>
{
serverSideStatistics[(1000, "osu")] = new UserStatistics { TotalScore = 5_000_000 };
serverSideStatistics[(1000, "taiko")] = new UserStatistics { TotalScore = 6_000_000 };
});
AddAssert("statistics matches user 1001 in osu",
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
() => Is.EqualTo(4_000_000));
AddAssert("statistics matches user 1001 in taiko",
() => statisticsProvider.GetStatisticsFor(new TaikoRuleset().RulesetInfo)!.TotalScore,
() => Is.EqualTo(3_000_000));
setUser(1000, false);
AddAssert("statistics matches user 1000 in osu",
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
() => Is.EqualTo(5_000_000));
AddAssert("statistics matches user 1000 in taiko",
() => statisticsProvider.GetStatisticsFor(new TaikoRuleset().RulesetInfo)!.TotalScore,
() => Is.EqualTo(6_000_000));
}
[Test]
public void TestRefetchStatistics()
{
UserStatisticsUpdate? update = null;
setUser(1001);
AddStep("update statistics server side",
() => serverSideStatistics[(1001, "osu")] = new UserStatistics { TotalScore = 9_000_000 });
AddAssert("statistics match old score",
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
() => Is.EqualTo(4_000_000));
AddStep("setup event", () =>
{
update = null;
statisticsProvider.StatisticsUpdated -= onStatisticsUpdated;
statisticsProvider.StatisticsUpdated += onStatisticsUpdated;
});
AddStep("request refetch", () => statisticsProvider.RefetchStatistics(new OsuRuleset().RulesetInfo));
AddUntilStep("statistics update raised",
() => update?.NewStatistics.TotalScore,
() => Is.EqualTo(9_000_000));
AddAssert("statistics match new score",
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
() => Is.EqualTo(9_000_000));
void onStatisticsUpdated(UserStatisticsUpdate u) => update = u;
}
private UserStatistics tryGetStatistics(int userId, string rulesetName)
=> serverSideStatistics.TryGetValue((userId, rulesetName), out var stats) ? stats : new UserStatistics();
private void setUser(int userId, bool generateStatistics = true)
{
AddStep($"set local user to {userId}", () =>
{
if (generateStatistics)
{
serverSideStatistics[(userId, "osu")] = new UserStatistics { TotalScore = 4_000_000 };
serverSideStatistics[(userId, "taiko")] = new UserStatistics { TotalScore = 3_000_000 };
serverSideStatistics[(userId, "fruits")] = new UserStatistics { TotalScore = 2_000_000 };
serverSideStatistics[(userId, "mania")] = new UserStatistics { TotalScore = 1_000_000 };
}
((DummyAPIAccess)API).LocalUser.Value = new APIUser { Id = userId };
});
}
}
}
@@ -1,8 +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.
#nullable disable
using System;
using NUnit.Framework;
using osu.Framework.Allocation;
@@ -11,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Online;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Rulesets;
@@ -24,17 +23,20 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public partial class TestSceneUserPanel : OsuTestScene
{
private readonly Bindable<UserActivity> activity = new Bindable<UserActivity>();
private readonly Bindable<UserActivity?> activity = new Bindable<UserActivity?>();
private readonly Bindable<UserStatus?> status = new Bindable<UserStatus?>();
private UserGridPanel boundPanel1;
private TestUserListPanel boundPanel2;
private UserGridPanel boundPanel1 = null!;
private TestUserListPanel boundPanel2 = null!;
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
[Cached(typeof(LocalUserStatisticsProvider))]
private readonly TestUserStatisticsProvider statisticsProvider = new TestUserStatisticsProvider();
[Resolved]
private IRulesetStore rulesetStore { get; set; }
private IRulesetStore rulesetStore { get; set; } = null!;
[SetUp]
public void SetUp() => Schedule(() =>
@@ -42,7 +44,11 @@ namespace osu.Game.Tests.Visual.Online
activity.Value = null;
status.Value = null;
Child = new FillFlowContainer
Remove(statisticsProvider, false);
Clear();
Add(statisticsProvider);
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -108,7 +114,7 @@ namespace osu.Game.Tests.Visual.Online
Statistics = new UserStatistics { GlobalRank = null, CountryRank = null }
}) { Width = 300 }
}
};
});
boundPanel1.Status.BindTo(status);
boundPanel1.Activity.BindTo(activity);
@@ -162,24 +168,21 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep("update statistics", () =>
{
API.UpdateStatistics(new UserStatistics
statisticsProvider.UpdateStatistics(new UserStatistics
{
GlobalRank = RNG.Next(100000),
CountryRank = RNG.Next(100000)
});
}, Ruleset.Value);
});
AddStep("set statistics to something big", () =>
{
API.UpdateStatistics(new UserStatistics
statisticsProvider.UpdateStatistics(new UserStatistics
{
GlobalRank = RNG.Next(1_000_000, 100_000_000),
CountryRank = RNG.Next(1_000_000, 100_000_000)
});
});
AddStep("set statistics to empty", () =>
{
API.UpdateStatistics(new UserStatistics());
}, Ruleset.Value);
});
AddStep("set statistics to empty", () => statisticsProvider.UpdateStatistics(new UserStatistics(), Ruleset.Value));
}
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(new BeatmapInfo(), rulesetStore.GetRuleset(rulesetId)!);
@@ -201,5 +204,11 @@ namespace osu.Game.Tests.Visual.Online
public new TextFlowContainer LastVisitMessage => base.LastVisitMessage;
}
public partial class TestUserStatisticsProvider : LocalUserStatisticsProvider
{
public new void UpdateStatistics(UserStatistics newStatistics, RulesetInfo ruleset, Action<UserStatisticsUpdate>? callback = null)
=> base.UpdateStatistics(newStatistics, ruleset, callback);
}
}
}
@@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Online
AddSliderStep("weekly best", 0, 250, 1, v => update(s => s.WeeklyStreakBest = v));
AddSliderStep("top 10%", 0, 999, 0, v => update(s => s.Top10PercentPlacements = v));
AddSliderStep("top 50%", 0, 999, 0, v => update(s => s.Top50PercentPlacements = v));
AddSliderStep("playcount", 0, 999, 0, v => update(s => s.PlayCount = v));
AddSliderStep("playcount", 0, 1500, 1, v => update(s => s.PlayCount = v));
AddStep("create", () =>
{
Clear();
@@ -66,8 +66,8 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestPlayCountRankingTier()
{
AddAssert("1 before silver", () => DailyChallengeStatsTooltip.TierForPlayCount(30) == RankingTier.Bronze);
AddAssert("first silver", () => DailyChallengeStatsTooltip.TierForPlayCount(31) == RankingTier.Silver);
AddAssert("1 before silver", () => DailyChallengeStatsTooltip.TierForPlayCount(29) == RankingTier.Bronze);
AddAssert("first silver", () => DailyChallengeStatsTooltip.TierForPlayCount(30) == RankingTier.Silver);
}
}
}
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
@@ -58,6 +59,16 @@ namespace osu.Game.Tests.Visual.Online
return true;
}
if (req is GetUserBeatmapsRequest getUserBeatmapsRequest)
{
getUserBeatmapsRequest.TriggerSuccess(new List<APIBeatmapSet>
{
CreateAPIBeatmapSet(),
CreateAPIBeatmapSet()
});
return true;
}
return false;
};
});
@@ -25,6 +25,7 @@ namespace osu.Game.Tests.Visual.Online
{
protected override bool UseOnlineAPI => false;
private LocalUserStatisticsProvider statisticsProvider = null!;
private UserStatisticsWatcher watcher = null!;
[Resolved]
@@ -107,7 +108,9 @@ namespace osu.Game.Tests.Visual.Online
AddStep("create watcher", () =>
{
Child = watcher = new UserStatisticsWatcher();
Clear();
Add(statisticsProvider = new LocalUserStatisticsProvider());
Add(watcher = new UserStatisticsWatcher(statisticsProvider));
});
}
@@ -123,7 +126,7 @@ namespace osu.Game.Tests.Visual.Online
var ruleset = new OsuRuleset().RulesetInfo;
UserStatisticsUpdate? update = null;
ScoreBasedUserStatisticsUpdate? update = null;
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
feignScoreProcessing(userId, ruleset, 5_000_000);
@@ -146,7 +149,7 @@ namespace osu.Game.Tests.Visual.Online
// note ordering - in this test processing completes *before* the registration is added.
feignScoreProcessing(userId, ruleset, 5_000_000);
UserStatisticsUpdate? update = null;
ScoreBasedUserStatisticsUpdate? update = null;
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
@@ -164,7 +167,7 @@ namespace osu.Game.Tests.Visual.Online
long scoreId = getScoreId();
var ruleset = new OsuRuleset().RulesetInfo;
UserStatisticsUpdate? update = null;
ScoreBasedUserStatisticsUpdate? update = null;
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
feignScoreProcessing(userId, ruleset, 5_000_000);
@@ -191,7 +194,7 @@ namespace osu.Game.Tests.Visual.Online
long scoreId = getScoreId();
var ruleset = new OsuRuleset().RulesetInfo;
UserStatisticsUpdate? update = null;
ScoreBasedUserStatisticsUpdate? update = null;
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
feignScoreProcessing(userId, ruleset, 5_000_000);
@@ -212,7 +215,7 @@ namespace osu.Game.Tests.Visual.Online
long scoreId = getScoreId();
var ruleset = new OsuRuleset().RulesetInfo;
UserStatisticsUpdate? update = null;
ScoreBasedUserStatisticsUpdate? update = null;
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
feignScoreProcessing(userId, ruleset, 5_000_000);
@@ -241,7 +244,7 @@ namespace osu.Game.Tests.Visual.Online
feignScoreProcessing(userId, ruleset, 6_000_000);
UserStatisticsUpdate? update = null;
ScoreBasedUserStatisticsUpdate? update = null;
registerForUpdates(secondScoreId, ruleset, receivedUpdate => update = receivedUpdate);
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, secondScoreId));
@@ -259,15 +262,14 @@ namespace osu.Game.Tests.Visual.Online
var ruleset = new OsuRuleset().RulesetInfo;
UserStatisticsUpdate? update = null;
ScoreBasedUserStatisticsUpdate? update = null;
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
feignScoreProcessing(userId, ruleset, 5_000_000);
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
AddUntilStep("update received", () => update != null);
AddAssert("local user values are correct", () => dummyAPI.LocalUser.Value.Statistics.TotalScore, () => Is.EqualTo(5_000_000));
AddAssert("statistics values are correct", () => dummyAPI.Statistics.Value!.TotalScore, () => Is.EqualTo(5_000_000));
AddAssert("statistics values are correct", () => statisticsProvider.GetStatisticsFor(ruleset)!.TotalScore, () => Is.EqualTo(5_000_000));
}
private int nextUserId = 2000;
@@ -289,7 +291,7 @@ namespace osu.Game.Tests.Visual.Online
});
}
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<UserStatisticsUpdate> onUpdateReady) =>
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<ScoreBasedUserStatisticsUpdate> onUpdateReady) =>
AddStep("register for updates", () =>
{
watcher.RegisterForStatisticsUpdateAfter(
@@ -1,8 +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.
#nullable disable
using System.Linq;
using NUnit.Framework;
using osu.Framework.Bindables;
@@ -21,7 +19,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
private TestLoungeSubScreen loungeScreen;
private TestLoungeSubScreen loungeScreen = null!;
public override void SetUpSteps()
{
@@ -97,7 +95,7 @@ namespace osu.Game.Tests.Visual.Playlists
private partial class TestLoungeSubScreen : PlaylistsLoungeSubScreen
{
public new Bindable<Room> SelectedRoom => base.SelectedRoom;
public new Bindable<Room?> SelectedRoom => base.SelectedRoom;
}
}
}
@@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
SelectedRoom.Value = new Room();
Child = settings = new TestRoomSettings(SelectedRoom.Value)
Child = settings = new TestRoomSettings(SelectedRoom.Value!)
{
RelativeSizeAxes = Axes.Both,
State = { Value = Visibility.Visible }
@@ -45,19 +45,19 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("clear name and beatmap", () =>
{
SelectedRoom.Value.Name = "";
SelectedRoom.Value.Playlist = [];
SelectedRoom.Value!.Name = "";
SelectedRoom.Value!.Playlist = [];
});
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
AddStep("set name", () => SelectedRoom.Value.Name = "Room name");
AddStep("set name", () => SelectedRoom.Value!.Name = "Room name");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
AddStep("set beatmap", () => SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]);
AddStep("set beatmap", () => SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]);
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
AddStep("clear name", () => SelectedRoom.Value.Name = "");
AddStep("clear name", () => SelectedRoom.Value!.Name = "");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
}
@@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
settings.NameField.Current.Value = expected_name;
settings.DurationField.Current.Value = expectedDuration;
SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
RoomManager.CreateRequested = r =>
{
@@ -98,8 +98,8 @@ namespace osu.Game.Tests.Visual.Playlists
{
var beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo;
SelectedRoom.Value.Name = "Test Room";
SelectedRoom.Value.Playlist = [new PlaylistItem(beatmap)];
SelectedRoom.Value!.Name = "Test Room";
SelectedRoom.Value!.Playlist = [new PlaylistItem(beatmap)];
errorMessage = $"{not_found_prefix} {beatmap.OnlineID}";
@@ -107,13 +107,13 @@ namespace osu.Game.Tests.Visual.Playlists
});
AddAssert("error not displayed", () => !settings.ErrorText.IsPresent);
AddAssert("playlist item valid", () => SelectedRoom.Value.Playlist[0].Valid.Value);
AddAssert("playlist item valid", () => SelectedRoom.Value!.Playlist[0].Valid.Value);
AddStep("create room", () => settings.ApplyButton.Action.Invoke());
AddAssert("error displayed", () => settings.ErrorText.IsPresent);
AddAssert("error has custom text", () => settings.ErrorText.Text != errorMessage);
AddAssert("playlist item marked invalid", () => !SelectedRoom.Value.Playlist[0].Valid.Value);
AddAssert("playlist item marked invalid", () => !SelectedRoom.Value!.Playlist[0].Valid.Value);
}
[Test]
@@ -125,8 +125,8 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("setup", () =>
{
SelectedRoom.Value.Name = "Test Room";
SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
SelectedRoom.Value!.Name = "Test Room";
SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
RoomManager.CreateRequested = _ => failText;
});
@@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("create component", () =>
{
Child = new ParticipantsDisplay(SelectedRoom.Value, Direction.Horizontal)
Child = new ParticipantsDisplay(SelectedRoom.Value!, Direction.Horizontal)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("create component", () =>
{
Child = new ParticipantsDisplay(SelectedRoom.Value, Direction.Vertical)
Child = new ParticipantsDisplay(SelectedRoom.Value!, Direction.Vertical)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -1,15 +1,13 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
@@ -34,14 +32,14 @@ namespace osu.Game.Tests.Visual.Playlists
private const int scores_per_result = 10;
private const int real_user_position = 200;
private TestResultsScreen resultsScreen;
private ResultsScreen resultsScreen = null!;
private int lowestScoreId; // Score ID of the lowest score in the list.
private int highestScoreId; // Score ID of the highest score in the list.
private bool requestComplete;
private int totalCount;
private ScoreInfo userScore;
private ScoreInfo userScore = null!;
[SetUpSteps]
public override void SetUpSteps()
@@ -71,11 +69,11 @@ namespace osu.Game.Tests.Visual.Playlists
}
[Test]
public void TestShowWithUserScore()
public void TestShowUserScore()
{
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
createResults(() => userScore);
createResultsWithScore(() => userScore);
waitForDisplay();
AddAssert("user score selected", () => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.OnlineID == userScore.OnlineID).State == PanelState.Expanded);
@@ -84,11 +82,24 @@ namespace osu.Game.Tests.Visual.Playlists
}
[Test]
public void TestShowNullUserScore()
public void TestShowUserBest()
{
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
createUserBestResults();
waitForDisplay();
AddAssert("user score selected", () => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.UserID == userScore.UserID).State == PanelState.Expanded);
AddAssert($"score panel position is {real_user_position}",
() => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.UserID == userScore.UserID).ScorePosition.Value == real_user_position);
}
[Test]
public void TestShowNonUserScores()
{
AddStep("bind user score info handler", () => bindHandler());
createResults();
createUserBestResults();
waitForDisplay();
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
@@ -99,7 +110,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("bind user score info handler", () => bindHandler(true, userScore));
createResults(() => userScore);
createResultsWithScore(() => userScore);
waitForDisplay();
AddAssert("more than 1 panel displayed", () => this.ChildrenOfType<ScorePanel>().Count() > 1);
@@ -107,11 +118,11 @@ namespace osu.Game.Tests.Visual.Playlists
}
[Test]
public void TestShowNullUserScoreWithDelay()
public void TestShowNonUserScoresWithDelay()
{
AddStep("bind delayed handler", () => bindHandler(true));
createResults();
createUserBestResults();
waitForDisplay();
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
@@ -122,7 +133,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("bind delayed handler", () => bindHandler(true));
createResults();
createUserBestResults();
waitForDisplay();
for (int i = 0; i < 2; i++)
@@ -130,13 +141,16 @@ namespace osu.Game.Tests.Visual.Playlists
int beforePanelCount = 0;
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
AddAssert("right loading spinner shown", () =>
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
waitForDisplay();
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
AddAssert("right loading spinner hidden", () =>
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
}
}
@@ -145,29 +159,36 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("bind delayed handler with scores", () => bindHandler(delayed: true));
createResults();
createUserBestResults();
waitForDisplay();
int beforePanelCount = 0;
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
AddAssert("right loading spinner shown", () =>
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
waitForDisplay();
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
AddAssert("right loading spinner hidden", () =>
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
AddStep("bind delayed handler with no scores", () => bindHandler(delayed: true, noScores: true));
AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
AddAssert("right loading spinner shown", () =>
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
waitForDisplay();
AddAssert("count not increased", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount);
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
AddAssert("right loading spinner hidden", () =>
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
AddAssert("no placeholders shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.Zero);
}
@@ -176,7 +197,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
createResults(() => userScore);
createResultsWithScore(() => userScore);
waitForDisplay();
AddStep("bind delayed handler", () => bindHandler(true));
@@ -186,30 +207,36 @@ namespace osu.Game.Tests.Visual.Playlists
int beforePanelCount = 0;
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
AddStep("scroll to left", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToStart(false));
AddStep("scroll to left", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToStart(false));
AddAssert("left loading spinner shown", () =>
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreLeft).State.Value == Visibility.Visible);
AddAssert("left loading spinner shown", () => resultsScreen.LeftSpinner.State.Value == Visibility.Visible);
waitForDisplay();
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
AddAssert("left loading spinner hidden", () => resultsScreen.LeftSpinner.State.Value == Visibility.Hidden);
AddAssert("left loading spinner hidden", () =>
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreLeft).State.Value == Visibility.Hidden);
}
}
/// <summary>
/// Shows the <see cref="TestUserBestResultsScreen"/> with no scores provided by the API.
/// </summary>
[Test]
public void TestShowWithNoScores()
public void TestShowUserBestWithNoScoresPresent()
{
AddStep("bind user score info handler", () => bindHandler(noScores: true));
createResults();
AddAssert("no scores visible", () => !resultsScreen.ScorePanelList.GetScorePanels().Any());
createUserBestResults();
AddAssert("no scores visible", () => !resultsScreen.ChildrenOfType<ScorePanelList>().Single().GetScorePanels().Any());
AddAssert("placeholder shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.EqualTo(1));
}
private void createResults(Func<ScoreInfo> getScore = null)
private void createResultsWithScore(Func<ScoreInfo> getScore)
{
AddStep("load results", () =>
{
LoadScreen(resultsScreen = new TestResultsScreen(getScore?.Invoke(), 1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
LoadScreen(resultsScreen = new TestScoreResultsScreen(getScore(), 1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}));
@@ -218,22 +245,36 @@ namespace osu.Game.Tests.Visual.Playlists
AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded);
}
private void createUserBestResults()
{
AddStep("load results", () =>
{
LoadScreen(resultsScreen = new TestUserBestResultsScreen(1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}, 2));
});
AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded);
}
private void waitForDisplay()
{
AddUntilStep("wait for scores loaded", () =>
requestComplete
// request handler may need to fire more than once to get scores.
&& totalCount > 0
&& resultsScreen.ScorePanelList.GetScorePanels().Count() == totalCount
&& resultsScreen.ScorePanelList.AllPanelsVisible);
&& resultsScreen.ChildrenOfType<ScorePanelList>().Single().GetScorePanels().Count() == totalCount
&& resultsScreen.ChildrenOfType<ScorePanelList>().Single().AllPanelsVisible);
AddWaitStep("wait for display", 5);
}
private void bindHandler(bool delayed = false, ScoreInfo userScore = null, bool failRequests = false, bool noScores = false) => ((DummyAPIAccess)API).HandleRequest = request =>
private void bindHandler(bool delayed = false, ScoreInfo? userScore = null, bool failRequests = false, bool noScores = false) => ((DummyAPIAccess)API).HandleRequest = request =>
{
// pre-check for requests we should be handling (as they are scheduled below).
switch (request)
{
case ShowPlaylistScoreRequest:
case ShowPlaylistUserScoreRequest:
case IndexPlaylistScoresRequest:
break;
@@ -256,7 +297,7 @@ namespace osu.Game.Tests.Visual.Playlists
switch (request)
{
case ShowPlaylistUserScoreRequest s:
case ShowPlaylistScoreRequest s:
if (userScore == null)
triggerFail(s);
else
@@ -264,6 +305,14 @@ namespace osu.Game.Tests.Visual.Playlists
break;
case ShowPlaylistUserScoreRequest u:
if (userScore == null)
triggerFail(u);
else
triggerSuccess(u, createUserResponse(userScore));
break;
case IndexPlaylistScoresRequest i:
triggerSuccess(i, createIndexResponse(i, noScores));
break;
@@ -286,7 +335,7 @@ namespace osu.Game.Tests.Visual.Playlists
req.TriggerFailure(new WebException("Failed."));
}
private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore)
private MultiplayerScore createUserResponse(ScoreInfo userScore)
{
var multiplayerUserScore = new MultiplayerScore
{
@@ -317,7 +366,7 @@ namespace osu.Game.Tests.Visual.Playlists
MaxCombo = userScore.MaxCombo,
User = new APIUser
{
Id = 2,
Id = 2 + i,
Username = $"peppy{i}",
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
},
@@ -332,7 +381,7 @@ namespace osu.Game.Tests.Visual.Playlists
MaxCombo = userScore.MaxCombo,
User = new APIUser
{
Id = 2,
Id = 2 + i,
Username = $"peppy{i}",
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
},
@@ -366,7 +415,7 @@ namespace osu.Game.Tests.Visual.Playlists
MaxCombo = 1000,
User = new APIUser
{
Id = 2,
Id = 2 + i,
Username = $"peppy{i}",
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
},
@@ -413,18 +462,22 @@ namespace osu.Game.Tests.Visual.Playlists
};
}
private partial class TestResultsScreen : PlaylistItemUserResultsScreen
private partial class TestScoreResultsScreen : PlaylistItemScoreResultsScreen
{
public new LoadingSpinner LeftSpinner => base.LeftSpinner;
public new LoadingSpinner CentreSpinner => base.CentreSpinner;
public new LoadingSpinner RightSpinner => base.RightSpinner;
public new ScorePanelList ScorePanelList => base.ScorePanelList;
public TestResultsScreen([CanBeNull] ScoreInfo score, int roomId, PlaylistItem playlistItem)
public TestScoreResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem)
: base(score, roomId, playlistItem)
{
AllowRetry = true;
}
}
private partial class TestUserBestResultsScreen : PlaylistItemUserBestResultsScreen
{
public TestUserBestResultsScreen(int roomId, PlaylistItem playlistItem, int userId)
: base(roomId, playlistItem, userId)
{
AllowRetry = true;
}
}
}
}
@@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Playlists
importBeatmap();
AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom.Value)));
AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom.Value!)));
AddUntilStep("wait for load", () => match.IsCurrentScreen());
}
@@ -119,7 +119,7 @@ namespace osu.Game.Tests.Visual.Playlists
];
});
AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value.Playlist[0]);
AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value!.Playlist[0]);
}
[Test]
@@ -199,7 +199,7 @@ namespace osu.Game.Tests.Visual.Playlists
private void setupAndCreateRoom(Action<Room> room)
{
AddStep("setup room", () => room(SelectedRoom.Value));
AddStep("setup room", () => room(SelectedRoom.Value!));
AddStep("click create button", () =>
{
@@ -218,7 +218,7 @@ namespace osu.Game.Tests.Visual.Playlists
private partial class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen
{
public new Bindable<PlaylistItem> SelectedItem => base.SelectedItem;
public new Bindable<PlaylistItem?> SelectedItem => base.SelectedItem;
public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap;
@@ -2,20 +2,67 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Extensions;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Online.Rooms.RoomStatuses;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Tests.Resources;
using osu.Game.Tests.Visual.OnlinePlay;
namespace osu.Game.Tests.Visual.Playlists
{
public partial class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene
{
private const double track_length = 10000;
[Resolved]
private IAPIProvider api { get; set; } = null!;
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
private BeatmapManager beatmaps = null!;
private RulesetStore rulesets = null!;
private BeatmapSetInfo? importedSet;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
Dependencies.Cache(new ScoreManager(rulesets, () => beatmaps, LocalStorage, Realm, API));
Dependencies.Cache(Realm);
beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();
Realm.Write(r =>
{
foreach (var set in r.All<BeatmapSetInfo>())
{
foreach (var b in set.Beatmaps)
{
// These will all have a virtual track length of 1000, see WorkingBeatmap.GetVirtualTrack().
b.Length = track_length - 1000;
}
}
});
importedSet = beatmaps.GetAllUsableBeatmapSets().First();
}
[Test]
public void TestStatusUpdateOnEnter()
{
@@ -37,5 +84,66 @@ namespace osu.Game.Tests.Visual.Playlists
AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
AddAssert("status is still ended", () => roomScreen.Room.Status, Is.TypeOf<RoomStatusEnded>);
}
[Test]
public void TestCloseButtonGoesAwayAfterGracePeriod()
{
Room room = null!;
PlaylistsRoomSubScreen roomScreen = null!;
AddStep("create room", () =>
{
RoomManager.AddRoom(room = new Room
{
Name = @"Test Room",
Host = api.LocalUser.Value,
Category = RoomCategory.Normal,
StartDate = DateTimeOffset.Now.AddMinutes(-5).AddSeconds(3),
EndDate = DateTimeOffset.Now.AddMinutes(30)
});
});
AddStep("push screen", () => LoadScreen(roomScreen = new PlaylistsRoomSubScreen(room)));
AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
AddAssert("close button present", () => roomScreen.ChildrenOfType<DangerousRoundedButton>().Any());
AddUntilStep("wait for close button to disappear", () => !roomScreen.ChildrenOfType<DangerousRoundedButton>().Any());
}
[TestCase(120_000, true)] // Definitely enough time.
[TestCase(45_000, true)] // Enough time.
[TestCase(35_000, false)] // Not enough time to complete beatmap after lenience.
[TestCase(20_000, false)] // Not enough time.
[TestCase(5_000, false)] // Not enough time to complete beatmap before lenience.
[TestCase(37_500, true, 2)] // Enough time to complete beatmap after mods are applied.
public void TestReadyButtonEnablementPeriod(int offsetMs, bool enabled, double rate = 1)
{
Room room = null!;
PlaylistsRoomSubScreen roomScreen = null!;
AddStep("create room", () =>
{
RoomManager.AddRoom(room = new Room
{
Name = @"Test Room",
Host = api.LocalUser.Value,
Category = RoomCategory.Normal,
StartDate = DateTimeOffset.Now,
EndDate = DateTimeOffset.Now.AddMilliseconds(offsetMs),
Playlist =
[
new PlaylistItem(importedSet!.Beatmaps[0])
{
RequiredMods = rate == 1
? []
: [new APIMod(new OsuModDoubleTime { SpeedChange = { Value = rate } })]
}
]
});
});
AddStep("push screen", () => LoadScreen(roomScreen = new PlaylistsRoomSubScreen(room)));
AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
AddUntilStep("ready button enabled", () => roomScreen.ChildrenOfType<PlaylistsReadyButton>().SingleOrDefault()?.Enabled.Value, () => Is.EqualTo(enabled));
}
}
}
@@ -112,6 +112,6 @@ namespace osu.Game.Tests.Visual.Ranking
});
private void displayUpdate(UserStatistics before, UserStatistics after) =>
AddStep("display update", () => overallRanking.StatisticsUpdate.Value = new UserStatisticsUpdate(new ScoreInfo(), before, after));
AddStep("display update", () => overallRanking.StatisticsUpdate.Value = new ScoreBasedUserStatisticsUpdate(new ScoreInfo(), before, after));
}
}
@@ -91,12 +91,12 @@ namespace osu.Game.Tests.Visual.Ranking
UserStatisticsWatcher userStatisticsWatcher = null!;
ScoreInfo score = null!;
AddStep("create user statistics watcher", () => Add(userStatisticsWatcher = new UserStatisticsWatcher()));
AddStep("create user statistics watcher", () => Add(userStatisticsWatcher = new UserStatisticsWatcher(new LocalUserStatisticsProvider())));
AddStep("set user statistics update", () =>
{
score = TestResources.CreateTestScoreInfo();
score.OnlineID = 1234;
((Bindable<UserStatisticsUpdate>)userStatisticsWatcher.LatestUpdate).Value = new UserStatisticsUpdate(score,
((Bindable<ScoreBasedUserStatisticsUpdate>)userStatisticsWatcher.LatestUpdate).Value = new ScoreBasedUserStatisticsUpdate(score,
new UserStatistics
{
Level = new UserStatistics.LevelInfo
@@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.Ranking
Score = { Value = score },
DisplayedUserStatisticsUpdate =
{
Value = new UserStatisticsUpdate(score, new UserStatistics
Value = new ScoreBasedUserStatisticsUpdate(score, new UserStatistics
{
Level = new UserStatistics.LevelInfo
{
@@ -8,14 +8,14 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
@@ -28,25 +28,31 @@ namespace osu.Game.Tests.Visual.SongSelect
{
public partial class TestSceneBeatmapRecommendations : OsuGameTestScene
{
[Resolved]
private IRulesetStore rulesetStore { get; set; }
[SetUpSteps]
public override void SetUpSteps()
{
AddStep("populate ruleset statistics", () =>
{
Dictionary<string, UserStatistics> rulesetStatistics = new Dictionary<string, UserStatistics>();
rulesetStore.AvailableRulesets.Where(ruleset => ruleset.IsLegacyRuleset()).ForEach(rulesetInfo =>
((DummyAPIAccess)API).HandleRequest = r =>
{
rulesetStatistics[rulesetInfo.ShortName] = new UserStatistics
switch (r)
{
PP = getNecessaryPP(rulesetInfo.OnlineID)
};
});
case GetUserRequest userRequest:
userRequest.TriggerSuccess(new APIUser
{
Id = 99,
Statistics = new UserStatistics
{
PP = getNecessaryPP(userRequest.Ruleset?.OnlineID ?? 0)
}
});
API.LocalUser.Value.RulesetsStatistics = rulesetStatistics;
return true;
default:
return false;
}
};
});
decimal getNecessaryPP(int? rulesetID)
@@ -101,7 +101,16 @@ namespace osu.Game.Tests.Visual.UserInterface
}
}
}
}
},
}
},
new OsuMenuItem(@"Another nested option")
{
Items = new MenuItem[]
{
new OsuMenuItem(@"Sub-One"),
new OsuMenuItem(@"Sub-Two"),
new OsuMenuItem(@"Sub-Three"),
}
},
new OsuMenuItem(@"Choose me please"),
@@ -151,7 +151,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("click delete option", () =>
{
InputManager.MoveMouseTo(contextMenuContainer.ChildrenOfType<DrawableOsuMenuItem>().First(i => i.Item.Text.Value.ToString().ToLowerInvariant() == "delete"));
InputManager.MoveMouseTo(contextMenuContainer.ChildrenOfType<DrawableOsuMenuItem>().First(i => string.Equals(i.Item.Text.Value.ToString(), "delete", System.StringComparison.OrdinalIgnoreCase)));
InputManager.Click(MouseButton.Left);
});
+3 -3
View File
@@ -12,9 +12,9 @@
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Label="Code Analysis">
<CodeAnalysisRuleSet>tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup Label="Code Analysis">
<GlobalAnalyzerConfigFiles Include="CodeAnalysis.tests.globalconfig" />
</ItemGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="osu! Rule Set" Description=" " ToolsVersion="16.0">
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA2007" Action="None" />
</Rules>
</RuleSet>

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