Rewrite test scene and add more tests

This commit is contained in:
smoogipoo 2020-07-31 21:39:50 +09:00
parent 17018ffa8b
commit f1e721e396
3 changed files with 329 additions and 69 deletions

View File

@ -3,14 +3,24 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Multi.Ranking;
using osu.Game.Screens.Ranking;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using osu.Game.Users; using osu.Game.Users;
@ -18,43 +28,134 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
public class TestSceneTimeshiftResultsScreen : ScreenTestScene public class TestSceneTimeshiftResultsScreen : ScreenTestScene
{ {
private bool roomsReceived; private const int scores_per_result = 10;
private TestResultsScreen resultsScreen;
private int currentScoreId;
private bool requestComplete;
[SetUp] [SetUp]
public void Setup() => Schedule(() => public void Setup() => Schedule(() =>
{ {
roomsReceived = false; currentScoreId = 0;
requestComplete = false;
bindHandler(); bindHandler();
}); });
[Test] [Test]
public void TestShowResultsWithScore() public void TestShowWithUserScore()
{ {
createResults(new TestScoreInfo(new OsuRuleset().RulesetInfo)); ScoreInfo userScore = null;
AddWaitStep("wait for display", 5);
AddStep("bind user score info handler", () =>
{
userScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { OnlineScoreID = currentScoreId++ };
bindHandler(userScore: userScore);
});
createResults(() => userScore);
waitForDisplay();
AddAssert("user score selected", () => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.OnlineScoreID == userScore.OnlineScoreID).State == PanelState.Expanded);
} }
[Test] [Test]
public void TestShowResultsNullScore() public void TestShowNullUserScore()
{ {
createResults(null); createResults();
AddWaitStep("wait for display", 5); waitForDisplay();
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
} }
[Test] [Test]
public void TestShowResultsNullScoreWithDelay() public void TestShowUserScoreWithDelay()
{
ScoreInfo userScore = null;
AddStep("bind user score info handler", () =>
{
userScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { OnlineScoreID = currentScoreId++ };
bindHandler(3000, userScore);
});
createResults(() => userScore);
waitForDisplay();
AddAssert("more than 1 panel displayed", () => this.ChildrenOfType<ScorePanel>().Count() > 1);
AddAssert("user score selected", () => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.OnlineScoreID == userScore.OnlineScoreID).State == PanelState.Expanded);
}
[Test]
public void TestShowNullUserScoreWithDelay()
{ {
AddStep("bind delayed handler", () => bindHandler(3000)); AddStep("bind delayed handler", () => bindHandler(3000));
createResults(null);
AddUntilStep("wait for rooms to be received", () => roomsReceived); createResults();
AddWaitStep("wait for display", 5); waitForDisplay();
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
} }
private void createResults(ScoreInfo score) [Test]
public void TestFetchWhenScrolledToTheRight()
{
createResults();
waitForDisplay();
AddStep("bind delayed handler", () => bindHandler(3000));
for (int i = 0; i < 2; i++)
{
int beforePanelCount = 0;
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
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);
}
}
[Test]
public void TestFetchWhenScrolledToTheLeft()
{
ScoreInfo userScore = null;
AddStep("bind user score info handler", () =>
{
userScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { OnlineScoreID = currentScoreId++ };
bindHandler(userScore: userScore);
});
createResults(() => userScore);
waitForDisplay();
AddStep("bind delayed handler", () => bindHandler(3000));
for (int i = 0; i < 2; i++)
{
int beforePanelCount = 0;
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
AddStep("scroll to left", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToStart(false));
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);
}
}
private void createResults(Func<ScoreInfo> getScore = null)
{ {
AddStep("load results", () => AddStep("load results", () =>
{ {
LoadScreen(new TimeshiftResultsScreen(score, 1, new PlaylistItem LoadScreen(resultsScreen = new TestResultsScreen(getScore?.Invoke(), 1, new PlaylistItem
{ {
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo } Ruleset = { Value = new OsuRuleset().RulesetInfo }
@ -62,62 +163,213 @@ namespace osu.Game.Tests.Visual.Multiplayer
}); });
} }
private void bindHandler(double delay = 0) private void waitForDisplay()
{ {
var roomScores = new List<MultiplayerScore>(); AddUntilStep("wait for request to complete", () => requestComplete);
AddWaitStep("wait for display", 5);
}
for (int i = 0; i < 10; i++) private void bindHandler(double delay = 0, ScoreInfo userScore = null, bool failRequests = false) => ((DummyAPIAccess)API).HandleRequest = request =>
{
requestComplete = false;
if (failRequests)
{ {
roomScores.Add(new MultiplayerScore triggerFail(request, delay);
return;
}
switch (request)
{
case ShowPlaylistUserScoreRequest s:
if (userScore == null)
triggerFail(s, delay);
else
triggerSuccess(s, createUserResponse(userScore), delay);
break;
case IndexPlaylistScoresRequest i:
triggerSuccess(i, createIndexResponse(i), delay);
break;
}
};
private void triggerSuccess<T>(APIRequest<T> req, T result, double delay)
where T : class
{
if (delay == 0)
success();
else
{
Task.Run(async () =>
{ {
ID = i, await Task.Delay(TimeSpan.FromMilliseconds(delay));
Accuracy = 0.9 - 0.01 * i, Schedule(success);
EndedAt = DateTimeOffset.Now.Subtract(TimeSpan.FromHours(i)), });
}
void success()
{
requestComplete = true;
req.TriggerSuccess(result);
}
}
private void triggerFail(APIRequest req, double delay)
{
if (delay == 0)
fail();
else
{
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromMilliseconds(delay));
Schedule(fail);
});
}
void fail()
{
requestComplete = true;
req.TriggerFailure(new WebException("Failed."));
}
}
private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore)
{
var multiplayerUserScore = new MultiplayerScore
{
ID = (int)(userScore.OnlineScoreID ?? currentScoreId++),
Accuracy = userScore.Accuracy,
EndedAt = userScore.Date,
Passed = userScore.Passed,
Rank = userScore.Rank,
MaxCombo = userScore.MaxCombo,
TotalScore = userScore.TotalScore,
User = userScore.User,
Statistics = userScore.Statistics,
ScoresAround = new MultiplayerScoresAround
{
Higher = new MultiplayerScores(),
Lower = new MultiplayerScores()
}
};
for (int i = 1; i <= scores_per_result; i++)
{
multiplayerUserScore.ScoresAround.Lower.Scores.Add(new MultiplayerScore
{
ID = currentScoreId++,
Accuracy = userScore.Accuracy,
EndedAt = userScore.Date,
Passed = true, Passed = true,
Rank = ScoreRank.B, Rank = userScore.Rank,
MaxCombo = 999, MaxCombo = userScore.MaxCombo,
TotalScore = 999999 - i * 1000, TotalScore = userScore.TotalScore - i,
User = new User User = new User
{ {
Id = 2, Id = 2,
Username = $"peppy{i}", Username = $"peppy{i}",
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
}, },
Statistics = Statistics = userScore.Statistics
});
multiplayerUserScore.ScoresAround.Higher.Scores.Add(new MultiplayerScore
{
ID = currentScoreId++,
Accuracy = userScore.Accuracy,
EndedAt = userScore.Date,
Passed = true,
Rank = userScore.Rank,
MaxCombo = userScore.MaxCombo,
TotalScore = userScore.TotalScore + i,
User = new User
{
Id = 2,
Username = $"peppy{i}",
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
},
Statistics = userScore.Statistics
});
}
addCursor(multiplayerUserScore.ScoresAround.Lower);
addCursor(multiplayerUserScore.ScoresAround.Higher);
return multiplayerUserScore;
}
private IndexedMultiplayerScores createIndexResponse(IndexPlaylistScoresRequest req)
{
var result = new IndexedMultiplayerScores();
long startTotalScore = req.Cursor?.Properties["total_score"].ToObject<long>() ?? 1000000;
string sort = req.IndexParams?.Properties["sort"].ToObject<string>() ?? "score_desc";
for (int i = 1; i <= scores_per_result; i++)
{
result.Scores.Add(new MultiplayerScore
{
ID = currentScoreId++,
Accuracy = 1,
EndedAt = DateTimeOffset.Now,
Passed = true,
Rank = ScoreRank.X,
MaxCombo = 1000,
TotalScore = startTotalScore + (sort == "score_asc" ? i : -i),
User = new User
{
Id = 2,
Username = $"peppy{i}",
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
},
Statistics = new Dictionary<HitResult, int>
{ {
{ HitResult.Miss, 1 }, { HitResult.Miss, 1 },
{ HitResult.Meh, 50 }, { HitResult.Meh, 50 },
{ HitResult.Good, 100 }, { HitResult.Good, 100 },
{ HitResult.Great, 300 }, { HitResult.Great, 300 }
} }
}); });
} }
((DummyAPIAccess)API).HandleRequest = request => addCursor(result);
return result;
}
private void addCursor(MultiplayerScores scores)
{
scores.Cursor = new Cursor
{ {
switch (request) Properties = new Dictionary<string, JToken>
{ {
case IndexPlaylistScoresRequest r: { "total_score", JToken.FromObject(scores.Scores[^1].TotalScore) },
if (delay == 0) { "score_id", JToken.FromObject(scores.Scores[^1].ID) },
success(); }
else };
{
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromMilliseconds(delay));
Schedule(success);
});
}
void success() scores.Params = new IndexScoresParams
{ {
r.TriggerSuccess(new IndexedMultiplayerScores { Scores = roomScores }); Properties = new Dictionary<string, JToken>
roomsReceived = true; {
} { "sort", JToken.FromObject(scores.Scores[^1].TotalScore > scores.Scores[^2].TotalScore ? "score_asc" : "score_desc") }
break;
} }
}; };
} }
private class TestResultsScreen : TimeshiftResultsScreen
{
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(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry = true)
: base(score, roomId, playlistItem, allowRetry)
{
}
}
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
using osu.Game.Extensions; using osu.Game.Extensions;
@ -14,39 +15,45 @@ namespace osu.Game.Online.Multiplayer
/// </summary> /// </summary>
public class IndexPlaylistScoresRequest : APIRequest<IndexedMultiplayerScores> public class IndexPlaylistScoresRequest : APIRequest<IndexedMultiplayerScores>
{ {
private readonly int roomId; public readonly int RoomId;
private readonly int playlistItemId; public readonly int PlaylistItemId;
private readonly Cursor cursor;
private readonly IndexScoresParams indexParams; [CanBeNull]
public readonly Cursor Cursor;
[CanBeNull]
public readonly IndexScoresParams IndexParams;
public IndexPlaylistScoresRequest(int roomId, int playlistItemId) public IndexPlaylistScoresRequest(int roomId, int playlistItemId)
{ {
this.roomId = roomId; RoomId = roomId;
this.playlistItemId = playlistItemId; PlaylistItemId = playlistItemId;
} }
public IndexPlaylistScoresRequest(int roomId, int playlistItemId, [NotNull] Cursor cursor, [NotNull] IndexScoresParams indexParams) public IndexPlaylistScoresRequest(int roomId, int playlistItemId, [NotNull] Cursor cursor, [NotNull] IndexScoresParams indexParams)
: this(roomId, playlistItemId) : this(roomId, playlistItemId)
{ {
this.cursor = cursor; Cursor = cursor;
this.indexParams = indexParams; IndexParams = indexParams;
} }
protected override WebRequest CreateWebRequest() protected override WebRequest CreateWebRequest()
{ {
var req = base.CreateWebRequest(); var req = base.CreateWebRequest();
if (cursor != null) if (Cursor != null)
{ {
req.AddCursor(cursor); Debug.Assert(IndexParams != null);
foreach (var (key, value) in indexParams.Properties) req.AddCursor(Cursor);
foreach (var (key, value) in IndexParams.Properties)
req.AddParameter(key, value.ToString()); req.AddParameter(key, value.ToString());
} }
return req; return req;
} }
protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores"; protected override string Target => $@"rooms/{RoomId}/playlist/{PlaylistItemId}/scores";
} }
} }

