Merge branch 'master' into back-button-part-2

This commit is contained in:
Dean Herbert
2019-09-03 19:44:30 +09:00
committed by GitHub
247 changed files with 4468 additions and 1494 deletions

View File

@ -16,15 +16,13 @@ using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
[Cached(Type = typeof(IPlacementHandler))]
public class TestSceneHitObjectComposer : OsuTestScene, IPlacementHandler
public class TestSceneHitObjectComposer : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -39,8 +37,6 @@ namespace osu.Game.Tests.Visual.Editor
typeof(HitCirclePlacementBlueprint),
};
private HitObjectComposer composer;
[BackgroundDependencyLoader]
private void load()
{
@ -67,15 +63,7 @@ namespace osu.Game.Tests.Visual.Editor
Dependencies.CacheAs<IAdjustableClock>(clock);
Dependencies.CacheAs<IFrameBasedClock>(clock);
Child = composer = new OsuHitObjectComposer(new OsuRuleset());
Child = new OsuHitObjectComposer(new OsuRuleset());
}
public void BeginPlacement(HitObject hitObject)
{
}
public void EndPlacement(HitObject hitObject) => composer.Add(hitObject);
public void Delete(HitObject hitObject) => composer.Remove(hitObject);
}
}

View File

@ -0,0 +1,119 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Catch.Objects;
using System;
using System.Collections.Generic;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Framework.MathUtils;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneBarHitErrorMeter : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(HitErrorMeter),
};
private HitErrorMeter meter;
private HitErrorMeter meter2;
private HitWindows hitWindows;
public TestSceneBarHitErrorMeter()
{
recreateDisplay(new OsuHitWindows(), 5);
AddRepeatStep("New random judgement", () => newJudgement(), 40);
AddRepeatStep("New max negative", () => newJudgement(-hitWindows.HalfWindowFor(HitResult.Meh)), 20);
AddRepeatStep("New max positive", () => newJudgement(hitWindows.HalfWindowFor(HitResult.Meh)), 20);
AddStep("New fixed judgement (50ms)", () => newJudgement(50));
}
[Test]
public void TestOsu()
{
AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1));
AddStep("OD 10", () => recreateDisplay(new OsuHitWindows(), 10));
}
[Test]
public void TestTaiko()
{
AddStep("OD 1", () => recreateDisplay(new TaikoHitWindows(), 1));
AddStep("OD 10", () => recreateDisplay(new TaikoHitWindows(), 10));
}
[Test]
public void TestMania()
{
AddStep("OD 1", () => recreateDisplay(new ManiaHitWindows(), 1));
AddStep("OD 10", () => recreateDisplay(new ManiaHitWindows(), 10));
}
[Test]
public void TestCatch()
{
AddStep("OD 1", () => recreateDisplay(new CatchHitWindows(), 1));
AddStep("OD 10", () => recreateDisplay(new CatchHitWindows(), 10));
}
private void recreateDisplay(HitWindows hitWindows, float overallDifficulty)
{
this.hitWindows = hitWindows;
hitWindows?.SetDifficulty(overallDifficulty);
Clear();
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
Children = new[]
{
new SpriteText { Text = $@"Great: {hitWindows?.Great}" },
new SpriteText { Text = $@"Good: {hitWindows?.Good}" },
new SpriteText { Text = $@"Meh: {hitWindows?.Meh}" },
}
});
Add(meter = new BarHitErrorMeter(hitWindows, true)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
});
Add(meter2 = new BarHitErrorMeter(hitWindows, false)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
});
}
private void newJudgement(double offset = 0)
{
var judgement = new JudgementResult(new HitObject(), new Judgement())
{
TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset,
Type = HitResult.Perfect,
};
meter.OnNewJudgement(judgement);
meter2.OnNewJudgement(judgement);
}
}
}

View File

