2022-07-07 15:42:48 +08:00
// 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.
2022-07-13 15:43:39 +08:00
using System ;
2023-10-05 13:30:04 +08:00
using System.IO ;
2022-07-07 15:42:48 +08:00
using System.Linq ;
using NUnit.Framework ;
2022-07-08 17:59:08 +08:00
using osu.Framework.Allocation ;
using osu.Framework.Audio ;
using osu.Framework.Extensions ;
2022-07-09 00:02:46 +08:00
using osu.Framework.Extensions.ObjectExtensions ;
2022-07-15 19:14:58 +08:00
using osu.Framework.Graphics.Containers ;
2022-07-08 17:59:08 +08:00
using osu.Framework.Platform ;
2022-07-07 15:42:48 +08:00
using osu.Framework.Screens ;
2022-07-15 19:14:58 +08:00
using osu.Framework.Testing ;
2022-07-08 17:59:08 +08:00
using osu.Game.Beatmaps ;
2022-07-15 19:14:58 +08:00
using osu.Game.Graphics.Containers ;
2023-10-10 15:18:57 +08:00
using osu.Game.Online.API ;
using osu.Game.Online.API.Requests ;
using osu.Game.Online.API.Requests.Responses ;
2022-07-07 15:42:48 +08:00
using osu.Game.Rulesets ;
2022-09-19 10:31:38 +08:00
using osu.Game.Rulesets.Mods ;
2022-07-07 15:42:48 +08:00
using osu.Game.Rulesets.Objects ;
using osu.Game.Rulesets.Osu ;
2022-09-19 10:31:38 +08:00
using osu.Game.Rulesets.Osu.Mods ;
2022-07-08 18:17:51 +08:00
using osu.Game.Rulesets.Scoring ;
2022-07-07 15:42:48 +08:00
using osu.Game.Scoring ;
2023-07-26 14:21:41 +08:00
using osu.Game.Screens ;
2022-07-15 19:14:58 +08:00
using osu.Game.Screens.Play ;
2022-07-07 15:42:48 +08:00
using osu.Game.Screens.Ranking ;
2022-07-08 17:59:08 +08:00
using osu.Game.Tests.Resources ;
2023-10-10 15:18:57 +08:00
using osu.Game.Users ;
2023-07-26 14:21:41 +08:00
using osuTK.Input ;
2022-07-07 15:42:48 +08:00
namespace osu.Game.Tests.Visual.Gameplay
{
public partial class TestScenePlayerLocalScoreImport : PlayerTestScene
{
2022-07-08 17:59:08 +08:00
private BeatmapManager beatmaps = null ! ;
private RulesetStore rulesets = null ! ;
private BeatmapSetInfo ? importedSet ;
2022-07-07 15:42:48 +08:00
2022-07-08 17:59:08 +08:00
[BackgroundDependencyLoader]
private void load ( GameHost host , AudioManager audio )
{
Dependencies . Cache ( rulesets = new RealmRulesetStore ( Realm ) ) ;
2022-07-28 15:19:05 +08:00
Dependencies . Cache ( beatmaps = new BeatmapManager ( LocalStorage , Realm , null , audio , Resources , host , Beatmap . Default ) ) ;
2022-08-22 20:31:30 +08:00
Dependencies . Cache ( new ScoreManager ( rulesets , ( ) = > beatmaps , LocalStorage , Realm , API ) ) ;
2022-07-08 17:59:08 +08:00
Dependencies . Cache ( Realm ) ;
}
public override void SetUpSteps ( )
{
base . SetUpSteps ( ) ;
AddStep ( "import beatmap" , ( ) = >
{
beatmaps . Import ( TestResources . GetQuickTestBeatmapForImport ( ) ) . WaitSafely ( ) ;
importedSet = beatmaps . GetAllUsableBeatmapSets ( ) . First ( ) ;
} ) ;
}
protected override IBeatmap CreateBeatmap ( RulesetInfo ruleset ) = > beatmaps . GetWorkingBeatmap ( importedSet ? . Beatmaps . First ( ) ) . Beatmap ;
private Ruleset ? customRuleset ;
2022-07-07 15:42:48 +08:00
protected override Ruleset CreatePlayerRuleset ( ) = > customRuleset ? ? new OsuRuleset ( ) ;
protected override TestPlayer CreatePlayer ( Ruleset ruleset ) = > new TestPlayer ( false ) ;
protected override bool HasCustomSteps = > true ;
2022-07-15 19:14:58 +08:00
protected override bool AllowFail = > allowFail ;
private bool allowFail ;
2022-07-16 03:09:54 +08:00
[SetUp]
public void SetUp ( )
{
allowFail = false ;
customRuleset = null ;
}
2022-07-15 19:14:58 +08:00
[Test]
public void TestSaveFailedReplay ( )
{
2022-07-16 03:09:54 +08:00
AddStep ( "allow fail" , ( ) = > allowFail = true ) ;
2022-07-15 19:14:58 +08:00
CreateTest ( ) ;
AddUntilStep ( "fail screen displayed" , ( ) = > Player . ChildrenOfType < FailOverlay > ( ) . First ( ) . State . Value = = Visibility . Visible ) ;
2022-09-08 15:48:43 +08:00
AddUntilStep ( "wait for button clickable" , ( ) = > Player . ChildrenOfType < SaveFailedScoreButton > ( ) . First ( ) . ChildrenOfType < OsuClickableContainer > ( ) . First ( ) . Enabled . Value ) ;
2022-07-15 19:14:58 +08:00
AddUntilStep ( "score not in database" , ( ) = > Realm . Run ( r = > r . Find < ScoreInfo > ( Player . Score . ScoreInfo . ID ) = = null ) ) ;
AddStep ( "click save button" , ( ) = > Player . ChildrenOfType < SaveFailedScoreButton > ( ) . First ( ) . ChildrenOfType < OsuClickableContainer > ( ) . First ( ) . TriggerClick ( ) ) ;
2022-09-08 15:48:43 +08:00
AddUntilStep ( "score in database" , ( ) = > Realm . Run ( r = > r . Find < ScoreInfo > ( Player . Score . ScoreInfo . ID ) ! = null ) ) ;
2022-07-15 19:14:58 +08:00
}
2022-07-07 15:42:48 +08:00
2022-07-13 15:43:39 +08:00
[Test]
public void TestLastPlayedUpdated ( )
{
DateTimeOffset ? getLastPlayed ( ) = > Realm . Run ( r = > r . Find < BeatmapInfo > ( Beatmap . Value . BeatmapInfo . ID ) ? . LastPlayed ) ;
2023-10-10 15:30:51 +08:00
AddStep ( "reset last played" , ( ) = > Realm . Write ( r = > r . Find < BeatmapInfo > ( Beatmap . Value . BeatmapInfo . ID ) ! . LastPlayed = null ) ) ;
2022-07-13 15:43:39 +08:00
AddAssert ( "last played is null" , ( ) = > getLastPlayed ( ) = = null ) ;
CreateTest ( ) ;
AddUntilStep ( "wait for track to start running" , ( ) = > Beatmap . Value . Track . IsRunning ) ;
AddUntilStep ( "wait for last played to update" , ( ) = > getLastPlayed ( ) ! = null ) ;
}
2022-09-19 10:31:38 +08:00
[Test]
public void TestModReferenceNotRetained ( )
{
AddStep ( "allow fail" , ( ) = > allowFail = false ) ;
Mod [ ] originalMods = { new OsuModDaycore { SpeedChange = { Value = 0.8 } } } ;
Mod [ ] playerMods = null ! ;
2022-09-19 20:51:54 +08:00
AddStep ( "load player with mods" , ( ) = > LoadPlayer ( originalMods ) ) ;
2022-09-19 10:31:38 +08:00
AddUntilStep ( "player loaded" , ( ) = > Player . IsLoaded & & Player . Alpha = = 1 ) ;
AddStep ( "get mods at start of gameplay" , ( ) = > playerMods = Player . Score . ScoreInfo . Mods . ToArray ( ) ) ;
// Player creates new instance of mods during load.
AddAssert ( "player score has copied mods" , ( ) = > playerMods . First ( ) , ( ) = > Is . Not . SameAs ( originalMods . First ( ) ) ) ;
AddAssert ( "player score has matching mods" , ( ) = > playerMods . First ( ) , ( ) = > Is . EqualTo ( originalMods . First ( ) ) ) ;
AddUntilStep ( "wait for track to start running" , ( ) = > Beatmap . Value . Track . IsRunning ) ;
AddStep ( "seek to completion" , ( ) = > Player . GameplayClockContainer . Seek ( Player . DrawableRuleset . Objects . Last ( ) . GetEndTime ( ) ) ) ;
AddUntilStep ( "results displayed" , ( ) = > Player . GetChildScreen ( ) is ResultsScreen ) ;
// Player creates new instance of mods after gameplay to ensure any runtime references to drawables etc. are not retained.
AddAssert ( "results screen score has copied mods" , ( ) = > ( Player . GetChildScreen ( ) as ResultsScreen ) ? . Score . Mods . First ( ) , ( ) = > Is . Not . SameAs ( playerMods . First ( ) ) ) ;
AddAssert ( "results screen score has matching" , ( ) = > ( Player . GetChildScreen ( ) as ResultsScreen ) ? . Score . Mods . First ( ) , ( ) = > Is . EqualTo ( playerMods . First ( ) ) ) ;
AddUntilStep ( "score in database" , ( ) = > Realm . Run ( r = > r . Find < ScoreInfo > ( Player . Score . ScoreInfo . ID ) ! = null ) ) ;
2023-07-06 12:37:42 +08:00
AddUntilStep ( "databased score has correct mods" , ( ) = > Realm . Run ( r = > r . Find < ScoreInfo > ( Player . Score . ScoreInfo . ID ) ) ! . Mods . First ( ) , ( ) = > Is . EqualTo ( playerMods . First ( ) ) ) ;
2022-09-19 10:31:38 +08:00
}
2022-07-07 15:42:48 +08:00
[Test]
public void TestScoreStoredLocally ( )
{
CreateTest ( ) ;
AddUntilStep ( "wait for track to start running" , ( ) = > Beatmap . Value . Track . IsRunning ) ;
AddStep ( "seek to completion" , ( ) = > Player . GameplayClockContainer . Seek ( Player . DrawableRuleset . Objects . Last ( ) . GetEndTime ( ) ) ) ;
AddUntilStep ( "results displayed" , ( ) = > Player . GetChildScreen ( ) is ResultsScreen ) ;
AddUntilStep ( "score in database" , ( ) = > Realm . Run ( r = > r . Find < ScoreInfo > ( Player . Score . ScoreInfo . ID ) ! = null ) ) ;
}
2023-10-10 15:18:57 +08:00
[Test]
public void TestGuestScoreIsStoredAsGuest ( )
{
AddStep ( "set up API" , ( ) = > ( ( DummyAPIAccess ) API ) . HandleRequest = req = >
{
switch ( req )
{
case GetUserRequest userRequest :
userRequest . TriggerSuccess ( new APIUser
{
Username = "Guest" ,
CountryCode = CountryCode . JP ,
Id = 1234
} ) ;
return true ;
default :
return false ;
}
} ) ;
AddStep ( "log out" , ( ) = > API . Logout ( ) ) ;
CreateTest ( ) ;
AddUntilStep ( "wait for track to start running" , ( ) = > Beatmap . Value . Track . IsRunning ) ;
AddStep ( "log back in" , ( ) = > API . Login ( "username" , "password" ) ) ;
AddStep ( "seek to completion" , ( ) = > Player . GameplayClockContainer . Seek ( Player . DrawableRuleset . Objects . Last ( ) . GetEndTime ( ) ) ) ;
AddUntilStep ( "results displayed" , ( ) = > Player . GetChildScreen ( ) is ResultsScreen ) ;
AddUntilStep ( "score in database" , ( ) = > Realm . Run ( r = > r . Find < ScoreInfo > ( Player . Score . ScoreInfo . ID ) ! = null ) ) ;
AddAssert ( "score is not associated with online user" , ( ) = > Realm . Run ( r = > r . Find < ScoreInfo > ( Player . Score . ScoreInfo . ID ) ) ! . UserID = = APIUser . SYSTEM_USER_ID ) ;
}
2023-07-26 14:21:41 +08:00
[Test]
public void TestReplayExport ( )
{
CreateTest ( ) ;
AddUntilStep ( "wait for track to start running" , ( ) = > Beatmap . Value . Track . IsRunning ) ;
AddStep ( "seek to completion" , ( ) = > Player . GameplayClockContainer . Seek ( Player . DrawableRuleset . Objects . Last ( ) . GetEndTime ( ) ) ) ;
AddUntilStep ( "results displayed" , ( ) = > ( Player . GetChildScreen ( ) as ResultsScreen ) ? . IsLoaded = = true ) ;
AddUntilStep ( "score in database" , ( ) = > Realm . Run ( r = > r . Find < ScoreInfo > ( Player . Score . ScoreInfo . ID ) ! = null ) ) ;
AddUntilStep ( "wait for button clickable" , ( ) = > ( ( OsuScreen ) Player . GetChildScreen ( ) )
. ChildrenOfType < ReplayDownloadButton > ( ) . FirstOrDefault ( ) ?
2023-07-27 02:52:03 +08:00
. ChildrenOfType < OsuClickableContainer > ( ) . FirstOrDefault ( ) ?
2023-07-26 14:21:41 +08:00
. Enabled . Value = = true ) ;
AddAssert ( "no export files" , ( ) = > ! LocalStorage . GetFiles ( "exports" ) . Any ( ) ) ;
AddStep ( "Export replay" , ( ) = > InputManager . PressKey ( Key . F2 ) ) ;
string? filePath = null ;
2023-07-27 16:44:34 +08:00
// Files starting with _ are temporary, created by CreateFileSafely call.
2023-10-05 13:30:04 +08:00
AddUntilStep ( "wait for export file" , ( ) = > filePath = LocalStorage . GetFiles ( "exports" ) . SingleOrDefault ( f = > ! Path . GetFileName ( f ) . StartsWith ( "_" , StringComparison . Ordinal ) ) , ( ) = > Is . Not . Null ) ;
2023-10-31 13:00:49 +08:00
AddUntilStep ( "filesize is non-zero" , ( ) = >
2023-07-26 14:21:41 +08:00
{
2023-10-31 13:00:49 +08:00
try
{
using ( var stream = LocalStorage . GetStream ( filePath ) )
return stream . Length ;
}
catch ( IOException )
{
// file move may still be in progress.
return 0 ;
}
2023-07-26 14:21:41 +08:00
} , ( ) = > Is . Not . Zero ) ;
}
2022-07-07 15:42:48 +08:00
[Test]
public void TestScoreStoredLocallyCustomRuleset ( )
{
2022-07-08 18:17:51 +08:00
Ruleset createCustomRuleset ( ) = > new CustomRuleset ( ) ;
2022-07-07 15:42:48 +08:00
AddStep ( "import custom ruleset" , ( ) = > Realm . Write ( r = > r . Add ( createCustomRuleset ( ) . RulesetInfo ) ) ) ;
AddStep ( "set custom ruleset" , ( ) = > customRuleset = createCustomRuleset ( ) ) ;
CreateTest ( ) ;
2022-07-09 00:02:46 +08:00
AddAssert ( "score has custom ruleset" , ( ) = > Player . Score . ScoreInfo . Ruleset . Equals ( customRuleset . AsNonNull ( ) . RulesetInfo ) ) ;
2022-07-07 15:42:48 +08:00
AddUntilStep ( "wait for track to start running" , ( ) = > Beatmap . Value . Track . IsRunning ) ;
AddStep ( "seek to completion" , ( ) = > Player . GameplayClockContainer . Seek ( Player . DrawableRuleset . Objects . Last ( ) . GetEndTime ( ) ) ) ;
AddUntilStep ( "results displayed" , ( ) = > Player . GetChildScreen ( ) is ResultsScreen ) ;
2022-07-08 17:59:08 +08:00
AddUntilStep ( "score in database" , ( ) = > Realm . Run ( r = > r . Find < ScoreInfo > ( Player . Score . ScoreInfo . ID ) ! = null ) ) ;
2022-07-07 15:42:48 +08:00
}
2022-07-08 18:17:51 +08:00
private class CustomRuleset : OsuRuleset , ILegacyRuleset
{
public override string Description = > "custom" ;
public override string ShortName = > "custom" ;
2022-07-09 00:01:21 +08:00
int ILegacyRuleset . LegacyID = > - 1 ;
2022-07-08 18:17:51 +08:00
public override ScoreProcessor CreateScoreProcessor ( ) = > new ScoreProcessor ( this ) ;
}
2022-07-07 15:42:48 +08:00
}
}