Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow choosing different background/audio files for individual difficulties #30860

Merged
merged 27 commits into from
Dec 11, 2024
Merged
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
259ad8a
Add failing test cases
frenzibyte Nov 24, 2024
871c365
Preserve existing beatmap background/audio files if used elsewhere
frenzibyte Nov 24, 2024
8e20dc7
Add option to update all difficulties with new background/audio file
frenzibyte Nov 24, 2024
e348b3a
Only enable button if there are multiple difficulties
frenzibyte Nov 24, 2024
dc210d5
Add test coverage for sync button
frenzibyte Nov 24, 2024
c8b13b7
Add localisation support
frenzibyte Nov 24, 2024
a872f74
Make sync button only affect changed resource type
frenzibyte Nov 24, 2024
95a6226
Only enable button if there are different filenames
frenzibyte Nov 24, 2024
3480da2
Remove no-op `SaveState` call
frenzibyte Nov 24, 2024
1468385
Reset new file states after syncing
frenzibyte Nov 25, 2024
b70fb4b
Add `FormBeatmapFileSelector` for intermediate user-choice step
frenzibyte Nov 27, 2024
efb68e4
Refactor `ResourcesSection` to support new form of selection
frenzibyte Nov 27, 2024
4b8094d
Update test coverage
frenzibyte Nov 27, 2024
238a1ce
Fix tests reliability and improve code
frenzibyte Nov 27, 2024
4d9d5ad
Rename parameter to be more clear
peppy Nov 28, 2024
32b34c1
Rename container to make more sense
peppy Nov 28, 2024
4a1401a
Rewrite bindable flow to make more sense
frenzibyte Nov 28, 2024
b1d0939
Add localisation support
frenzibyte Nov 28, 2024
311f094
Abstractify resource change logic and share between background and audio
frenzibyte Nov 28, 2024
489d7a3
Perform a single `Save` call rather than doing it in each difficulty
frenzibyte Nov 28, 2024
06824c1
Add failing test case
frenzibyte Dec 4, 2024
60d91ba
Merge branch 'master' into editor-multiple-background-audio-files
frenzibyte Dec 4, 2024
8e0f6fc
Re-encode difficulties on resource change
frenzibyte Dec 4, 2024
7ec2d91
Merge branch 'master' into editor-multiple-background-audio-files
peppy Dec 10, 2024
92dfcae
Adjust bad grammar
peppy Dec 10, 2024
3cac583
Rewrite resource changing code to be more legible (to my eye)
peppy Dec 10, 2024
bbaa542
Add note about expensive operation
peppy Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 116 additions & 82 deletions osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs
Original file line number Diff line number Diff line change
@@ -15,8 +15,6 @@
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Collections;
using osu.Game.Database;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Localisation;
using osu.Game.Overlays.Dialog;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
@@ -102,17 +100,7 @@

AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddAssert("track is virtual", () => Beatmap.Value.Track is TrackVirtual);
AddAssert("switch track to real track", () =>
{
var setup = Editor.ChildrenOfType<SetupScreen>().First();

return setFile(TestResources.GetTestBeatmapForImport(), extractedFolder =>
{
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(new FileInfo(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3")));
Assert.That(Beatmap.Value.Metadata.AudioFile == "audio.mp3");
return success;
});
});
AddAssert("switch track to real track", () => setAudio(applyToAllDifficulties: true, expected: "audio.mp3"));

AddAssert("track is not virtual", () => Beatmap.Value.Track is not TrackVirtual);
AddUntilStep("track length changed", () => Beatmap.Value.Track.Length > 60000);
@@ -518,10 +506,10 @@
}

[Test]
public void TestMultipleBackgroundFiles()
public void TestSingleBackgroundFile()
{
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddAssert("set background", () => setBackground(expected: "bg.jpg"));
AddAssert("set background", () => setBackground(applyToAllDifficulties: true, expected: "bg.jpg"));

AddStep("save", () => Editor.Save());
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo));
@@ -533,40 +521,42 @@
return difficultyName != null && difficultyName == "New Difficulty";
});

AddAssert("new difficulty uses same background", () => Beatmap.Value.Metadata.BackgroundFile == "bg.jpg");
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo));
AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog);
AddStep("confirm creation with no objects", () => DialogOverlay.CurrentDialog!.PerformOkAction());
AddUntilStep("wait for created", () =>
{
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
return difficultyName != null && difficultyName == "New Difficulty (1)";
});