@ -0,0 +1,305 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneDrawableScrollingRuleset : OsuTestScene
{
/// <summary>
/// The amount of time visible by the "view window" of the playfield.
/// All hitobjects added through <see cref="createBeatmap"/> are spaced apart by this value, such that for a beat length of 1000,
/// there will be at most 2 hitobjects visible in the "view window".
/// </summary>
private const double time_range = 1000;
private readonly ManualClock testClock = new ManualClock();
private TestDrawableScrollingRuleset drawableRuleset;
[SetUp]
public void Setup() => Schedule(() => testClock.CurrentTime = 0);
[Test]
public void TestRelativeBeatLengthScaleSingleTimingPoint()
{
var beatmap = createBeatmap(new TimingControlPoint { BeatLength = time_range / 2 });
createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true);
assertPosition(0, 0f);
// The single timing point is 1x speed relative to itself, such that the hitobject occurring time_range milliseconds later should appear
// at the bottom of the view window regardless of the timing point's beat length
assertPosition(1, 1f);
}
[Test]
public void TestRelativeBeatLengthScaleTimingPointBeyondEndDoesNotBecomeDominant()
{
var beatmap = createBeatmap(
new TimingControlPoint { BeatLength = time_range / 2 },
new TimingControlPoint { Time = 12000, BeatLength = time_range },
new TimingControlPoint { Time = 100000, BeatLength = time_range });
createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true);
assertPosition(0, 0f);
assertPosition(1, 1f);
}
[Test]
public void TestRelativeBeatLengthScaleFromSecondTimingPoint()
{
var beatmap = createBeatmap(
new TimingControlPoint { BeatLength = time_range },
new TimingControlPoint { Time = 3 * time_range, BeatLength = time_range / 2 });
createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true);
// The first timing point should have a relative velocity of 2
assertPosition(0, 0f);
assertPosition(1, 0.5f);
assertPosition(2, 1f);
// Move to the second timing point
setTime(3 * time_range);
assertPosition(3, 0f);
// As above, this is the timing point that is 1x speed relative to itself, so the hitobject occurring time_range milliseconds later should be at the bottom of the view window
assertPosition(4, 1f);
}
[Test]
public void TestNonRelativeScale()
{
var beatmap = createBeatmap(
new TimingControlPoint { BeatLength = time_range },
new TimingControlPoint { Time = 3 * time_range, BeatLength = time_range / 2 });
createTest(beatmap);
assertPosition(0, 0f);
assertPosition(1, 1);
// Move to the second timing point
setTime(3 * time_range);
assertPosition(3, 0f);
// For a beat length of 500, the view window of this timing point is elongated 2x (1000 / 500), such that the second hitobject is two TimeRanges away (offscreen)
// To bring it on-screen, half TimeRange is added to the current time, bringing the second half of the view window into view, and the hitobject should appear at the bottom
setTime(3 * time_range + time_range / 2);
assertPosition(4, 1f);
}
private void assertPosition(int index, float relativeY) => AddAssert($"hitobject {index} at {relativeY}",
() => Precision.AlmostEquals(drawableRuleset.Playfield.AllHitObjects.ElementAt(index).DrawPosition.Y, drawableRuleset.Playfield.HitObjectContainer.DrawHeight * relativeY));
private void setTime(double time)
{
AddStep($"set time = {time}", () => testClock.CurrentTime = time);
}
/// <summary>
/// Creates an <see cref="IBeatmap"/>, containing 10 hitobjects and user-provided timing points.
/// The hitobjects are spaced <see cref="time_range"/> milliseconds apart.
/// </summary>
/// <param name="timingControlPoints">The timing points to add to the beatmap.</param>
/// <returns>The <see cref="IBeatmap"/>.</returns>
private IBeatmap createBeatmap(params TimingControlPoint[] timingControlPoints)
{
var beatmap = new Beatmap<HitObject> { BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } };
beatmap.ControlPointInfo.TimingPoints.AddRange(timingControlPoints);
for (int i = 0; i < 10; i++)
beatmap.HitObjects.Add(new HitObject { StartTime = i * time_range });
return beatmap;
}
private void createTest(IBeatmap beatmap, Action<TestDrawableScrollingRuleset> overrideAction = null) => AddStep("create test", () =>
{
var ruleset = new TestScrollingRuleset();
drawableRuleset = (TestDrawableScrollingRuleset)ruleset.CreateDrawableRulesetWith(CreateWorkingBeatmap(beatmap), Array.Empty<Mod>());
drawableRuleset.FrameStablePlayback = false;
overrideAction?.Invoke(drawableRuleset);
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Height = 0.75f,
Width = 400,
Masking = true,
Clock = new FramedClock(testClock),
Child = drawableRuleset
};
});
#region Ruleset
private class TestScrollingRuleset : Ruleset
{
public TestScrollingRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{
}
public override IEnumerable<Mod> GetModsFor(ModType type) => throw new NotImplementedException();
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new TestDrawableScrollingRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException();
public override string Description { get; } = string.Empty;
public override string ShortName { get; } = string.Empty;
}
private class TestDrawableScrollingRuleset : DrawableScrollingRuleset<TestHitObject>
{
public bool RelativeScaleBeatLengthsOverride { get; set; }
protected override bool RelativeScaleBeatLengths => RelativeScaleBeatLengthsOverride;
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping;
public TestDrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap, mods)
{
TimeRange.Value = time_range;
}
public override DrawableHitObject<TestHitObject> CreateDrawableRepresentation(TestHitObject h) => new DrawableTestHitObject(h);
protected override PassThroughInputManager CreateInputManager() => new PassThroughInputManager();
protected override Playfield CreatePlayfield() => new TestPlayfield();
}
private class TestPlayfield : ScrollingPlayfield
{
public TestPlayfield()
{
AddInternal(new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 150 },
Children = new Drawable[]
{
new Box
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = 2,
Colour = Color4.Green
},
HitObjectContainer
}
}
}
});
}
}
private class TestBeatmapConverter : BeatmapConverter<TestHitObject>
{
public TestBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
{
}
protected override IEnumerable<Type> ValidConversionTypes => new[] { typeof(HitObject) };
protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap)
{
yield return new TestHitObject
{
StartTime = original.StartTime,
EndTime = (original as IHasEndTime)?.EndTime ?? (original.StartTime + 100)
};
}
}
#endregion
#region HitObject
private class TestHitObject : HitObject, IHasEndTime
{
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
}
private class DrawableTestHitObject : DrawableHitObject<TestHitObject>
{
public DrawableTestHitObject(TestHitObject hitObject)
: base(hitObject)
{
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
Size = new Vector2(100, 25);
AddRangeInternal(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.LightPink
},
new Box
{
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 2,
Colour = Color4.Red
}
});
}
}
#endregion
}
}