View File

@ -22,9 +22,10 @@ namespace osu.Game.Screens.Multi.Ranking
private readonly int roomId; private readonly int roomId;
private readonly PlaylistItem playlistItem; private readonly PlaylistItem playlistItem;
private LoadingSpinner leftLoadingLayer; protected LoadingSpinner LeftSpinner { get; private set; }
private LoadingSpinner centreLoadingLayer; protected LoadingSpinner CentreSpinner { get; private set; }
private LoadingSpinner rightLoadingLayer; protected LoadingSpinner RightSpinner { get; private set; }
private MultiplayerScores higherScores; private MultiplayerScores higherScores;
private MultiplayerScores lowerScores; private MultiplayerScores lowerScores;
@ -47,18 +48,18 @@ namespace osu.Game.Screens.Multi.Ranking
Padding = new MarginPadding { Bottom = TwoLayerButton.SIZE_EXTENDED.Y }, Padding = new MarginPadding { Bottom = TwoLayerButton.SIZE_EXTENDED.Y },
Children = new Drawable[] Children = new Drawable[]
{ {
leftLoadingLayer = new PanelListLoadingSpinner(ScorePanelList) LeftSpinner = new PanelListLoadingSpinner(ScorePanelList)
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre, Origin = Anchor.Centre,
}, },
centreLoadingLayer = new PanelListLoadingSpinner(ScorePanelList) CentreSpinner = new PanelListLoadingSpinner(ScorePanelList)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
State = { Value = Score == null ? Visibility.Visible : Visibility.Hidden }, State = { Value = Score == null ? Visibility.Visible : Visibility.Hidden },
}, },
rightLoadingLayer = new PanelListLoadingSpinner(ScorePanelList) RightSpinner = new PanelListLoadingSpinner(ScorePanelList)
{ {
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -110,9 +111,9 @@ namespace osu.Game.Screens.Multi.Ranking
return null; return null;
if (pivot == higherScores) if (pivot == higherScores)
leftLoadingLayer.Show(); LeftSpinner.Show();
else else
rightLoadingLayer.Show(); RightSpinner.Show();
return createIndexRequest(scoresCallback, pivot); return createIndexRequest(scoresCallback, pivot);
} }
@ -174,12 +175,12 @@ namespace osu.Game.Screens.Multi.Ranking
private void hideLoadingSpinners([CanBeNull] MultiplayerScores pivot = null) private void hideLoadingSpinners([CanBeNull] MultiplayerScores pivot = null)
{ {
centreLoadingLayer.Hide(); CentreSpinner.Hide();
if (pivot == lowerScores) if (pivot == lowerScores)
rightLoadingLayer.Hide(); RightSpinner.Hide();
else if (pivot == higherScores) else if (pivot == higherScores)
leftLoadingLayer.Hide(); LeftSpinner.Hide();
} }
private class PanelListLoadingSpinner : LoadingSpinner private class PanelListLoadingSpinner : LoadingSpinner