From 15b533f2a4dcf427b9ddfe5838e237427a7f0bbb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Sep 2020 15:06:10 +0900 Subject: [PATCH 1/9] Hash skins based on name, not skin.ini contents It is feasible that a user may be changing the contents of skin.ini without changing the skin name / author. Such changes should not create a new skin if already imported. --- osu.Game/Database/ArchiveModelManager.cs | 10 +++++++--- osu.Game/Skinning/SkinManager.cs | 8 ++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 49d7edd56c..e87ab8167a 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -253,6 +253,9 @@ namespace osu.Game.Database /// Generally should include all file types which determine the file's uniqueness. /// Large files should be avoided if possible. /// + /// + /// This is only used by the default hash implementation. If is overridden, it will not be used. + /// protected abstract string[] HashableFileTypes { get; } internal static void LogForModel(TModel model, string message, Exception e = null) @@ -271,7 +274,7 @@ namespace osu.Game.Database /// /// In the case of no matching files, a hash will be generated from the passed archive's . /// - private string computeHash(TModel item, ArchiveReader reader = null) + protected virtual string ComputeHash(TModel item, ArchiveReader reader = null) { // for now, concatenate all .osu files in the set to create a unique hash. MemoryStream hashable = new MemoryStream(); @@ -318,10 +321,11 @@ namespace osu.Game.Database LogForModel(item, "Beginning import..."); item.Files = archive != null ? createFileInfos(archive, Files) : new List(); - item.Hash = computeHash(item, archive); await Populate(item, archive, cancellationToken); + item.Hash = ComputeHash(item, archive); + using (var write = ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. { try @@ -437,7 +441,7 @@ namespace osu.Game.Database { using (ContextFactory.GetForWrite()) { - item.Hash = computeHash(item); + item.Hash = ComputeHash(item); ModelStore.Update(item); } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index e1f713882a..46130cbdd4 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -12,6 +12,7 @@ using Microsoft.EntityFrameworkCore; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; @@ -86,6 +87,13 @@ namespace osu.Game.Skinning protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name }; + protected override string ComputeHash(SkinInfo item, ArchiveReader reader = null) + { + // this is the optimal way to hash legacy skins, but will need to be reconsidered when we move forward with skin implementation. + // likely, the skin should expose a real version (ie. the version of the skin, not the skin.ini version it's targeting). + return item.ToString().ComputeSHA2Hash(); + } + protected override async Task Populate(SkinInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) { await base.Populate(model, archive, cancellationToken); From 62e5c9d2636cd1b21b064633471e0e6e9c4844d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Sep 2020 16:20:30 +0900 Subject: [PATCH 2/9] Add test coverage --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 170 ++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 osu.Game.Tests/Skins/IO/ImportSkinTest.cs diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs new file mode 100644 index 0000000000..af38d0f3c4 --- /dev/null +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -0,0 +1,170 @@ +// Copyright (c) ppy Pty Ltd . 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.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.IO.Archives; +using osu.Game.Skinning; +using osu.Game.Tests.Resources; +using SharpCompress.Archives.Zip; + +namespace osu.Game.Tests.Skins.IO +{ + public class ImportSkinTest + { + [Test] + public async Task TestBasicImport() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + { + try + { + var osu = await loadOsu(host); + + var imported = await loadIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); + + Assert.That(imported.Name, Is.EqualTo("test skin")); + Assert.That(imported.Creator, Is.EqualTo("skinner")); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public async Task TestImportTwiceWithSameMetadata() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + { + try + { + var osu = await loadOsu(host); + + var imported = await loadIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); + var imported2 = await loadIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin2.osk")); + + Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(1)); + + // the first should be overwritten by the second import. + Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public async Task TestImportTwiceWithDifferentMetadata() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + { + try + { + var osu = await loadOsu(host); + + var imported = await loadIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2", "skinner"), "skin.osk")); + var imported2 = await loadIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2.1", "skinner"), "skin2.osk")); + + Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(2)); + + Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); + } + finally + { + host.Exit(); + } + } + } + + private MemoryStream createOsk(string name, string author) + { + var zipStream = new MemoryStream(); + using var zip = ZipArchive.Create(); + zip.AddEntry("skin.ini", generateSkinIni(name, author)); + zip.SaveTo(zipStream); + return zipStream; + } + + private MemoryStream generateSkinIni(string name, string author) + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + + writer.WriteLine("[General]"); + writer.WriteLine($"Name: {name}"); + writer.WriteLine($"Author: {author}"); + writer.WriteLine(); + writer.WriteLine($"# unique {Guid.NewGuid()}"); + + writer.Flush(); + + return stream; + } + + private async Task loadIntoOsu(OsuGameBase osu, ArchiveReader archive = null) + { + var beatmapManager = osu.Dependencies.Get(); + + var skinManager = osu.Dependencies.Get(); + return await skinManager.Import(archive); + } + + private async Task loadOsu(GameHost host) + { + var osu = new OsuGameBase(); + +#pragma warning disable 4014 + Task.Run(() => host.Run(osu)); +#pragma warning restore 4014 + + waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); + + var beatmapFile = TestResources.GetTestBeatmapForImport(); + var beatmapManager = osu.Dependencies.Get(); + await beatmapManager.Import(beatmapFile); + + return osu; + } + + private void waitForOrAssert(Func result, string failureMessage, int timeout = 60000) + { + Task task = Task.Run(() => + { + while (!result()) Thread.Sleep(200); + }); + + Assert.IsTrue(task.Wait(timeout), failureMessage); + } + + private class TestArchiveReader : ArchiveReader + { + public TestArchiveReader() + : base("test_archive") + { + } + + public override Stream GetStream(string name) => new MemoryStream(); + + public override void Dispose() + { + } + + public override IEnumerable Filenames => new[] { "test_file.osr" }; + } + } +} From ef77658311f2d7471e1bcbc56f252862131e1615 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Sep 2020 16:29:14 +0900 Subject: [PATCH 3/9] Add coverage of case where skin.ini doesn't specify name/author --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 26 +++++++++++++++++++++++ osu.Game/Skinning/SkinManager.cs | 16 ++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index af38d0f3c4..ad1a41a2b3 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -66,6 +66,32 @@ namespace osu.Game.Tests.Skins.IO } } + [Test] + public async Task TestImportTwiceWithNoMetadata() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + { + try + { + var osu = await loadOsu(host); + + // if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety. + var imported = await loadIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); + var imported2 = await loadIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); + + Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(2)); + + Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); + } + finally + { + host.Exit(); + } + } + } + [Test] public async Task TestImportTwiceWithDifferentMetadata() { diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 46130cbdd4..eacfdaec4a 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -87,11 +87,19 @@ namespace osu.Game.Skinning protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name }; + private const string unknown_creator_string = "Unknown"; + protected override string ComputeHash(SkinInfo item, ArchiveReader reader = null) { - // this is the optimal way to hash legacy skins, but will need to be reconsidered when we move forward with skin implementation. - // likely, the skin should expose a real version (ie. the version of the skin, not the skin.ini version it's targeting). - return item.ToString().ComputeSHA2Hash(); + if (item.Creator != null && item.Creator != unknown_creator_string) + { + // this is the optimal way to hash legacy skins, but will need to be reconsidered when we move forward with skin implementation. + // likely, the skin should expose a real version (ie. the version of the skin, not the skin.ini version it's targeting). + return item.ToString().ComputeSHA2Hash(); + } + + // if there was no creator, the ToString above would give the filename, which along isn't really enough to base any decisions on. + return base.ComputeHash(item, reader); } protected override async Task Populate(SkinInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) @@ -108,7 +116,7 @@ namespace osu.Game.Skinning else { model.Name = model.Name.Replace(".osk", ""); - model.Creator ??= "Unknown"; + model.Creator ??= unknown_creator_string; } } From 948437865b32bafbe7af9df7c51c675f041e30d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Sep 2020 16:42:11 +0900 Subject: [PATCH 4/9] Remove unused code --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index ad1a41a2b3..14eff4c5e3 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -144,8 +143,6 @@ namespace osu.Game.Tests.Skins.IO private async Task loadIntoOsu(OsuGameBase osu, ArchiveReader archive = null) { - var beatmapManager = osu.Dependencies.Get(); - var skinManager = osu.Dependencies.Get(); return await skinManager.Import(archive); } @@ -176,21 +173,5 @@ namespace osu.Game.Tests.Skins.IO Assert.IsTrue(task.Wait(timeout), failureMessage); } - - private class TestArchiveReader : ArchiveReader - { - public TestArchiveReader() - : base("test_archive") - { - } - - public override Stream GetStream(string name) => new MemoryStream(); - - public override void Dispose() - { - } - - public override IEnumerable Filenames => new[] { "test_file.osr" }; - } } } From 91d37e0459e37f1caacfb7c623b05cd83d454848 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Sep 2020 20:17:00 +0900 Subject: [PATCH 5/9] Fix typo in comment --- osu.Game/Skinning/SkinManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index eacfdaec4a..303c59b05e 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -98,7 +98,7 @@ namespace osu.Game.Skinning return item.ToString().ComputeSHA2Hash(); } - // if there was no creator, the ToString above would give the filename, which along isn't really enough to base any decisions on. + // if there was no creator, the ToString above would give the filename, which alone isn't really enough to base any decisions on. return base.ComputeHash(item, reader); } From 1884e0167bdb5bfa1d2769e00874520b39af8c71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Sep 2020 23:31:03 +0900 Subject: [PATCH 6/9] Eagerly populate skin metadata to allow usage in hashing computation --- osu.Game/Database/ArchiveModelManager.cs | 3 +-- osu.Game/Skinning/SkinManager.cs | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index e87ab8167a..76bc4f7755 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -321,11 +321,10 @@ namespace osu.Game.Database LogForModel(item, "Beginning import..."); item.Files = archive != null ? createFileInfos(archive, Files) : new List(); + item.Hash = ComputeHash(item, archive); await Populate(item, archive, cancellationToken); - item.Hash = ComputeHash(item, archive); - using (var write = ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. { try diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 303c59b05e..ee4b7bc8e7 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -91,6 +91,10 @@ namespace osu.Game.Skinning protected override string ComputeHash(SkinInfo item, ArchiveReader reader = null) { + // we need to populate early to create a hash based off skin.ini contents + if (item.Name?.Contains(".osk") == true) + populateMetadata(item); + if (item.Creator != null && item.Creator != unknown_creator_string) { // this is the optimal way to hash legacy skins, but will need to be reconsidered when we move forward with skin implementation. @@ -106,17 +110,23 @@ namespace osu.Game.Skinning { await base.Populate(model, archive, cancellationToken); - Skin reference = GetSkin(model); + if (model.Name?.Contains(".osk") == true) + populateMetadata(model); + } + + private void populateMetadata(SkinInfo item) + { + Skin reference = GetSkin(item); if (!string.IsNullOrEmpty(reference.Configuration.SkinInfo.Name)) { - model.Name = reference.Configuration.SkinInfo.Name; - model.Creator = reference.Configuration.SkinInfo.Creator; + item.Name = reference.Configuration.SkinInfo.Name; + item.Creator = reference.Configuration.SkinInfo.Creator; } else { - model.Name = model.Name.Replace(".osk", ""); - model.Creator ??= unknown_creator_string; + item.Name = item.Name.Replace(".osk", ""); + item.Creator ??= unknown_creator_string; } } From 3529a1bfeacf3c765731871d54db2b7f01911fb4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Sep 2020 19:36:36 +0900 Subject: [PATCH 7/9] Fix global bindings being lost when running tests under headless contexts --- osu.Game/Tests/Visual/OsuTestScene.cs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index f00cefaefd..b59a1db403 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -69,12 +69,26 @@ namespace osu.Game.Tests.Visual /// protected virtual bool UseOnlineAPI => false; + /// + /// When running headless, there is an opportunity to use the host storage rather than creating a second isolated one. + /// This is because the host is recycled per TestScene execution in headless at an nunit level. + /// + private Storage isolatedHostStorage; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { + if (!UseFreshStoragePerRun) + isolatedHostStorage = (parent.Get() as HeadlessGameHost)?.Storage; + contextFactory = new Lazy(() => { var factory = new DatabaseContextFactory(LocalStorage); - factory.ResetDatabase(); + + // only reset the database if not using the host storage. + // if we reset the host storage, it will delete global key bindings. + if (isolatedHostStorage == null) + factory.ResetDatabase(); + using (var usage = factory.Get()) usage.Migrate(); return factory; @@ -135,12 +149,9 @@ namespace osu.Game.Tests.Visual } localStorage = - new Lazy(() => !UseFreshStoragePerRun && host is HeadlessGameHost ? host.Storage : new NativeStorage(Path.Combine(RuntimeInfo.StartupDirectory, $"{GetType().Name}-{Guid.NewGuid()}"))); + new Lazy(() => isolatedHostStorage ?? new NativeStorage(Path.Combine(RuntimeInfo.StartupDirectory, $"{GetType().Name}-{Guid.NewGuid()}"))); } - [Resolved] - private GameHost host { get; set; } - [Resolved] protected AudioManager Audio { get; private set; } From d2580ebc7023b9730dbf4fe4e047dcd597946803 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Sep 2020 13:01:34 +0900 Subject: [PATCH 8/9] Attempt to fix tests by avoiding clash between import tests names --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 14eff4c5e3..ef5ff0e75d 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Skins.IO [Test] public async Task TestBasicImport() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) { try { @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Skins.IO [Test] public async Task TestImportTwiceWithSameMetadata() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) { try { @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Skins.IO [Test] public async Task TestImportTwiceWithNoMetadata() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) { try { @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Skins.IO [Test] public async Task TestImportTwiceWithDifferentMetadata() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) { try { From 0b289d2e779140930d8fd90c3de13bd3b0dcef8d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Sep 2020 13:07:05 +0900 Subject: [PATCH 9/9] Add hostname differentiation to beatmap tests too --- .../Beatmaps/IO/ImportBeatmapTest.cs | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index dd3dba1274..bc6fbed07a 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Beatmaps.IO public async Task TestImportWhenClosed() { // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Beatmaps.IO public async Task TestImportThenDelete() { // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Beatmaps.IO public async Task TestImportThenImport() { // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenImportWithReZip() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -156,7 +156,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenImportWithChangedFile() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -207,7 +207,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenImportWithDifferentFilename() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -259,7 +259,7 @@ namespace osu.Game.Tests.Beatmaps.IO public async Task TestImportCorruptThenImport() { // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -301,7 +301,7 @@ namespace osu.Game.Tests.Beatmaps.IO public async Task TestRollbackOnFailure() { // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -378,7 +378,7 @@ namespace osu.Game.Tests.Beatmaps.IO public async Task TestImportThenDeleteThenImport() { // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -406,7 +406,7 @@ namespace osu.Game.Tests.Beatmaps.IO public async Task TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set) { // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(set.ToString())) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"{nameof(ImportBeatmapTest)}-{set}")) { try { @@ -440,7 +440,7 @@ namespace osu.Game.Tests.Beatmaps.IO public async Task TestImportWithDuplicateBeatmapIDs() { // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -496,8 +496,8 @@ namespace osu.Game.Tests.Beatmaps.IO [Ignore("Binding IPC on Appveyor isn't working (port in use). Need to figure out why")] public void TestImportOverIPC() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost("host", true)) - using (HeadlessGameHost client = new CleanRunHeadlessGameHost("client", true)) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"{nameof(ImportBeatmapTest)}-host", true)) + using (HeadlessGameHost client = new CleanRunHeadlessGameHost($"{nameof(ImportBeatmapTest)}-client", true)) { try { @@ -526,7 +526,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportWhenFileOpen() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -548,7 +548,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportWithDuplicateHashes() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -590,7 +590,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportNestedStructure() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -635,7 +635,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportWithIgnoredDirectoryInArchive() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -689,7 +689,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestUpdateBeatmapInfo() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -719,7 +719,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestUpdateBeatmapFile() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -761,7 +761,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public void TestCreateNewEmptyBeatmap() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try { @@ -788,7 +788,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public void TestCreateNewBeatmapWithObject() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) { try {