2020-09-24 16:24:05 +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.
2020-09-24 21:25:04 +08:00
using System ;
2020-09-24 21:00:13 +08:00
using System.IO ;
using System.Linq ;
2020-09-24 16:24:05 +08:00
using NUnit.Framework ;
2020-10-04 22:57:35 +08:00
using osu.Framework.Allocation ;
2022-07-13 18:31:53 +08:00
using osu.Framework.Audio.Track ;
2022-02-07 01:44:12 +08:00
using osu.Framework.Extensions.ObjectExtensions ;
using osu.Framework.Graphics ;
2020-10-04 22:57:35 +08:00
using osu.Framework.Screens ;
2020-09-24 21:00:13 +08:00
using osu.Framework.Testing ;
2020-09-24 16:24:05 +08:00
using osu.Game.Beatmaps ;
2022-02-07 01:03:54 +08:00
using osu.Game.Beatmaps.ControlPoints ;
2023-01-22 00:10:14 +08:00
using osu.Game.Collections ;
2022-02-07 03:01:47 +08:00
using osu.Game.Database ;
2022-03-21 16:52:54 +08:00
using osu.Game.Overlays.Dialog ;
2020-09-24 16:24:05 +08:00
using osu.Game.Rulesets ;
2022-02-07 00:56:51 +08:00
using osu.Game.Rulesets.Catch ;
2023-08-09 04:06:30 +08:00
using osu.Game.Rulesets.Catch.Objects ;
2020-09-24 16:24:05 +08:00
using osu.Game.Rulesets.Osu ;
2022-02-07 01:03:54 +08:00
using osu.Game.Rulesets.Osu.Objects ;
using osu.Game.Rulesets.Osu.UI ;
2023-01-15 01:46:14 +08:00
using osu.Game.Rulesets.Taiko ;
using osu.Game.Rulesets.Taiko.Objects ;
2021-09-12 21:50:41 +08:00
using osu.Game.Screens.Edit ;
2020-09-24 21:00:13 +08:00
using osu.Game.Screens.Edit.Setup ;
2022-01-25 20:25:28 +08:00
using osu.Game.Storyboards ;
2020-09-24 21:00:13 +08:00
using osu.Game.Tests.Resources ;
2022-02-07 01:03:54 +08:00
using osuTK ;
2022-03-21 16:52:54 +08:00
using osuTK.Input ;
2020-09-24 21:00:13 +08:00
using SharpCompress.Archives ;
using SharpCompress.Archives.Zip ;
2020-09-24 16:24:05 +08:00
namespace osu.Game.Tests.Visual.Editing
{
public partial class TestSceneEditorBeatmapCreation : EditorTestScene
{
protected override Ruleset CreateEditorRuleset ( ) = > new OsuRuleset ( ) ;
2021-05-28 13:33:06 +08:00
protected override bool IsolateSavingFromDatabase = > false ;
2020-10-04 22:57:35 +08:00
[Resolved]
2022-07-07 16:51:49 +08:00
private BeatmapManager beatmapManager { get ; set ; } = null ! ;
2023-01-22 00:10:14 +08:00
[Resolved]
private RealmAccess realm { get ; set ; } = null ! ;
2022-07-07 16:51:49 +08:00
private Guid currentBeatmapSetID = > EditorBeatmap . BeatmapInfo . BeatmapSet ? . ID ? ? Guid . Empty ;
2020-10-04 22:57:35 +08:00
2020-09-24 21:25:04 +08:00
public override void SetUpSteps ( )
{
base . SetUpSteps ( ) ;
2020-09-24 16:24:05 +08:00
2020-09-25 11:25:50 +08:00
// if we save a beatmap with a hash collision, things fall over.
// probably needs a more solid resolution in the future but this will do for now.
AddStep ( "make new beatmap unique" , ( ) = > EditorBeatmap . Metadata . Title = Guid . NewGuid ( ) . ToString ( ) ) ;
2020-09-25 17:40:20 +08:00
}
2020-09-25 11:25:50 +08:00
2022-07-07 16:51:49 +08:00
protected override WorkingBeatmap CreateWorkingBeatmap ( IBeatmap beatmap , Storyboard ? storyboard = null ) = > new DummyWorkingBeatmap ( Audio , null ) ;
2021-05-31 13:24:46 +08:00
2020-09-25 17:40:20 +08:00
[Test]
public void TestCreateNewBeatmap ( )
{
2022-08-05 13:25:10 +08:00
AddAssert ( "status is none" , ( ) = > EditorBeatmap . BeatmapInfo . Status = = BeatmapOnlineStatus . None ) ;
2020-09-24 16:24:05 +08:00
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
2022-07-07 16:51:49 +08:00
AddAssert ( "new beatmap in database" , ( ) = > beatmapManager . QueryBeatmapSet ( s = > s . ID = = currentBeatmapSetID ) ? . Value . DeletePending = = false ) ;
2022-08-05 13:25:10 +08:00
AddAssert ( "status is modified" , ( ) = > EditorBeatmap . BeatmapInfo . Status = = BeatmapOnlineStatus . LocallyModified ) ;
2020-10-04 22:57:35 +08:00
}
[Test]
public void TestExitWithoutSave ( )
{
2022-07-07 16:51:49 +08:00
EditorBeatmap editorBeatmap = null ! ;
2021-09-12 21:50:41 +08:00
AddStep ( "store editor beatmap" , ( ) = > editorBeatmap = EditorBeatmap ) ;
2022-03-21 16:52:54 +08:00
AddStep ( "exit without save" , ( ) = > Editor . Exit ( ) ) ;
AddStep ( "hold to confirm" , ( ) = >
2021-06-23 10:30:52 +08:00
{
2022-03-21 16:52:54 +08:00
var confirmButton = DialogOverlay . CurrentDialog . ChildrenOfType < PopupDialogDangerousButton > ( ) . First ( ) ;
InputManager . MoveMouseTo ( confirmButton ) ;
InputManager . PressButton ( MouseButton . Left ) ;
2021-06-23 10:30:52 +08:00
} ) ;
2020-10-04 22:57:35 +08:00
AddUntilStep ( "wait for exit" , ( ) = > ! Editor . IsCurrentScreen ( ) ) ;
2022-03-21 16:52:54 +08:00
AddStep ( "release" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
2022-07-07 16:51:49 +08:00
AddAssert ( "new beatmap not persisted" , ( ) = > beatmapManager . QueryBeatmapSet ( s = > s . ID = = editorBeatmap . BeatmapInfo . BeatmapSet . AsNonNull ( ) . ID ) ? . Value . DeletePending = = true ) ;
2020-09-24 16:24:05 +08:00
}
2020-09-24 21:00:13 +08:00
[Test]
2022-07-01 15:17:40 +08:00
[FlakyTest]
/ *
* Fail rate around 1.2 % .
*
* Failing with realm refetch occasionally being null .
* My only guess is that the WorkingBeatmap at SetupScreen is dummy instead of the true one .
* If it ' s something else , we have larger issues with realm , but I don ' t think that ' s the case .
*
* at osu . Framework . Logging . ThrowingTraceListener . Fail ( String message1 , String message2 )
* at System . Diagnostics . TraceInternal . Fail ( String message , String detailMessage )
* at System . Diagnostics . TraceInternal . TraceProvider . Fail ( String message , String detailMessage )
* at System . Diagnostics . Debug . Fail ( String message , String detailMessage )
* at osu . Game . Database . ModelManager ` 1. < > c__DisplayClass8_0 . < performFileOperation > b__0 ( Realm realm ) ModelManager . cs : line 50
* at osu . Game . Database . RealmExtensions . Write ( Realm realm , Action ` 1 function ) RealmExtensions . cs : line 14
* at osu . Game . Database . ModelManager ` 1. performFileOperation ( TModel item , Action ` 1 operation ) ModelManager . cs : line 47
* at osu . Game . Database . ModelManager ` 1. AddFile ( TModel item , Stream contents , String filename ) ModelManager . cs : line 37
* at osu . Game . Screens . Edit . Setup . ResourcesSection . ChangeAudioTrack ( FileInfo source ) ResourcesSection . cs : line 115
* at osu . Game . Tests . Visual . Editing . TestSceneEditorBeatmapCreation . < TestAddAudioTrack > b__11_0 ( ) TestSceneEditorBeatmapCreation . cs : line 101
* /
2020-09-24 21:00:13 +08:00
public void TestAddAudioTrack ( )
{
2022-07-13 18:31:53 +08:00
AddAssert ( "track is virtual" , ( ) = > Beatmap . Value . Track is TrackVirtual ) ;
2020-09-24 21:00:13 +08:00
AddAssert ( "switch track to real track" , ( ) = >
{
var setup = Editor . ChildrenOfType < SetupScreen > ( ) . First ( ) ;
2021-10-27 12:04:41 +08:00
string temp = TestResources . GetTestBeatmapForImport ( ) ;
2020-09-24 21:00:13 +08:00
string extractedFolder = $"{temp}_extracted" ;
Directory . CreateDirectory ( extractedFolder ) ;
2022-06-27 14:53:05 +08:00
try
{
using ( var zip = ZipArchive . Open ( temp ) )
zip . WriteToDirectory ( extractedFolder ) ;
bool success = setup . ChildrenOfType < ResourcesSection > ( ) . First ( ) . ChangeAudioTrack ( new FileInfo ( Path . Combine ( extractedFolder , "03. Renatus - Soleily 192kbps.mp3" ) ) ) ;
// ensure audio file is copied to beatmap as "audio.mp3" rather than original filename.
Assert . That ( Beatmap . Value . Metadata . AudioFile = = "audio.mp3" ) ;
return success ;
}
finally
{
File . Delete ( temp ) ;
Directory . Delete ( extractedFolder , true ) ;
}
2020-09-24 21:00:13 +08:00
} ) ;
2022-07-13 18:31:53 +08:00
AddAssert ( "track is not virtual" , ( ) = > Beatmap . Value . Track is not TrackVirtual ) ;
2020-09-24 21:00:13 +08:00
AddAssert ( "track length changed" , ( ) = > Beatmap . Value . Track . Length > 60000 ) ;
2022-08-01 15:53:47 +08:00
AddStep ( "test play" , ( ) = > Editor . TestGameplay ( ) ) ;
AddUntilStep ( "wait for dialog" , ( ) = > DialogOverlay . CurrentDialog ! = null ) ;
AddStep ( "confirm save" , ( ) = > InputManager . Key ( Key . Number1 ) ) ;
AddUntilStep ( "wait for return to editor" , ( ) = > Editor . IsCurrentScreen ( ) ) ;
AddAssert ( "track is still not virtual" , ( ) = > Beatmap . Value . Track is not TrackVirtual ) ;
AddAssert ( "track length correct" , ( ) = > Beatmap . Value . Track . Length > 60000 ) ;
AddUntilStep ( "track not playing" , ( ) = > ! EditorClock . IsRunning ) ;
AddStep ( "play track" , ( ) = > InputManager . Key ( Key . Space ) ) ;
AddUntilStep ( "wait for track playing" , ( ) = > EditorClock . IsRunning ) ;
2020-09-24 21:00:13 +08:00
}
2022-01-24 01:34:33 +08:00
[Test]
2022-02-07 00:56:51 +08:00
public void TestCreateNewDifficulty ( [ Values ] bool sameRuleset )
2022-01-24 01:34:33 +08:00
{
string firstDifficultyName = Guid . NewGuid ( ) . ToString ( ) ;
string secondDifficultyName = Guid . NewGuid ( ) . ToString ( ) ;
AddStep ( "set unique difficulty name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = firstDifficultyName ) ;
2022-02-07 01:03:54 +08:00
AddStep ( "add timing point" , ( ) = > EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 1000 } ) ) ;
AddStep ( "add hitobjects" , ( ) = > EditorBeatmap . AddRange ( new [ ]
{
new HitCircle
{
Position = new Vector2 ( 0 ) ,
StartTime = 0
} ,
new HitCircle
{
Position = OsuPlayfield . BASE_SIZE ,
StartTime = 1000
}
} ) ) ;
2022-01-24 01:34:33 +08:00
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
AddAssert ( "new beatmap persisted" , ( ) = >
{
var beatmap = beatmapManager . QueryBeatmap ( b = > b . DifficultyName = = firstDifficultyName ) ;
2022-07-07 16:51:49 +08:00
var set = beatmapManager . QueryBeatmapSet ( s = > s . ID = = currentBeatmapSetID ) ;
2022-01-24 01:34:33 +08:00
return beatmap ! = null
& & beatmap . DifficultyName = = firstDifficultyName
& & set ! = null
& & set . PerformRead ( s = > s . Beatmaps . Single ( ) . ID = = beatmap . ID ) ;
} ) ;
2022-01-24 02:50:02 +08:00
AddAssert ( "can save again" , ( ) = > Editor . Save ( ) ) ;
2022-01-24 01:34:33 +08:00
2022-02-07 00:56:51 +08:00
AddStep ( "create new difficulty" , ( ) = > Editor . CreateNewDifficulty ( sameRuleset ? new OsuRuleset ( ) . RulesetInfo : new CatchRuleset ( ) . RulesetInfo ) ) ;
if ( sameRuleset )
{
AddUntilStep ( "wait for dialog" , ( ) = > DialogOverlay . CurrentDialog is CreateNewDifficultyDialog ) ;
AddStep ( "confirm creation with no objects" , ( ) = > DialogOverlay . CurrentDialog . PerformOkAction ( ) ) ;
}
2022-01-24 01:34:33 +08:00
AddUntilStep ( "wait for created" , ( ) = >
{
2022-07-07 16:51:49 +08:00
string? difficultyName = Editor . ChildrenOfType < EditorBeatmap > ( ) . SingleOrDefault ( ) ? . BeatmapInfo . DifficultyName ;
2022-01-24 01:34:33 +08:00
return difficultyName ! = null & & difficultyName ! = firstDifficultyName ;
} ) ;
2022-02-07 01:03:54 +08:00
AddAssert ( "created difficulty has timing point" , ( ) = >
{
var timingPoint = EditorBeatmap . ControlPointInfo . TimingPoints . Single ( ) ;
return timingPoint . Time = = 0 & & timingPoint . BeatLength = = 1000 ;
} ) ;
AddAssert ( "created difficulty has no objects" , ( ) = > EditorBeatmap . HitObjects . Count = = 0 ) ;
2022-08-05 13:25:10 +08:00
AddAssert ( "status is modified" , ( ) = > EditorBeatmap . BeatmapInfo . Status = = BeatmapOnlineStatus . LocallyModified ) ;
2022-02-07 01:03:54 +08:00
AddStep ( "set unique difficulty name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = secondDifficultyName ) ;
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
AddAssert ( "new beatmap persisted" , ( ) = >
{
var beatmap = beatmapManager . QueryBeatmap ( b = > b . DifficultyName = = secondDifficultyName ) ;
2022-07-07 16:51:49 +08:00
var set = beatmapManager . QueryBeatmapSet ( s = > s . ID = = currentBeatmapSetID ) ;
2022-02-07 01:03:54 +08:00
return beatmap ! = null
& & beatmap . DifficultyName = = secondDifficultyName
& & set ! = null
2023-01-22 00:10:14 +08:00
& & set . PerformRead ( s = >
s . Beatmaps . Count = = 2 & & s . Beatmaps . Any ( b = > b . DifficultyName = = secondDifficultyName ) & & s . Beatmaps . All ( b = > s . Status = = BeatmapOnlineStatus . LocallyModified ) ) ;
2022-02-07 01:03:54 +08:00
} ) ;
}
[Test]
public void TestCopyDifficulty ( )
{
2022-02-15 02:59:54 +08:00
string originalDifficultyName = Guid . NewGuid ( ) . ToString ( ) ;
string copyDifficultyName = $"{originalDifficultyName} (copy)" ;
2022-02-07 01:03:54 +08:00
2022-02-15 02:59:54 +08:00
AddStep ( "set unique difficulty name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = originalDifficultyName ) ;
2022-02-07 01:03:54 +08:00
AddStep ( "add timing point" , ( ) = > EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 1000 } ) ) ;
AddStep ( "add hitobjects" , ( ) = > EditorBeatmap . AddRange ( new [ ]
{
new HitCircle
{
Position = new Vector2 ( 0 ) ,
StartTime = 0
} ,
new HitCircle
{
Position = OsuPlayfield . BASE_SIZE ,
StartTime = 1000
}
} ) ) ;
2022-02-07 01:26:19 +08:00
AddStep ( "set approach rate" , ( ) = > EditorBeatmap . Difficulty . ApproachRate = 4 ) ;
2022-02-07 01:44:12 +08:00
AddStep ( "set combo colours" , ( ) = >
{
var beatmapSkin = EditorBeatmap . BeatmapSkin . AsNonNull ( ) ;
beatmapSkin . ComboColours . Clear ( ) ;
beatmapSkin . ComboColours . AddRange ( new [ ]
{
new Colour4 ( 255 , 0 , 0 , 255 ) ,
new Colour4 ( 0 , 0 , 255 , 255 )
} ) ;
} ) ;
2022-02-14 01:54:52 +08:00
AddStep ( "set status & online ID" , ( ) = >
{
EditorBeatmap . BeatmapInfo . OnlineID = 123456 ;
EditorBeatmap . BeatmapInfo . Status = BeatmapOnlineStatus . WIP ;
} ) ;
2022-02-07 01:03:54 +08:00
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
AddAssert ( "new beatmap persisted" , ( ) = >
{
2022-02-15 02:59:54 +08:00
var beatmap = beatmapManager . QueryBeatmap ( b = > b . DifficultyName = = originalDifficultyName ) ;
2022-07-07 16:51:49 +08:00
var set = beatmapManager . QueryBeatmapSet ( s = > s . ID = = currentBeatmapSetID ) ;
2022-02-07 01:03:54 +08:00
return beatmap ! = null
2022-02-15 02:59:54 +08:00
& & beatmap . DifficultyName = = originalDifficultyName
2022-02-07 01:03:54 +08:00
& & set ! = null
& & set . PerformRead ( s = > s . Beatmaps . Single ( ) . ID = = beatmap . ID ) ;
} ) ;
AddAssert ( "can save again" , ( ) = > Editor . Save ( ) ) ;
AddStep ( "create new difficulty" , ( ) = > Editor . CreateNewDifficulty ( new OsuRuleset ( ) . RulesetInfo ) ) ;
AddUntilStep ( "wait for dialog" , ( ) = > DialogOverlay . CurrentDialog is CreateNewDifficultyDialog ) ;
AddStep ( "confirm creation as a copy" , ( ) = > DialogOverlay . CurrentDialog . Buttons . ElementAt ( 1 ) . TriggerClick ( ) ) ;
AddUntilStep ( "wait for created" , ( ) = >
{
2022-07-07 16:51:49 +08:00
string? difficultyName = Editor . ChildrenOfType < EditorBeatmap > ( ) . SingleOrDefault ( ) ? . BeatmapInfo . DifficultyName ;
2022-02-15 02:59:54 +08:00
return difficultyName ! = null & & difficultyName ! = originalDifficultyName ;
2022-02-07 01:03:54 +08:00
} ) ;
2022-02-15 02:59:54 +08:00
AddAssert ( "created difficulty has copy suffix in name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = = copyDifficultyName ) ;
2022-02-07 01:03:54 +08:00
AddAssert ( "created difficulty has timing point" , ( ) = >
{
var timingPoint = EditorBeatmap . ControlPointInfo . TimingPoints . Single ( ) ;
return timingPoint . Time = = 0 & & timingPoint . BeatLength = = 1000 ;
} ) ;
AddAssert ( "created difficulty has objects" , ( ) = > EditorBeatmap . HitObjects . Count = = 2 ) ;
2022-02-07 01:26:19 +08:00
AddAssert ( "approach rate correctly copied" , ( ) = > EditorBeatmap . Difficulty . ApproachRate = = 4 ) ;
2022-02-07 01:44:12 +08:00
AddAssert ( "combo colours correctly copied" , ( ) = > EditorBeatmap . BeatmapSkin . AsNonNull ( ) . ComboColours . Count = = 2 ) ;
2022-02-07 01:03:54 +08:00
2022-08-03 19:20:06 +08:00
AddAssert ( "status is modified" , ( ) = > EditorBeatmap . BeatmapInfo . Status = = BeatmapOnlineStatus . LocallyModified ) ;
2022-02-14 01:54:52 +08:00
AddAssert ( "online ID not copied" , ( ) = > EditorBeatmap . BeatmapInfo . OnlineID = = - 1 ) ;
2022-01-24 01:34:33 +08:00
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
2022-02-07 03:01:47 +08:00
2022-07-07 16:51:49 +08:00
BeatmapInfo ? refetchedBeatmap = null ;
Live < BeatmapSetInfo > ? refetchedBeatmapSet = null ;
2022-02-07 03:01:47 +08:00
AddStep ( "refetch from database" , ( ) = >
2022-01-24 01:34:33 +08:00
{
2022-02-15 02:59:54 +08:00
refetchedBeatmap = beatmapManager . QueryBeatmap ( b = > b . DifficultyName = = copyDifficultyName ) ;
2022-07-07 16:51:49 +08:00
refetchedBeatmapSet = beatmapManager . QueryBeatmapSet ( s = > s . ID = = currentBeatmapSetID ) ;
2022-02-07 03:01:47 +08:00
} ) ;
2022-01-24 01:34:33 +08:00
2022-02-07 03:01:47 +08:00
AddAssert ( "new beatmap persisted" , ( ) = >
{
return refetchedBeatmap ! = null
2022-02-15 02:59:54 +08:00
& & refetchedBeatmap . DifficultyName = = copyDifficultyName
2022-02-07 03:01:47 +08:00
& & refetchedBeatmapSet ! = null
2022-02-15 02:59:54 +08:00
& & refetchedBeatmapSet . PerformRead ( s = >
s . Beatmaps . Count = = 2
& & s . Beatmaps . Any ( b = > b . DifficultyName = = originalDifficultyName )
& & s . Beatmaps . Any ( b = > b . DifficultyName = = copyDifficultyName ) ) ;
2022-01-24 01:34:33 +08:00
} ) ;
2022-02-07 03:01:47 +08:00
AddAssert ( "old beatmap file not deleted" , ( ) = > refetchedBeatmapSet . AsNonNull ( ) . PerformRead ( s = > s . Files . Count = = 2 ) ) ;
2022-01-24 01:34:33 +08:00
}
2022-01-24 02:25:59 +08:00
2023-01-22 00:10:14 +08:00
[Test]
public void TestCopyDifficultyDoesNotChangeCollections ( )
{
string originalDifficultyName = Guid . NewGuid ( ) . ToString ( ) ;
AddStep ( "set unique difficulty name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = originalDifficultyName ) ;
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
string originalMd5 = string . Empty ;
BeatmapCollection collection = null ! ;
AddStep ( "setup a collection with original beatmap" , ( ) = >
{
collection = new BeatmapCollection ( "test copy" ) ;
collection . BeatmapMD5Hashes . Add ( originalMd5 = EditorBeatmap . BeatmapInfo . MD5Hash ) ;
realm . Write ( r = >
{
r . Add ( collection ) ;
} ) ;
} ) ;
AddAssert ( "collection contains original beatmap" , ( ) = >
! string . IsNullOrEmpty ( originalMd5 ) & & collection . BeatmapMD5Hashes . Contains ( originalMd5 ) ) ;
AddStep ( "create new difficulty" , ( ) = > Editor . CreateNewDifficulty ( new OsuRuleset ( ) . RulesetInfo ) ) ;
AddUntilStep ( "wait for dialog" , ( ) = > DialogOverlay . CurrentDialog is CreateNewDifficultyDialog ) ;
AddStep ( "confirm creation as a copy" , ( ) = > DialogOverlay . CurrentDialog . Buttons . ElementAt ( 1 ) . TriggerClick ( ) ) ;
AddUntilStep ( "wait for created" , ( ) = >
{
string? difficultyName = Editor . ChildrenOfType < EditorBeatmap > ( ) . SingleOrDefault ( ) ? . BeatmapInfo . DifficultyName ;
return difficultyName ! = null & & difficultyName ! = originalDifficultyName ;
} ) ;
AddStep ( "save without changes" , ( ) = > Editor . Save ( ) ) ;
AddAssert ( "collection still points to old beatmap" , ( ) = > ! collection . BeatmapMD5Hashes . Contains ( EditorBeatmap . BeatmapInfo . MD5Hash )
& & collection . BeatmapMD5Hashes . Contains ( originalMd5 ) ) ;
AddStep ( "clean up collection" , ( ) = >
{
realm . Write ( r = >
{
r . Remove ( collection ) ;
} ) ;
} ) ;
}
2022-01-24 02:25:59 +08:00
[Test]
2022-02-17 06:13:43 +08:00
public void TestCreateMultipleNewDifficultiesSucceeds ( )
2022-01-24 02:25:59 +08:00
{
Guid setId = Guid . Empty ;
AddStep ( "retrieve set ID" , ( ) = > setId = EditorBeatmap . BeatmapInfo . BeatmapSet ! . ID ) ;
2022-02-17 06:13:43 +08:00
AddStep ( "set difficulty name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = "New Difficulty" ) ;
2022-01-24 02:25:59 +08:00
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
AddAssert ( "new beatmap persisted" , ( ) = >
{
var set = beatmapManager . QueryBeatmapSet ( s = > s . ID = = setId ) ;
return set ! = null & & set . PerformRead ( s = > s . Beatmaps . Count = = 1 & & s . Files . Count = = 1 ) ;
} ) ;
AddStep ( "try to create new difficulty" , ( ) = > Editor . CreateNewDifficulty ( new OsuRuleset ( ) . RulesetInfo ) ) ;
2022-02-17 06:13:43 +08:00
AddUntilStep ( "wait for dialog" , ( ) = > DialogOverlay . CurrentDialog is CreateNewDifficultyDialog ) ;
AddStep ( "confirm creation with no objects" , ( ) = > DialogOverlay . CurrentDialog . PerformOkAction ( ) ) ;
AddUntilStep ( "wait for created" , ( ) = >
{
2022-07-07 16:51:49 +08:00
string? difficultyName = Editor . ChildrenOfType < EditorBeatmap > ( ) . SingleOrDefault ( ) ? . BeatmapInfo . DifficultyName ;
2022-02-17 06:13:43 +08:00
return difficultyName ! = null & & difficultyName ! = "New Difficulty" ;
} ) ;
AddAssert ( "new difficulty has correct name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = = "New Difficulty (1)" ) ;
AddAssert ( "new difficulty persisted" , ( ) = >
2022-01-24 02:25:59 +08:00
{
var set = beatmapManager . QueryBeatmapSet ( s = > s . ID = = setId ) ;
2022-02-17 06:13:43 +08:00
return set ! = null & & set . PerformRead ( s = > s . Beatmaps . Count = = 2 & & s . Files . Count = = 2 ) ;
2022-01-24 02:25:59 +08:00
} ) ;
}
[Test]
2022-02-17 06:13:43 +08:00
public void TestSavingBeatmapFailsWithSameNamedDifficulties ( [ Values ] bool sameRuleset )
2022-01-24 02:25:59 +08:00
{
Guid setId = Guid . Empty ;
const string duplicate_difficulty_name = "duplicate" ;
AddStep ( "retrieve set ID" , ( ) = > setId = EditorBeatmap . BeatmapInfo . BeatmapSet ! . ID ) ;
AddStep ( "set difficulty name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = duplicate_difficulty_name ) ;
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
AddAssert ( "new beatmap persisted" , ( ) = >
{
var set = beatmapManager . QueryBeatmapSet ( s = > s . ID = = setId ) ;
return set ! = null & & set . PerformRead ( s = > s . Beatmaps . Count = = 1 & & s . Files . Count = = 1 ) ;
} ) ;
2022-02-07 00:56:51 +08:00
AddStep ( "create new difficulty" , ( ) = > Editor . CreateNewDifficulty ( sameRuleset ? new OsuRuleset ( ) . RulesetInfo : new CatchRuleset ( ) . RulesetInfo ) ) ;
if ( sameRuleset )
{
AddUntilStep ( "wait for dialog" , ( ) = > DialogOverlay . CurrentDialog is CreateNewDifficultyDialog ) ;
AddStep ( "confirm creation with no objects" , ( ) = > DialogOverlay . CurrentDialog . PerformOkAction ( ) ) ;
}
2022-01-24 02:25:59 +08:00
AddUntilStep ( "wait for created" , ( ) = >
{
2022-07-07 16:51:49 +08:00
string? difficultyName = Editor . ChildrenOfType < EditorBeatmap > ( ) . SingleOrDefault ( ) ? . BeatmapInfo . DifficultyName ;
2022-01-24 02:25:59 +08:00
return difficultyName ! = null & & difficultyName ! = duplicate_difficulty_name ;
} ) ;
AddStep ( "set difficulty name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = duplicate_difficulty_name ) ;
AddStep ( "try to save beatmap" , ( ) = > Editor . Save ( ) ) ;
AddAssert ( "beatmap set not corrupted" , ( ) = >
{
var set = beatmapManager . QueryBeatmapSet ( s = > s . ID = = setId ) ;
// the difficulty was already created at the point of the switch.
// what we want to check is that both difficulties do not use the same file.
return set ! = null & & set . PerformRead ( s = > s . Beatmaps . Count = = 2 & & s . Files . Count = = 2 ) ;
} ) ;
}
2023-01-15 01:46:14 +08:00
2023-08-09 04:06:30 +08:00
[Test]
public void TestExitBlockedWhenSavingBeatmapWithSameNamedDifficulties ( )
{
Guid setId = Guid . Empty ;
const string duplicate_difficulty_name = "duplicate" ;
AddStep ( "retrieve set ID" , ( ) = > setId = EditorBeatmap . BeatmapInfo . BeatmapSet ! . ID ) ;
AddStep ( "set difficulty name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = duplicate_difficulty_name ) ;
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
AddAssert ( "new beatmap persisted" , ( ) = >
{
var set = beatmapManager . QueryBeatmapSet ( s = > s . ID = = setId ) ;
return set ! = null & & set . PerformRead ( s = > s . Beatmaps . Count = = 1 & & s . Files . Count = = 1 ) ;
} ) ;
AddStep ( "create new difficulty" , ( ) = > Editor . CreateNewDifficulty ( new CatchRuleset ( ) . RulesetInfo ) ) ;
AddUntilStep ( "wait for created" , ( ) = >
{
string? difficultyName = Editor . ChildrenOfType < EditorBeatmap > ( ) . SingleOrDefault ( ) ? . BeatmapInfo . DifficultyName ;
return difficultyName ! = null & & difficultyName ! = duplicate_difficulty_name ;
} ) ;
AddUntilStep ( "wait for editor load" , ( ) = > Editor . IsLoaded & & DialogOverlay . IsLoaded ) ;
AddStep ( "add hitobjects" , ( ) = > EditorBeatmap . AddRange ( new [ ]
{
new Fruit
{
StartTime = 0
} ,
new Fruit
{
StartTime = 1000
}
} ) ) ;
AddStep ( "set difficulty name" , ( ) = > EditorBeatmap . BeatmapInfo . DifficultyName = duplicate_difficulty_name ) ;
AddUntilStep ( "wait for has unsaved changes" , ( ) = > Editor . HasUnsavedChanges ) ;
AddStep ( "exit" , ( ) = > Editor . Exit ( ) ) ;
AddUntilStep ( "wait for dialog" , ( ) = > DialogOverlay . CurrentDialog is PromptForSaveDialog ) ;
AddStep ( "attempt to save" , ( ) = > DialogOverlay . CurrentDialog . PerformOkAction ( ) ) ;
AddAssert ( "editor is still current" , ( ) = > Editor . IsCurrentScreen ( ) ) ;
}
2023-01-15 01:46:14 +08:00
[Test]
public void TestCreateNewDifficultyForInconvertibleRuleset ( )
{
Guid setId = Guid . Empty ;
AddStep ( "retrieve set ID" , ( ) = > setId = EditorBeatmap . BeatmapInfo . BeatmapSet ! . ID ) ;
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
AddStep ( "try to create new taiko difficulty" , ( ) = > Editor . CreateNewDifficulty ( new TaikoRuleset ( ) . RulesetInfo ) ) ;
AddUntilStep ( "wait for created" , ( ) = >
{
string? difficultyName = Editor . ChildrenOfType < EditorBeatmap > ( ) . SingleOrDefault ( ) ? . BeatmapInfo . DifficultyName ;
return difficultyName ! = null & & difficultyName = = "New Difficulty" ;
} ) ;
AddAssert ( "new difficulty persisted" , ( ) = >
{
var set = beatmapManager . QueryBeatmapSet ( s = > s . ID = = setId ) ;
return set ! = null & & set . PerformRead ( s = > s . Beatmaps . Count = = 2 & & s . Files . Count = = 2 ) ;
} ) ;
AddStep ( "add timing point" , ( ) = > EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 1000 } ) ) ;
AddStep ( "add hitobjects" , ( ) = > EditorBeatmap . AddRange ( new [ ]
{
new Hit
{
StartTime = 0
} ,
new Hit
{
StartTime = 1000
}
} ) ) ;
AddStep ( "save beatmap" , ( ) = > Editor . Save ( ) ) ;
AddStep ( "try to create new catch difficulty" , ( ) = > Editor . CreateNewDifficulty ( new CatchRuleset ( ) . RulesetInfo ) ) ;
AddUntilStep ( "wait for created" , ( ) = >
{
string? difficultyName = Editor . ChildrenOfType < EditorBeatmap > ( ) . SingleOrDefault ( ) ? . BeatmapInfo . DifficultyName ;
return difficultyName ! = null & & difficultyName = = "New Difficulty (1)" ;
} ) ;
AddAssert ( "new difficulty persisted" , ( ) = >
{
var set = beatmapManager . QueryBeatmapSet ( s = > s . ID = = setId ) ;
return set ! = null & & set . PerformRead ( s = > s . Beatmaps . Count = = 3 & & s . Files . Count = = 3 ) ;
} ) ;
}
2020-09-24 16:24:05 +08:00
}
}