1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-18 16:32:55 +08:00
osu-lazer/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs

1068 lines
42 KiB
C#
Raw Normal View History

// 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.
2018-04-13 17:19:50 +08:00
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Platform;
using osu.Game.IPC;
using osu.Framework.Allocation;
2020-06-03 17:30:27 +08:00
using osu.Framework.Extensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging;
2018-04-13 17:19:50 +08:00
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.IO;
2020-09-03 18:38:01 +08:00
using osu.Game.Rulesets.Osu;
2020-01-08 11:36:07 +08:00
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Scoring;
2019-01-28 17:19:57 +08:00
using osu.Game.Tests.Resources;
using osu.Game.Tests.Scores.IO;
using osu.Game.Users;
using SharpCompress.Archives;
2018-05-29 12:48:27 +08:00
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Writers.Zip;
2020-06-03 17:30:27 +08:00
using FileInfo = System.IO.FileInfo;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Tests.Beatmaps.IO
{
[TestFixture]
2020-09-18 17:05:33 +08:00
public class ImportBeatmapTest : ImportTest
2018-04-13 17:19:50 +08:00
{
[Test]
public async Task TestImportWhenClosed()
2018-04-13 17:19:50 +08:00
{
2020-05-05 09:31:11 +08:00
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2018-04-13 17:19:50 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
await LoadOszIntoOsu(LoadOsuIntoHost(host));
2018-04-13 17:19:50 +08:00
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestImportThenDelete()
2018-04-13 17:19:50 +08:00
{
2020-05-05 09:31:11 +08:00
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2018-04-13 17:19:50 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2018-04-13 17:19:50 +08:00
var imported = await LoadOszIntoOsu(osu);
2018-04-13 17:19:50 +08:00
deleteBeatmapSet(imported, osu);
}
finally
{
host.Exit();
}
}
}
2020-12-07 17:18:14 +08:00
[Test]
public async Task TestImportThenDeleteFromStream()
{
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
{
try
{
var osu = LoadOsuIntoHost(host);
string tempPath = TestResources.GetTestBeatmapForImport();
2020-12-07 17:18:14 +08:00
var manager = osu.Dependencies.Get<BeatmapManager>();
ILive<BeatmapSetInfo> importedSet;
2020-12-07 17:18:14 +08:00
using (var stream = File.OpenRead(tempPath))
{
importedSet = await manager.Import(new ImportTask(stream, Path.GetFileName(tempPath)));
ensureLoaded(osu);
}
Assert.IsTrue(File.Exists(tempPath), "Stream source file somehow went missing");
File.Delete(tempPath);
var imported = manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID);
2020-12-07 17:18:14 +08:00
deleteBeatmapSet(imported, osu);
}
finally
{
host.Exit();
}
}
}
2018-04-13 17:19:50 +08:00
[Test]
public async Task TestImportThenImport()
2018-04-13 17:19:50 +08:00
{
2020-05-05 09:31:11 +08:00
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2018-04-13 17:19:50 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2018-04-13 17:19:50 +08:00
var imported = await LoadOszIntoOsu(osu);
var importedSecondTime = await LoadOszIntoOsu(osu);
2018-04-13 17:19:50 +08:00
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
Assert.IsTrue(imported.ID == importedSecondTime.ID);
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
2019-09-20 14:05:48 +08:00
checkBeatmapSetCount(osu, 1);
checkSingleReferencedFileCount(osu, 18);
}
finally
{
host.Exit();
}
}
}
2020-06-03 17:30:27 +08:00
[Test]
public async Task TestImportThenImportWithReZip()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2020-06-03 17:30:27 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2020-06-03 17:30:27 +08:00
string temp = TestResources.GetTestBeatmapForImport();
2020-06-03 17:30:27 +08:00
string extractedFolder = $"{temp}_extracted";
Directory.CreateDirectory(extractedFolder);
try
{
var imported = await LoadOszIntoOsu(osu);
string hashBefore = hashFile(temp);
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(extractedFolder);
using (var zip = ZipArchive.Create())
{
zip.AddAllFromDirectory(extractedFolder);
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
// zip files differ because different compression or encoder.
Assert.AreNotEqual(hashBefore, hashFile(temp));
var importedSecondTime = await osu.Dependencies.Get<BeatmapManager>().Import(new ImportTask(temp));
2020-06-03 17:30:27 +08:00
ensureLoaded(osu);
// but contents doesn't, so existing should still be used.
Assert.IsTrue(imported.ID == importedSecondTime.Value.ID);
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Value.Beatmaps.First().ID);
2020-06-03 17:30:27 +08:00
}
finally
{
Directory.Delete(extractedFolder, true);
}
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestImportThenImportWithChangedHashedFile()
2020-06-03 17:30:27 +08:00
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
{
try
{
var osu = LoadOsuIntoHost(host);
string temp = TestResources.GetTestBeatmapForImport();
string extractedFolder = $"{temp}_extracted";
Directory.CreateDirectory(extractedFolder);
try
{
var imported = await LoadOszIntoOsu(osu);
await createScoreForBeatmap(osu, imported.Beatmaps.First());
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(extractedFolder);
// arbitrary write to hashed file
// this triggers the special BeatmapManager.PreImport deletion/replacement flow.
using (var sw = new FileInfo(Directory.GetFiles(extractedFolder, "*.osu").First()).AppendText())
await sw.WriteLineAsync("// changed");
using (var zip = ZipArchive.Create())
{
zip.AddAllFromDirectory(extractedFolder);
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
var importedSecondTime = await osu.Dependencies.Get<BeatmapManager>().Import(new ImportTask(temp));
ensureLoaded(osu);
// check the newly "imported" beatmap is not the original.
Assert.IsTrue(imported.ID != importedSecondTime.Value.ID);
Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Value.Beatmaps.First().ID);
}
finally
{
Directory.Delete(extractedFolder, true);
}
}
finally
{
host.Exit();
}
}
2020-06-03 17:30:27 +08:00
}
[Test]
[Ignore("intentionally broken by import optimisations")]
public async Task TestImportThenImportWithChangedFile()
2020-06-03 17:30:27 +08:00
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2020-06-03 17:30:27 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2020-06-03 17:30:27 +08:00
string temp = TestResources.GetTestBeatmapForImport();
2020-06-03 17:30:27 +08:00
string extractedFolder = $"{temp}_extracted";
Directory.CreateDirectory(extractedFolder);
try
{
var imported = await LoadOszIntoOsu(osu);
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(extractedFolder);
// arbitrary write to non-hashed file
using (var sw = new FileInfo(Directory.GetFiles(extractedFolder, "*.mp3").First()).AppendText())
2020-06-09 23:08:48 +08:00
await sw.WriteLineAsync("text");
2020-06-03 17:30:27 +08:00
using (var zip = ZipArchive.Create())
{
zip.AddAllFromDirectory(extractedFolder);
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
var importedSecondTime = await osu.Dependencies.Get<BeatmapManager>().Import(new ImportTask(temp));
2020-06-03 17:30:27 +08:00
ensureLoaded(osu);
// check the newly "imported" beatmap is not the original.
Assert.IsTrue(imported.ID != importedSecondTime.Value.ID);
Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Value.Beatmaps.First().ID);
2020-06-03 17:30:27 +08:00
}
finally
{
Directory.Delete(extractedFolder, true);
}
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestImportThenImportWithDifferentFilename()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2020-06-03 17:30:27 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2020-06-03 17:30:27 +08:00
string temp = TestResources.GetTestBeatmapForImport();
2020-06-03 17:30:27 +08:00
string extractedFolder = $"{temp}_extracted";
Directory.CreateDirectory(extractedFolder);
try
{
var imported = await LoadOszIntoOsu(osu);
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(extractedFolder);
// change filename
var firstFile = new FileInfo(Directory.GetFiles(extractedFolder).First());
firstFile.MoveTo(Path.Combine(firstFile.DirectoryName.AsNonNull(), $"{firstFile.Name}-changed{firstFile.Extension}"));
2020-06-03 17:30:27 +08:00
using (var zip = ZipArchive.Create())
{
zip.AddAllFromDirectory(extractedFolder);
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
var importedSecondTime = await osu.Dependencies.Get<BeatmapManager>().Import(new ImportTask(temp));
2020-06-03 17:30:27 +08:00
ensureLoaded(osu);
// check the newly "imported" beatmap is not the original.
Assert.IsTrue(imported.ID != importedSecondTime.Value.ID);
Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Value.Beatmaps.First().ID);
2020-06-03 17:30:27 +08:00
}
finally
{
Directory.Delete(extractedFolder, true);
}
}
2019-09-20 14:05:48 +08:00
finally
{
host.Exit();
}
}
}
[Test]
[Ignore("intentionally broken by import optimisations")]
2019-09-20 14:05:48 +08:00
public async Task TestImportCorruptThenImport()
{
2020-05-05 09:31:11 +08:00
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2019-09-20 14:05:48 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2019-09-20 14:05:48 +08:00
var imported = await LoadOszIntoOsu(osu);
var firstFile = imported.Files.First();
var files = osu.Dependencies.Get<FileStore>();
long originalLength;
using (var stream = files.Storage.GetStream(firstFile.FileInfo.StoragePath))
originalLength = stream.Length;
using (var stream = files.Storage.GetStream(firstFile.FileInfo.StoragePath, FileAccess.Write, FileMode.Create))
stream.WriteByte(0);
var importedSecondTime = await LoadOszIntoOsu(osu);
using (var stream = files.Storage.GetStream(firstFile.FileInfo.StoragePath))
Assert.AreEqual(stream.Length, originalLength, "Corruption was not fixed on second import");
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
Assert.IsTrue(imported.ID == importedSecondTime.ID);
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
checkBeatmapSetCount(osu, 1);
checkSingleReferencedFileCount(osu, 18);
2018-05-29 12:48:27 +08:00
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestRollbackOnFailure()
2018-05-29 12:48:27 +08:00
{
2020-05-05 09:31:11 +08:00
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2018-05-29 12:48:27 +08:00
{
try
{
int itemAddRemoveFireCount = 0;
int loggedExceptionCount = 0;
Logger.NewEntry += l =>
{
if (l.Target == LoggingTarget.Database && l.Exception != null)
Interlocked.Increment(ref loggedExceptionCount);
};
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2018-05-29 12:48:27 +08:00
var manager = osu.Dependencies.Get<BeatmapManager>();
// ReSharper disable once AccessToModifiedClosure
manager.ItemUpdated.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount));
2020-05-19 15:44:22 +08:00
manager.ItemRemoved.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount));
2018-05-29 12:48:27 +08:00
var imported = await LoadOszIntoOsu(osu);
2018-05-29 12:48:27 +08:00
Assert.AreEqual(0, itemAddRemoveFireCount -= 1);
2018-05-29 12:48:27 +08:00
imported.Hash += "-changed";
manager.Update(imported);
Assert.AreEqual(0, itemAddRemoveFireCount -= 1);
2018-05-29 12:48:27 +08:00
checkBeatmapSetCount(osu, 1);
checkBeatmapCount(osu, 12);
checkSingleReferencedFileCount(osu, 18);
string brokenTempFilename = TestResources.GetTestBeatmapForImport();
2018-05-29 12:48:27 +08:00
MemoryStream brokenOsu = new MemoryStream();
MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(brokenTempFilename));
2018-05-29 12:48:27 +08:00
File.Delete(brokenTempFilename);
2018-05-29 12:48:27 +08:00
using (var outStream = File.Open(brokenTempFilename, FileMode.CreateNew))
2018-05-29 12:48:27 +08:00
using (var zip = ZipArchive.Open(brokenOsz))
{
zip.AddEntry("broken.osu", brokenOsu, false);
zip.SaveTo(outStream, CompressionType.Deflate);
2018-05-29 12:48:27 +08:00
}
// this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu.
2019-06-10 16:26:56 +08:00
try
{
await manager.Import(new ImportTask(brokenTempFilename));
2019-06-10 16:26:56 +08:00
}
catch
{
}
2018-05-29 12:48:27 +08:00
// no events should be fired in the case of a rollback.
Assert.AreEqual(0, itemAddRemoveFireCount);
2018-05-29 12:48:27 +08:00
checkBeatmapSetCount(osu, 1);
checkBeatmapCount(osu, 12);
checkSingleReferencedFileCount(osu, 18);
Assert.AreEqual(1, loggedExceptionCount);
File.Delete(brokenTempFilename);
2018-04-13 17:19:50 +08:00
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestImportThenDeleteThenImport()
2018-04-13 17:19:50 +08:00
{
2020-05-05 09:31:11 +08:00
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2018-04-13 17:19:50 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2018-04-13 17:19:50 +08:00
var imported = await LoadOszIntoOsu(osu);
2018-04-13 17:19:50 +08:00
deleteBeatmapSet(imported, osu);
var importedSecondTime = await LoadOszIntoOsu(osu);
2018-04-13 17:19:50 +08:00
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
Assert.IsTrue(imported.ID == importedSecondTime.ID);
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
}
finally
{
host.Exit();
}
}
}
2021-06-28 09:42:28 +08:00
[Test]
public async Task TestImportThenDeleteThenImportWithOnlineIDsMissing()
{
2020-05-05 09:31:11 +08:00
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
2021-06-28 09:42:28 +08:00
using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"{nameof(ImportBeatmapTest)}"))
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
var imported = await LoadOszIntoOsu(osu);
foreach (var b in imported.Beatmaps)
b.OnlineBeatmapID = null;
osu.Dependencies.Get<BeatmapManager>().Update(imported);
deleteBeatmapSet(imported, osu);
var importedSecondTime = await LoadOszIntoOsu(osu);
// check the newly "imported" beatmap has been reimported due to mismatch (even though hashes matched)
Assert.IsTrue(imported.ID != importedSecondTime.ID);
Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID);
}
finally
{
host.Exit();
}
}
}
2018-04-13 17:19:50 +08:00
[Test]
public async Task TestImportWithDuplicateBeatmapIDs()
{
2020-05-05 09:31:11 +08:00
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
var metadata = new BeatmapMetadata
{
Artist = "SomeArtist",
AuthorString = "SomeAuthor"
};
2019-03-12 14:24:35 +08:00
var difficulty = new BeatmapDifficulty();
var toImport = new BeatmapSetInfo
{
OnlineBeatmapSetID = 1,
Metadata = metadata,
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
OnlineBeatmapID = 2,
Metadata = metadata,
BaseDifficulty = difficulty
},
new BeatmapInfo
{
OnlineBeatmapID = 2,
Metadata = metadata,
Status = BeatmapSetOnlineStatus.Loved,
BaseDifficulty = difficulty
}
}
};
var manager = osu.Dependencies.Get<BeatmapManager>();
var imported = await manager.Import(toImport);
Assert.NotNull(imported);
Assert.AreEqual(null, imported.Value.Beatmaps[0].OnlineBeatmapID);
Assert.AreEqual(null, imported.Value.Beatmaps[1].OnlineBeatmapID);
}
finally
{
host.Exit();
}
}
}
[Test]
2018-04-13 17:19:50 +08:00
[NonParallelizable]
public void TestImportOverIPC()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"{nameof(ImportBeatmapTest)}-host", true))
using (HeadlessGameHost client = new CleanRunHeadlessGameHost($"{nameof(ImportBeatmapTest)}-client", true))
2018-04-13 17:19:50 +08:00
{
try
{
Assert.IsTrue(host.IsPrimaryInstance);
Assert.IsFalse(client.IsPrimaryInstance);
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2018-04-13 17:19:50 +08:00
string temp = TestResources.GetTestBeatmapForImport();
2018-04-13 17:19:50 +08:00
var importer = new ArchiveImportIPCChannel(client);
if (!importer.ImportAsync(temp).Wait(10000))
Assert.Fail(@"IPC took too long to send");
ensureLoaded(osu);
waitForOrAssert(() => !File.Exists(temp), "Temporary still exists after IPC import", 5000);
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestImportWhenFileOpen()
2018-04-13 17:19:50 +08:00
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2018-04-13 17:19:50 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
string temp = TestResources.GetTestBeatmapForImport();
2018-04-13 17:19:50 +08:00
using (File.OpenRead(temp))
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
2018-04-13 17:19:50 +08:00
ensureLoaded(osu);
File.Delete(temp);
Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't");
}
finally
{
host.Exit();
}
}
}
2019-11-14 18:15:15 +08:00
[Test]
public async Task TestImportWithDuplicateHashes()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2019-11-14 18:15:15 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2019-11-14 18:15:15 +08:00
string temp = TestResources.GetTestBeatmapForImport();
2019-11-14 18:15:15 +08:00
string extractedFolder = $"{temp}_extracted";
Directory.CreateDirectory(extractedFolder);
try
{
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(extractedFolder);
using (var zip = ZipArchive.Create())
{
zip.AddAllFromDirectory(extractedFolder);
zip.AddEntry("duplicate.osu", Directory.GetFiles(extractedFolder, "*.osu").First());
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
ensureLoaded(osu);
}
finally
{
Directory.Delete(extractedFolder, true);
}
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestImportNestedStructure()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
string temp = TestResources.GetTestBeatmapForImport();
string extractedFolder = $"{temp}_extracted";
string subfolder = Path.Combine(extractedFolder, "subfolder");
Directory.CreateDirectory(subfolder);
try
{
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(subfolder);
using (var zip = ZipArchive.Create())
{
zip.AddAllFromDirectory(extractedFolder);
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
var imported = await osu.Dependencies.Get<BeatmapManager>().Import(new ImportTask(temp));
ensureLoaded(osu);
Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("subfolder")), "Files contain common subfolder");
}
finally
{
Directory.Delete(extractedFolder, true);
}
}
finally
{
2018-04-13 17:19:50 +08:00
host.Exit();
}
}
}
[Test]
public async Task TestImportWithIgnoredDirectoryInArchive()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
string temp = TestResources.GetTestBeatmapForImport();
string extractedFolder = $"{temp}_extracted";
string dataFolder = Path.Combine(extractedFolder, "actual_data");
string resourceForkFolder = Path.Combine(extractedFolder, "__MACOSX");
string resourceForkFilePath = Path.Combine(resourceForkFolder, ".extracted");
Directory.CreateDirectory(dataFolder);
Directory.CreateDirectory(resourceForkFolder);
using (var resourceForkFile = File.CreateText(resourceForkFilePath))
{
await resourceForkFile.WriteLineAsync("adding content so that it's not empty");
}
try
{
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(dataFolder);
using (var zip = ZipArchive.Create())
{
zip.AddAllFromDirectory(extractedFolder);
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
var imported = await osu.Dependencies.Get<BeatmapManager>().Import(new ImportTask(temp));
ensureLoaded(osu);
Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("__MACOSX")), "Files contain resource fork folder, which should be ignored");
Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("actual_data")), "Files contain common subfolder");
}
finally
{
Directory.Delete(extractedFolder, true);
}
}
finally
{
2018-04-13 17:19:50 +08:00
host.Exit();
}
}
}
2020-01-02 12:20:38 +08:00
[Test]
public async Task TestUpdateBeatmapInfo()
2020-01-02 12:20:38 +08:00
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2020-01-09 17:56:45 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2020-01-09 17:56:45 +08:00
var manager = osu.Dependencies.Get<BeatmapManager>();
string temp = TestResources.GetTestBeatmapForImport();
2020-01-09 17:56:45 +08:00
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
// Update via the beatmap, not the beatmap info, to ensure correct linking
BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0];
Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap;
beatmapToUpdate.BeatmapInfo.Version = "updated";
manager.Update(setToUpdate);
BeatmapInfo updatedInfo = manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID);
Assert.That(updatedInfo.Version, Is.EqualTo("updated"));
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestUpdateBeatmapFile()
2020-01-09 17:56:45 +08:00
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2020-01-02 12:20:38 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2020-01-02 12:20:38 +08:00
var manager = osu.Dependencies.Get<BeatmapManager>();
string temp = TestResources.GetTestBeatmapForImport();
2020-01-02 12:20:38 +08:00
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0];
2020-06-08 21:35:01 +08:00
var beatmapInfo = setToUpdate.Beatmaps.First(b => b.RulesetID == 0);
2020-01-02 12:20:38 +08:00
Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap;
BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename));
string oldMd5Hash = beatmapToUpdate.BeatmapInfo.MD5Hash;
2020-01-08 11:36:07 +08:00
2020-06-08 21:35:01 +08:00
beatmapToUpdate.HitObjects.Clear();
beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 });
2020-01-02 12:20:38 +08:00
2020-06-08 21:35:01 +08:00
manager.Save(beatmapInfo, beatmapToUpdate);
2020-01-14 18:23:34 +08:00
// Check that the old file reference has been removed
Assert.That(manager.QueryBeatmapSet(s => s.ID == setToUpdate.ID).Files.All(f => f.ID != fileToUpdate.ID));
2020-01-10 11:39:45 +08:00
// Check that the new file is referenced correctly by attempting a retrieval
Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap;
Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1));
Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000));
Assert.That(updatedBeatmap.BeatmapInfo.MD5Hash, Is.Not.EqualTo(oldMd5Hash));
}
finally
{
host.Exit();
2020-09-03 18:38:01 +08:00
}
}
}
// TODO: needs to be pulled across to realm implementation when this file is nuked.
[Test]
public void TestSaveRemovesInvalidCharactersFromPath()
{
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
{
try
{
var osu = LoadOsuIntoHost(host);
var manager = osu.Dependencies.Get<BeatmapManager>();
var working = manager.CreateNew(new OsuRuleset().RulesetInfo, User.SYSTEM_USER);
var beatmap = working.Beatmap;
beatmap.BeatmapInfo.Version = "difficulty";
beatmap.BeatmapInfo.Metadata = new BeatmapMetadata
{
Artist = "Artist/With\\Slashes",
Title = "Title",
AuthorString = "mapper",
};
manager.Save(beatmap.BeatmapInfo, working.Beatmap);
Assert.AreEqual("Artist_With_Slashes - Title (mapper) [difficulty].osu", beatmap.BeatmapInfo.Path);
}
finally
{
host.Exit();
}
}
}
2020-09-03 18:38:01 +08:00
[Test]
public void TestCreateNewEmptyBeatmap()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2020-09-03 18:38:01 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2020-09-03 18:38:01 +08:00
var manager = osu.Dependencies.Get<BeatmapManager>();
var working = manager.CreateNew(new OsuRuleset().RulesetInfo, User.SYSTEM_USER);
2020-09-03 18:38:01 +08:00
manager.Save(working.BeatmapInfo, working.Beatmap);
var retrievedSet = manager.GetAllUsableBeatmapSets()[0];
// Check that the new file is referenced correctly by attempting a retrieval
Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(retrievedSet.Beatmaps[0]).Beatmap;
Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(0));
}
finally
{
host.Exit();
}
}
}
[Test]
public void TestCreateNewBeatmapWithObject()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
2020-09-03 18:38:01 +08:00
{
try
{
2020-09-18 17:05:33 +08:00
var osu = LoadOsuIntoHost(host);
2020-09-03 18:38:01 +08:00
var manager = osu.Dependencies.Get<BeatmapManager>();
var working = manager.CreateNew(new OsuRuleset().RulesetInfo, User.SYSTEM_USER);
2020-09-03 18:38:01 +08:00
((Beatmap)working.Beatmap).HitObjects.Add(new HitCircle { StartTime = 5000 });
manager.Save(working.BeatmapInfo, working.Beatmap);
var retrievedSet = manager.GetAllUsableBeatmapSets()[0];
// Check that the new file is referenced correctly by attempting a retrieval
Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(retrievedSet.Beatmaps[0]).Beatmap;
Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1));
Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000));
}
finally
{
host.Exit();
2020-01-02 12:20:38 +08:00
}
}
}
public static async Task<BeatmapSetInfo> LoadQuickOszIntoOsu(OsuGameBase osu)
{
string temp = TestResources.GetQuickTestBeatmapForImport();
var manager = osu.Dependencies.Get<BeatmapManager>();
var importedSet = await manager.Import(new ImportTask(temp)).ConfigureAwait(false);
ensureLoaded(osu);
waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000);
return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID);
}
2019-10-10 16:39:41 +08:00
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
2018-05-29 10:57:11 +08:00
{
string temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
2018-04-13 17:19:50 +08:00
var manager = osu.Dependencies.Get<BeatmapManager>();
var importedSet = await manager.Import(new ImportTask(temp)).ConfigureAwait(false);
2018-04-13 17:19:50 +08:00
ensureLoaded(osu);
waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000);
return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID);
2018-04-13 17:19:50 +08:00
}
private void deleteBeatmapSet(BeatmapSetInfo imported, OsuGameBase osu)
{
var manager = osu.Dependencies.Get<BeatmapManager>();
manager.Delete(imported);
checkBeatmapSetCount(osu, 0);
checkBeatmapSetCount(osu, 1, true);
checkSingleReferencedFileCount(osu, 0);
2018-04-13 17:19:50 +08:00
Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending);
}
2021-10-02 23:55:29 +08:00
private static Task createScoreForBeatmap(OsuGameBase osu, BeatmapInfo beatmapInfo)
{
return ImportScoreTest.LoadScoreIntoOsu(osu, new ScoreInfo
{
OnlineScoreID = 2,
BeatmapInfo = beatmapInfo,
2021-10-02 23:55:29 +08:00
BeatmapInfoID = beatmapInfo.ID
}, new ImportScoreTest.TestArchiveReader());
}
2021-06-23 16:37:24 +08:00
private static void checkBeatmapSetCount(OsuGameBase osu, int expected, bool includeDeletePending = false)
{
var manager = osu.Dependencies.Get<BeatmapManager>();
Assert.AreEqual(expected, includeDeletePending
? manager.QueryBeatmapSets(_ => true).ToList().Count
: manager.GetAllUsableBeatmapSets().Count);
}
2021-06-23 16:37:24 +08:00
private static string hashFile(string filename)
{
using (var s = File.OpenRead(filename))
return s.ComputeMD5Hash();
}
2021-06-23 16:37:24 +08:00
private static void checkBeatmapCount(OsuGameBase osu, int expected)
{
Assert.AreEqual(expected, osu.Dependencies.Get<BeatmapManager>().QueryBeatmaps(_ => true).ToList().Count);
}
2021-06-23 16:37:24 +08:00
private static void checkSingleReferencedFileCount(OsuGameBase osu, int expected)
{
Assert.AreEqual(expected, osu.Dependencies.Get<FileStore>().QueryFiles(f => f.ReferenceCount == 1).Count());
}
private static void ensureLoaded(OsuGameBase osu, int timeout = 60000)
2018-04-13 17:19:50 +08:00
{
IEnumerable<BeatmapSetInfo> resultSets = null;
var store = osu.Dependencies.Get<BeatmapManager>();
waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any(),
@"BeatmapSet did not import to the database in allocated time.", timeout);
2020-05-05 09:31:11 +08:00
// ensure we were stored to beatmap database backing...
2018-04-13 17:19:50 +08:00
Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1).");
IEnumerable<BeatmapInfo> queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0);
IEnumerable<BeatmapSetInfo> queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526);
2020-05-05 09:31:11 +08:00
// if we don't re-check here, the set will be inserted but the beatmaps won't be present yet.
2018-04-13 17:19:50 +08:00
waitForOrAssert(() => queryBeatmaps().Count() == 12,
@"Beatmaps did not import to the database in allocated time", timeout);
waitForOrAssert(() => queryBeatmapSets().Count() == 1,
@"BeatmapSet did not import to the database in allocated time", timeout);
int countBeatmapSetBeatmaps = 0;
int countBeatmaps = 0;
waitForOrAssert(() =>
(countBeatmapSetBeatmaps = queryBeatmapSets().First().Beatmaps.Count) ==
(countBeatmaps = queryBeatmaps().Count()),
$@"Incorrect database beatmap count post-import ({countBeatmaps} but should be {countBeatmapSetBeatmaps}).", timeout);
var set = queryBeatmapSets().First();
foreach (BeatmapInfo b in set.Beatmaps)
Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID));
Assert.IsTrue(set.Beatmaps.Count > 0);
var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
2018-04-19 19:44:38 +08:00
Assert.IsTrue(beatmap?.HitObjects.Any() == true);
2018-04-13 17:19:50 +08:00
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap;
2018-04-19 19:44:38 +08:00
Assert.IsTrue(beatmap?.HitObjects.Any() == true);
2018-04-13 17:19:50 +08:00
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap;
2018-04-19 19:44:38 +08:00
Assert.IsTrue(beatmap?.HitObjects.Any() == true);
2018-04-13 17:19:50 +08:00
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap;
2018-04-19 19:44:38 +08:00
Assert.IsTrue(beatmap?.HitObjects.Any() == true);
2018-04-13 17:19:50 +08:00
}
private static void waitForOrAssert(Func<bool> result, string failureMessage, int timeout = 60000)
2018-04-13 17:19:50 +08:00
{
2018-06-04 21:25:18 +08:00
Task task = Task.Run(() =>
2018-04-13 17:19:50 +08:00
{
while (!result()) Thread.Sleep(200);
2018-06-04 21:25:18 +08:00
});
2018-04-13 17:19:50 +08:00
2018-06-04 21:25:18 +08:00
Assert.IsTrue(task.Wait(timeout), failureMessage);
2018-04-13 17:19:50 +08:00
}
}
}