View File

@ -200,10 +200,6 @@ namespace osu.Game.Tests.Visual.Gameplay
break;
}
}
protected override void UpdateState(ArmedState state)
{
}
}
private class TestDrawableHitObject : DrawableHitObject<HitObject>
@ -216,10 +212,6 @@ namespace osu.Game.Tests.Visual.Gameplay
AddInternal(new Box { Size = new Vector2(75) });
}
protected override void UpdateState(ArmedState state)
{
}
}
}
}

View File

@ -10,6 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Skinning;
@ -27,7 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("setup layout larger source", () =>
{
Child = new LocalSkinOverrideContainer(new SizedSource(50))
Child = new SkinProvidingContainer(new SizedSource(50))
{
RelativeSizeAxes = Axes.Both,
Child = fill = new FillFlowContainer<ExposedSkinnableDrawable>
@ -59,7 +60,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("setup layout larger source", () =>
{
Child = new LocalSkinOverrideContainer(new SizedSource(30))
Child = new SkinProvidingContainer(new SizedSource(30))
{
RelativeSizeAxes = Axes.Both,
Child = fill = new FillFlowContainer<ExposedSkinnableDrawable>
@ -95,7 +96,7 @@ namespace osu.Game.Tests.Visual.Gameplay
Child = new SkinSourceContainer
{
RelativeSizeAxes = Axes.Both,
Child = new LocalSkinOverrideContainer(secondarySource)
Child = new SkinProvidingContainer(secondarySource)
{
RelativeSizeAxes = Axes.Both,
Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)
@ -120,7 +121,7 @@ namespace osu.Game.Tests.Visual.Gameplay
Child = new SkinSourceContainer
{
RelativeSizeAxes = Axes.Both,
Child = target = new LocalSkinOverrideContainer(secondarySource)
Child = target = new SkinProvidingContainer(secondarySource)
{
RelativeSizeAxes = Axes.Both,
}
@ -136,8 +137,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public new Drawable Drawable => base.Drawable;
public ExposedSkinnableDrawable(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(name, defaultImplementation, allowFallback, confineMode)
public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode)
{
}
}
@ -205,8 +206,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public new Drawable Drawable => base.Drawable;
public int SkinChangedCount { get; private set; }
public SkinConsumer(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null)
: base(name, defaultImplementation, allowFallback)
public SkinConsumer(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null)
: base(new TestSkinComponent(name), defaultImplementation, allowFallback)
{
}
@ -242,8 +243,8 @@ namespace osu.Game.Tests.Visual.Gameplay
this.size = size;
}
public Drawable GetDrawableComponent(string componentName) =>
componentName == "available"
public Drawable GetDrawableComponent(ISkinComponent componentName) =>
componentName.LookupName == "available"
? new DrawWidthBox
{
Colour = Color4.Yellow,
@ -253,31 +254,45 @@ namespace osu.Game.Tests.Visual.Gameplay
public Texture GetTexture(string componentName) => throw new NotImplementedException();
public SampleChannel GetSample(string sampleName) => throw new NotImplementedException();
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
}
private class SecondarySource : ISkin
{
public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox();
public Drawable GetDrawableComponent(ISkinComponent componentName) => new SecondarySourceBox();
public Texture GetTexture(string componentName) => throw new NotImplementedException();
public SampleChannel GetSample(string sampleName) => throw new NotImplementedException();
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
}
private class SkinSourceContainer : Container, ISkin
{
public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox();
public Drawable GetDrawableComponent(ISkinComponent componentName) => new BaseSourceBox();
public Texture GetTexture(string componentName) => throw new NotImplementedException();
public SampleChannel GetSample(string sampleName) => throw new NotImplementedException();
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
}
private class TestSkinComponent : ISkinComponent
{
private readonly string name;
public TestSkinComponent(string name)
{
this.name = name;
}
public string ComponentGroup => string.Empty;
public string LookupName => name;
}
}
}

View File

@ -135,6 +135,9 @@ namespace osu.Game.Tests.Visual.Online
});
downloadAssert(true);
AddStep("show many difficulties", () => overlay.ShowBeatmapSet(createManyDifficultiesBeatmapSet()));
downloadAssert(true);
}
[Test]
@ -173,6 +176,8 @@ namespace osu.Game.Tests.Visual.Online
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
Language = new BeatmapSetOnlineLanguage { Id = 3, Name = "English" },
Genre = new BeatmapSetOnlineGenre { Id = 4, Name = "Rock" },
},
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
Beatmaps = new List<BeatmapInfo>
@ -222,6 +227,56 @@ namespace osu.Game.Tests.Visual.Online
AddStep(@"show without reload", overlay.Show);
}
private BeatmapSetInfo createManyDifficultiesBeatmapSet()
{
var beatmaps = new List<BeatmapInfo>();
for (int i = 1; i < 41; i++)
{
beatmaps.Add(new BeatmapInfo
{
OnlineBeatmapID = i * 10,
Version = $"Test #{i}",
Ruleset = Ruleset.Value,
StarDifficulty = 2 + i * 0.1,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 3.5f,
},
OnlineInfo = new BeatmapOnlineInfo(),
Metrics = new BeatmapMetrics
{
Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(),
},
});
}
return new BeatmapSetInfo
{
OnlineBeatmapSetID = 123,
Metadata = new BeatmapMetadata
{
Title = @"many difficulties beatmap",
Artist = @"none",
Author = new User
{
Username = @"BanchoBot",
Id = 3,
},
},
OnlineInfo = new BeatmapSetOnlineInfo
{
Preview = @"https://b.ppy.sh/preview/123.mp3",
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
},
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
Beatmaps = beatmaps,
};
}
private void downloadAssert(bool shown)
{
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.DownloadButtonsVisible == shown);

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Users;
using osuTK;
@ -24,7 +23,7 @@ namespace osu.Game.Tests.Visual.Online
typeof(IconPill)
};
private BeatmapSetInfo getUndownloadableBeatmapSet(RulesetInfo ruleset) => new BeatmapSetInfo
private BeatmapSetInfo getUndownloadableBeatmapSet() => new BeatmapSetInfo
{
OnlineBeatmapSetID = 123,
Metadata = new BeatmapMetadata
@ -56,23 +55,62 @@ namespace osu.Game.Tests.Visual.Online
{
new BeatmapInfo
{
Ruleset = ruleset,
Ruleset = Ruleset.Value,
Version = "Test",
StarDifficulty = 6.42,
}
}
};
[BackgroundDependencyLoader]
private void load()
private BeatmapSetInfo getManyDifficultiesBeatmapSet(RulesetStore rulesets)
{
var ruleset = new OsuRuleset().RulesetInfo;
var beatmaps = new List<BeatmapInfo>();
var normal = CreateWorkingBeatmap(ruleset).BeatmapSetInfo;
for (int i = 0; i < 100; i++)
{
beatmaps.Add(new BeatmapInfo
{
Ruleset = rulesets.GetRuleset(i % 4),
StarDifficulty = 2 + i % 4 * 2,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 3.5f,
}
});
}
return new BeatmapSetInfo
{
OnlineBeatmapSetID = 1,
Metadata = new BeatmapMetadata
{
Title = "many difficulties beatmap",
Artist = "test",
Author = new User
{
Username = "BanchoBot",
Id = 3,
}
},
OnlineInfo = new BeatmapSetOnlineInfo
{
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
},
Beatmaps = beatmaps,
};
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
var normal = CreateWorkingBeatmap(Ruleset.Value).BeatmapSetInfo;
normal.OnlineInfo.HasVideo = true;
normal.OnlineInfo.HasStoryboard = true;
var undownloadable = getUndownloadableBeatmapSet(ruleset);
var undownloadable = getUndownloadableBeatmapSet();
var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets);
Child = new BasicScrollContainer
{
@ -81,15 +119,17 @@ namespace osu.Game.Tests.Visual.Online
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Direction = FillDirection.Full,
Padding = new MarginPadding(20),
Spacing = new Vector2(0, 20),
Spacing = new Vector2(5, 20),
Children = new Drawable[]
{
new DirectGridPanel(normal),
new DirectListPanel(normal),
new DirectGridPanel(undownloadable),
new DirectGridPanel(manyDifficulties),
new DirectListPanel(normal),
new DirectListPanel(undownloadable),
new DirectListPanel(manyDifficulties),
},
},
};