AddStep("switch to second difficulty", () => Editor.SwitchToDifficulty(Beatmap.Value.BeatmapSetInfo.Beatmaps.ElementAt(1)));
AddUntilStep("wait for editor load", () => Editor.IsLoaded);
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
AddAssert("set background", () => setBackground(expected: "bg (1).jpg"));
AddAssert("new difficulty uses new background", () => Beatmap.Value.Metadata.BackgroundFile == "bg (1).jpg");

AddAssert("set background on second diff only", () => setBackground(applyToAllDifficulties: false, expected: "bg (1).jpg"));
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg (1).jpg"));
AddStep("save", () => Editor.Save());
AddStep("switch to previous difficulty", () => Editor.SwitchToDifficulty(Beatmap.Value.BeatmapSetInfo.Beatmaps.First()));
AddAssert("old difficulty uses old background", () => Beatmap.Value.Metadata.BackgroundFile == "bg.jpg");
AddAssert("old background not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg.jpg"));

AddStep("switch to first difficulty", () => Editor.SwitchToDifficulty(Beatmap.Value.BeatmapSetInfo.Beatmaps.First()));
AddUntilStep("wait for editor load", () => Editor.IsLoaded);
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
AddStep("set background", () => setBackground(expected: "bg.jpg"));
AddAssert("other background not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg (1).jpg"));

bool setBackground(string expected)
{
var setup = Editor.ChildrenOfType<SetupScreen>().First();
AddAssert("set background on first diff only", () => setBackground(applyToAllDifficulties: false, expected: "bg (2).jpg"));
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg (2).jpg"));
AddStep("save", () => Editor.Save());

return setFile(TestResources.GetQuickTestBeatmapForImport(), extractedFolder =>
{
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeBackgroundImage(new FileInfo(Path.Combine(extractedFolder, "machinetop_background.jpg")));
Assert.That(Beatmap.Value.Metadata.BackgroundFile, Is.EqualTo(expected));
return success;
});
}
AddAssert("set background on all diff", () => setBackground(applyToAllDifficulties: true, expected: "bg.jpg"));
AddAssert("all diff uses one background", () => Beatmap.Value.BeatmapSetInfo.Beatmaps.All(b => b.Metadata.BackgroundFile == "bg.jpg"));
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg.jpg"));
AddAssert("other files removed", () => !Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg (1).jpg" || f.Filename == "bg (2).jpg"));
}

[Test]
public void TestMultipleAudioFiles()
public void TestSingleAudioFile()
{
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddAssert("set audio", () => setAudio(expected: "audio.mp3"));
AddAssert("set audio", () => setAudio(applyToAllDifficulties: true, expected: "audio.mp3"));

AddStep("save", () => Editor.Save());
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo));
@@ -578,44 +568,42 @@
return difficultyName != null && difficultyName == "New Difficulty";
});

AddAssert("new difficulty uses same audio", () => Beatmap.Value.Metadata.AudioFile == "audio.mp3");
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo));
AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog);
AddStep("confirm creation with no objects", () => DialogOverlay.CurrentDialog!.PerformOkAction());
AddUntilStep("wait for created", () =>
{
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
return difficultyName != null && difficultyName == "New Difficulty (1)";
});

AddStep("switch to second difficulty", () => Editor.SwitchToDifficulty(Beatmap.Value.BeatmapSetInfo.Beatmaps.ElementAt(1)));
AddUntilStep("wait for editor load", () => Editor.IsLoaded);
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
AddAssert("set audio", () => setAudio(expected: "audio (1).mp3"));
AddAssert("new difficulty uses new audio", () => Beatmap.Value.Metadata.AudioFile == "audio (1).mp3");

AddAssert("set audio on second diff only", () => setAudio(applyToAllDifficulties: false, expected: "audio (1).mp3"));
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio (1).mp3"));
AddStep("save", () => Editor.Save());
AddStep("switch to previous difficulty", () => Editor.SwitchToDifficulty(Beatmap.Value.BeatmapSetInfo.Beatmaps.First()));
AddAssert("old difficulty uses old audio", () => Beatmap.Value.Metadata.AudioFile == "audio.mp3");
AddAssert("old audio not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio.mp3"));

AddStep("switch to first difficulty", () => Editor.SwitchToDifficulty(Beatmap.Value.BeatmapSetInfo.Beatmaps.First()));
AddUntilStep("wait for editor load", () => Editor.IsLoaded);
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
AddStep("set audio", () => setAudio(expected: "audio.mp3"));
AddAssert("other audio not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio (1).mp3"));

bool setAudio(string expected)
{
var setup = Editor.ChildrenOfType<SetupScreen>().First();
AddAssert("set audio on first diff only", () => setAudio(applyToAllDifficulties: false, expected: "audio (2).mp3"));
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio (2).mp3"));
AddStep("save", () => Editor.Save());

return setFile(TestResources.GetTestBeatmapForImport(), extractedFolder =>
{
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(new FileInfo(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3")));
Assert.That(Beatmap.Value.Metadata.AudioFile, Is.EqualTo(expected));
return success;
});
}
AddAssert("set audio on all diff", () => setAudio(applyToAllDifficulties: true, expected: "audio.mp3"));
AddAssert("all diff uses one audio", () => Beatmap.Value.BeatmapSetInfo.Beatmaps.All(b => b.Metadata.AudioFile == "audio.mp3"));
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio.mp3"));
AddAssert("other files removed", () => !Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio (1).mp3" || f.Filename == "audio (2).mp3"));
}

[Test]
public void TestUpdateBackgroundOnAllDifficulties()
public void TestMultipleBackgroundFiles()
{
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddAssert("button disabled", () => !getButton().Enabled.Value);
AddAssert("set background", () => setBackground(expected: "bg.jpg"));

// there is only one diff so this should still be disabled.
AddAssert("button still disabled", () => !getButton().Enabled.Value);
AddAssert("set background", () => setBackground(applyToAllDifficulties: false, expected: "bg.jpg"));

AddStep("save", () => Editor.Save());
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo));
@@ -630,35 +618,81 @@
AddAssert("new difficulty uses same background", () => Beatmap.Value.Metadata.BackgroundFile == "bg.jpg");
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
AddAssert("button disabled", () => !getButton().Enabled.Value);
AddAssert("set background", () => setBackground(expected: "bg (1).jpg"));
AddAssert("new background added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg (1).jpg"));
AddAssert("set background", () => setBackground(applyToAllDifficulties: false, expected: "bg (1).jpg"));
AddAssert("new difficulty uses new background", () => Beatmap.Value.Metadata.BackgroundFile == "bg (1).jpg");

AddAssert("button enabled", () => getButton().Enabled.Value);
AddStep("press button", () => getButton().TriggerClick());
AddStep("save", () => Editor.Save());
AddStep("switch to previous difficulty", () => Editor.SwitchToDifficulty(Beatmap.Value.BeatmapSetInfo.Beatmaps.First()));
AddAssert("old difficulty uses old background", () => Beatmap.Value.Metadata.BackgroundFile == "bg.jpg");
AddAssert("old background not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg.jpg"));

AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());

Check failure on line 630 in osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs

GitHub Actions / Results

osu.Game.Tests.Visual.Editing.TestSceneEditorBeatmapCreation ► TestMultipleBackgroundFiles

Failed test found in: TestResults-Linux-SingleThread.trx Error: "wait for load" timed out
Raw output
"wait for load" timed out
   at osu.Game.Tests.Visual.Editing.TestSceneEditorBeatmapCreation.TestMultipleBackgroundFiles() in /home/runner/work/osu/osu/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs:line 630

AddStep("set background", () => setBackground(applyToAllDifficulties: false, expected: "bg.jpg"));
AddAssert("other background not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg (1).jpg"));
}

[Test]
public void TestMultipleAudioFiles()
{
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddAssert("set audio", () => setAudio(applyToAllDifficulties: false, expected: "audio.mp3"));

AddStep("save", () => Editor.Save());
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo));
AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog);
AddStep("confirm creation with no objects", () => DialogOverlay.CurrentDialog!.PerformOkAction());
AddUntilStep("wait for created", () =>
{
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
return difficultyName != null && difficultyName == "New Difficulty";
});