View File

@ -0,0 +1,246 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Overlays.Profile.Sections.Kudosu;
using System.Collections.Generic;
using System;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests.Responses;
using osu.Framework.Extensions.IEnumerableExtensions;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneKudosuHistory : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(DrawableKudosuHistoryItem),
};
private readonly Box background;
public TestSceneKudosuHistory()
{
FillFlowContainer<DrawableKudosuHistoryItem> content;
AddRange(new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
content = new FillFlowContainer<DrawableKudosuHistoryItem>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Width = 0.7f,
AutoSizeAxes = Axes.Y,
}
});
items.ForEach(t => content.Add(new DrawableKudosuHistoryItem(t)));
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.GreySeafoam;
}
private readonly IEnumerable<APIKudosuHistory> items = new[]
{
new APIKudosuHistory
{
Amount = 10,
CreatedAt = new DateTimeOffset(new DateTime(2011, 11, 11)),
Source = KudosuSource.DenyKudosu,
Action = KudosuAction.Reset,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 1",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username1",
Url = @"https://osu.ppy.sh/u/1234"
}
},
new APIKudosuHistory
{
Amount = 5,
CreatedAt = new DateTimeOffset(new DateTime(2012, 10, 11)),
Source = KudosuSource.Forum,
Action = KudosuAction.Give,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 2",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username2",
Url = @"https://osu.ppy.sh/u/1234"
}
},
new APIKudosuHistory
{
Amount = 8,
CreatedAt = new DateTimeOffset(new DateTime(2013, 9, 11)),
Source = KudosuSource.Forum,
Action = KudosuAction.Reset,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 3",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username3",
Url = @"https://osu.ppy.sh/u/1234"
}
},
new APIKudosuHistory
{
Amount = 7,
CreatedAt = new DateTimeOffset(new DateTime(2014, 8, 11)),
Source = KudosuSource.Forum,
Action = KudosuAction.Revoke,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 4",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username4",
Url = @"https://osu.ppy.sh/u/1234"
}
},
new APIKudosuHistory
{
Amount = 100,
CreatedAt = new DateTimeOffset(new DateTime(2015, 7, 11)),
Source = KudosuSource.Vote,
Action = KudosuAction.Give,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 5",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username5",
Url = @"https://osu.ppy.sh/u/1234"
}
},
new APIKudosuHistory
{
Amount = 20,
CreatedAt = new DateTimeOffset(new DateTime(2016, 6, 11)),
Source = KudosuSource.Vote,
Action = KudosuAction.Reset,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 6",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username6",
Url = @"https://osu.ppy.sh/u/1234"
}
},
new APIKudosuHistory
{
Amount = 11,
CreatedAt = new DateTimeOffset(new DateTime(2016, 6, 11)),
Source = KudosuSource.AllowKudosu,
Action = KudosuAction.Give,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 7",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username7",
Url = @"https://osu.ppy.sh/u/1234"
}
},
new APIKudosuHistory
{
Amount = 24,
CreatedAt = new DateTimeOffset(new DateTime(2014, 6, 11)),
Source = KudosuSource.Delete,
Action = KudosuAction.Reset,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 8",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username8",
Url = @"https://osu.ppy.sh/u/1234"
}
},
new APIKudosuHistory
{
Amount = 12,
CreatedAt = new DateTimeOffset(new DateTime(2016, 6, 11)),
Source = KudosuSource.Restore,
Action = KudosuAction.Give,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 9",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username9",
Url = @"https://osu.ppy.sh/u/1234"
}
},
new APIKudosuHistory
{
Amount = 2,
CreatedAt = new DateTimeOffset(new DateTime(2012, 6, 11)),
Source = KudosuSource.Recalculate,
Action = KudosuAction.Give,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 10",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username10",
Url = @"https://osu.ppy.sh/u/1234"
}
},
new APIKudosuHistory
{
Amount = 32,
CreatedAt = new DateTimeOffset(new DateTime(2019, 8, 11)),
Source = KudosuSource.Recalculate,
Action = KudosuAction.Reset,
Post = new APIKudosuHistory.ModdingPost
{
Title = @"Random post 11",
Url = @"https://osu.ppy.sh/b/1234",
},
Giver = new APIKudosuHistory.KudosuGiver
{
Username = @"Username11",
Url = @"https://osu.ppy.sh/u/1234"
}
}
};
}
}

View File

@ -516,6 +516,7 @@ namespace osu.Game.Tests.Visual.SongSelect
OnlineBeatmapID = b * 10,
Path = $"extra{b}.osu",
Version = $"Extra {b}",
Ruleset = rulesets.GetRuleset((b - 1) % 4),
StarDifficulty = 2,
BaseDifficulty = new BeatmapDifficulty
{

View File

@ -15,6 +15,7 @@ using osu.Framework.MathUtils;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
@ -79,8 +80,12 @@ namespace osu.Game.Tests.Visual.SongSelect
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default));
Beatmap.SetDefault();
Dependencies.Cache(config = new OsuConfigManager(LocalStorage));
}
private OsuConfigManager config;
[SetUp]
public virtual void SetUp() => Schedule(() =>
{
@ -111,13 +116,15 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap);
AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; });
AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; });
AddStep(@"Sort by DateAdded", delegate { songSelect.FilterControl.Sort = SortMode.DateAdded; });
AddStep(@"Sort by BPM", delegate { songSelect.FilterControl.Sort = SortMode.BPM; });
AddStep(@"Sort by Length", delegate { songSelect.FilterControl.Sort = SortMode.Length; });
AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
var sortMode = config.GetBindable<SortMode>(OsuSetting.SongSelectSortingMode);
AddStep(@"Sort by Artist", delegate { sortMode.Value = SortMode.Artist; });
AddStep(@"Sort by Title", delegate { sortMode.Value = SortMode.Title; });
AddStep(@"Sort by Author", delegate { sortMode.Value = SortMode.Author; });
AddStep(@"Sort by DateAdded", delegate { sortMode.Value = SortMode.DateAdded; });
AddStep(@"Sort by BPM", delegate { sortMode.Value = SortMode.BPM; });
AddStep(@"Sort by Length", delegate { sortMode.Value = SortMode.Length; });
AddStep(@"Sort by Difficulty", delegate { sortMode.Value = SortMode.Difficulty; });
}
[Test]

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
@ -25,6 +26,11 @@ namespace osu.Game.Tests.Visual.UserInterface
{
private readonly NowPlayingOverlay np;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(BeatSyncedContainer)
};
[Cached]
private MusicController musicController = new MusicController();
@ -154,7 +160,9 @@ namespace osu.Game.Tests.Visual.UserInterface
if (timingPoints[timingPoints.Count - 1] == current)
return current;
return timingPoints[timingPoints.IndexOf(current) + 1];
int index = timingPoints.IndexOf(current); // -1 means that this is a "default beat"
return index == -1 ? current : timingPoints[index + 1];
}
private int calculateBeatCount(TimingControlPoint current)

View File

@ -249,7 +249,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Size = new Vector2(50);
Masking = true;
Blending = BlendingMode.Additive;
Blending = BlendingParameters.Additive;
Alpha = 0.5f;
Child = new Box { RelativeSizeAxes = Axes.Both };