AddAssert("new difficulty still uses new background", () => Beatmap.Value.BeatmapSetInfo.Beatmaps[1].Metadata.BackgroundFile == "bg (1).jpg");
AddAssert("old difficulty uses new background", () => Beatmap.Value.BeatmapSetInfo.Beatmaps[0].Metadata.BackgroundFile == "bg (1).jpg");
AddAssert("old background removed", () => Beatmap.Value.BeatmapSetInfo.Files.All(f => f.Filename != "bg.jpg"));
AddAssert("new difficulty uses same audio", () => Beatmap.Value.Metadata.AudioFile == "audio.mp3");
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
AddAssert("set audio", () => setAudio(applyToAllDifficulties: false, expected: "audio (1).mp3"));
AddAssert("new difficulty uses new audio", () => Beatmap.Value.Metadata.AudioFile == "audio (1).mp3");

AddStep("save", () => Editor.Save());
AddStep("switch to previous difficulty", () => Editor.SwitchToDifficulty(Beatmap.Value.BeatmapSetInfo.Beatmaps.First()));
AddAssert("old difficulty still uses new background", () => Beatmap.Value.Metadata.BackgroundFile == "bg (1).jpg");
AddAssert("old difficulty uses old audio", () => Beatmap.Value.Metadata.AudioFile == "audio.mp3");
AddAssert("old audio not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio.mp3"));

AddStep("enter setup mode", () => InputManager.Key(Key.F4));
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());

Check failure on line 663 in osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs

GitHub Actions / Results

osu.Game.Tests.Visual.Editing.TestSceneEditorBeatmapCreation ► TestMultipleAudioFiles

Failed test found in: TestResults-Linux-SingleThread.trx TestResults-Linux-SingleThread.trx Error: "wait for load" timed out
Raw output
"wait for load" timed out
   at osu.Game.Tests.Visual.Editing.TestSceneEditorBeatmapCreation.TestMultipleAudioFiles() in /home/runner/work/osu/osu/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs:line 663

AddStep("set audio", () => setAudio(applyToAllDifficulties: false, expected: "audio.mp3"));
AddAssert("other audio not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio (1).mp3"));
}

bool setBackground(string expected)
private bool setBackground(bool applyToAllDifficulties, string expected)
{
var setup = Editor.ChildrenOfType<SetupScreen>().First();

return setFile(TestResources.GetQuickTestBeatmapForImport(), extractedFolder =>
{
var setup = Editor.ChildrenOfType<SetupScreen>().First();
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeBackgroundImage(
new FileInfo(Path.Combine(extractedFolder, @"machinetop_background.jpg")),
applyToAllDifficulties);

return setFile(TestResources.GetQuickTestBeatmapForImport(), extractedFolder =>
{
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeBackgroundImage(new FileInfo(Path.Combine(extractedFolder, "machinetop_background.jpg")));
Assert.That(Beatmap.Value.Metadata.BackgroundFile, Is.EqualTo(expected));
return success;
});
}
Assert.That(Beatmap.Value.Metadata.BackgroundFile, Is.EqualTo(expected));
return success;
});
}

private bool setAudio(bool applyToAllDifficulties, string expected)
{
var setup = Editor.ChildrenOfType<SetupScreen>().First();

RoundedButton getButton() => Editor.ChildrenOfType<RoundedButton>().Single(b => b.Text == EditorSetupStrings.ResourcesUpdateAllDifficulties);
return setFile(TestResources.GetTestBeatmapForImport(), extractedFolder =>
{
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(
new FileInfo(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3")),
applyToAllDifficulties);

Assert.That(Beatmap.Value.Metadata.AudioFile, Is.EqualTo(expected));
return success;
});
}

private bool setFile(string archivePath, Func<string, bool> func)
10 changes: 8 additions & 2 deletions osu.Game.Tests/Visual/UserInterface/TestSceneFormControls.cs
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Localisation;
using osu.Game.Screens.Edit.Setup;
using osuTK;

namespace osu.Game.Tests.Visual.UserInterface
@@ -89,8 +90,13 @@ public TestSceneFormControls()
},
new FormFileSelector
{
Caption = "Audio file",
PlaceholderText = "Select an audio file",
Caption = "File selector",
PlaceholderText = "Select a file",
},
new FormBeatmapFileSelector(true)
{
Caption = "File selector with intermediate choice dialog",
PlaceholderText = "Select a file",
},
new FormColourPalette
{