2020-09-11 15:20:30 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2022-06-17 15:37:17 +08:00
#nullable disable
2020-09-11 15:20:30 +08:00
using System ;
using System.IO ;
using System.Linq ;
2021-10-20 15:48:32 +08:00
using System.Runtime.CompilerServices ;
2020-09-11 15:20:30 +08:00
using System.Threading.Tasks ;
using NUnit.Framework ;
using osu.Framework.Allocation ;
2022-01-03 16:31:12 +08:00
using osu.Framework.Extensions ;
2020-09-11 15:20:30 +08:00
using osu.Framework.Platform ;
2021-11-29 16:57:17 +08:00
using osu.Game.Database ;
2022-04-11 22:57:15 +08:00
using osu.Game.Extensions ;
2021-10-20 16:03:30 +08:00
using osu.Game.IO ;
2020-09-11 15:20:30 +08:00
using osu.Game.Skinning ;
using SharpCompress.Archives.Zip ;
namespace osu.Game.Tests.Skins.IO
{
2020-09-18 17:05:33 +08:00
public class ImportSkinTest : ImportTest
2020-09-11 15:20:30 +08:00
{
2021-10-20 15:48:32 +08:00
#region Testing filename metadata inclusion
2020-09-11 15:20:30 +08:00
[Test]
2021-10-20 15:48:32 +08:00
public Task TestSingleImportDifferentFilename ( ) = > runSkinTest ( async osu = >
2020-09-11 15:20:30 +08:00
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "test skin" , "skinner" ) , "skin.osk" ) ) ;
2020-09-11 15:20:30 +08:00
2021-10-20 15:48:32 +08:00
// When the import filename doesn't match, it should be appended (and update the skin.ini).
2021-10-20 16:12:44 +08:00
assertCorrectMetadata ( import1 , "test skin [skin]" , "skinner" , osu ) ;
2021-10-20 15:48:32 +08:00
} ) ;
2020-09-11 15:20:30 +08:00
2021-11-01 12:55:45 +08:00
[Test]
public Task TestSingleImportWeirdIniFileCase ( ) = > runSkinTest ( async osu = >
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "test skin" , "skinner" , iniFilename : "Skin.InI" ) , "skin.osk" ) ) ;
2021-11-01 12:55:45 +08:00
// When the import filename doesn't match, it should be appended (and update the skin.ini).
assertCorrectMetadata ( import1 , "test skin [skin]" , "skinner" , osu ) ;
} ) ;
2021-11-02 13:04:25 +08:00
[Test]
public Task TestSingleImportMissingSectionHeader ( ) = > runSkinTest ( async osu = >
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "test skin" , "skinner" , includeSectionHeader : false ) , "skin.osk" ) ) ;
2021-11-02 13:04:25 +08:00
// When the import filename doesn't match, it should be appended (and update the skin.ini).
assertCorrectMetadata ( import1 , "test skin [skin]" , "skinner" , osu ) ;
} ) ;
2020-09-11 15:20:30 +08:00
[Test]
2021-10-20 15:48:32 +08:00
public Task TestSingleImportMatchingFilename ( ) = > runSkinTest ( async osu = >
2020-09-11 15:20:30 +08:00
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "test skin" , "skinner" ) , "test skin.osk" ) ) ;
2020-09-11 15:20:30 +08:00
2021-10-20 15:48:32 +08:00
// When the import filename matches it shouldn't be appended.
2021-10-20 16:12:44 +08:00
assertCorrectMetadata ( import1 , "test skin" , "skinner" , osu ) ;
2021-10-20 16:03:30 +08:00
} ) ;
[Test]
public Task TestSingleImportNoIniFile ( ) = > runSkinTest ( async osu = >
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithNonIniFile ( ) , "test skin.osk" ) ) ;
2021-10-20 16:03:30 +08:00
// When the import filename matches it shouldn't be appended.
2021-10-20 16:12:44 +08:00
assertCorrectMetadata ( import1 , "test skin" , "Unknown" , osu ) ;
2021-10-20 16:03:30 +08:00
} ) ;
[Test]
2021-10-21 12:35:26 +08:00
public Task TestEmptyImportImportsWithFilename ( ) = > runSkinTest ( async osu = >
2021-10-20 16:03:30 +08:00
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createEmptyOsk ( ) , "test skin.osk" ) ) ;
2021-10-20 16:03:30 +08:00
2021-10-21 12:35:26 +08:00
// When the import filename matches it shouldn't be appended.
assertCorrectMetadata ( import1 , "test skin" , "Unknown" , osu ) ;
2021-10-20 15:48:32 +08:00
} ) ;
2020-09-11 15:20:30 +08:00
2021-10-20 15:48:32 +08:00
#endregion
2020-09-11 15:20:30 +08:00
2021-10-20 15:48:32 +08:00
#region Cases where imports should match existing
2021-10-20 14:22:47 +08:00
[Test]
2021-10-20 15:48:32 +08:00
public Task TestImportTwiceWithSameMetadataAndFilename ( ) = > runSkinTest ( async osu = >
2021-10-20 14:22:47 +08:00
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "test skin" , "skinner" ) , "skin.osk" ) ) ;
var import2 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "test skin" , "skinner" ) , "skin.osk" ) ) ;
2021-10-20 14:22:47 +08:00
2021-10-20 16:12:44 +08:00
assertImportedOnce ( import1 , import2 ) ;
2021-10-20 15:48:32 +08:00
} ) ;
2021-10-20 14:22:47 +08:00
2021-10-20 15:48:32 +08:00
[Test]
public Task TestImportTwiceWithNoMetadataSameDownloadFilename ( ) = > runSkinTest ( async osu = >
{
// 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.
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( string . Empty , string . Empty ) , "download.osk" ) ) ;
var import2 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( string . Empty , string . Empty ) , "download.osk" ) ) ;
2021-10-20 14:22:47 +08:00
2021-10-20 16:12:44 +08:00
assertImportedOnce ( import1 , import2 ) ;
2021-10-20 15:48:32 +08:00
} ) ;
2020-09-11 15:20:30 +08:00
2020-09-11 15:29:14 +08:00
[Test]
2021-10-20 15:48:32 +08:00
public Task TestImportUpperCasedOskArchive ( ) = > runSkinTest ( async osu = >
2020-09-11 15:29:14 +08:00
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "name 1" , "author 1" ) , "name 1.OsK" ) ) ;
2021-10-20 16:12:44 +08:00
assertCorrectMetadata ( import1 , "name 1" , "author 1" , osu ) ;
2021-10-20 16:03:30 +08:00
2022-06-20 14:14:57 +08:00
var import2 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "name 1" , "author 1" ) , "name 1.oSK" ) ) ;
2021-10-20 16:03:30 +08:00
2021-10-20 16:12:44 +08:00
assertImportedOnce ( import1 , import2 ) ;
2021-10-20 16:03:30 +08:00
} ) ;
2020-09-11 15:29:14 +08:00
2022-04-11 22:57:15 +08:00
[Test]
public Task TestImportExportedSkinFilename ( ) = > runSkinTest ( async osu = >
{
MemoryStream exportStream = new MemoryStream ( ) ;
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "name 1" , "author 1" ) , "custom.osk" ) ) ;
2022-04-11 22:57:15 +08:00
assertCorrectMetadata ( import1 , "name 1 [custom]" , "author 1" , osu ) ;
import1 . PerformRead ( s = >
{
new LegacySkinExporter ( osu . Dependencies . Get < Storage > ( ) ) . ExportModelTo ( s , exportStream ) ;
} ) ;
string exportFilename = import1 . GetDisplayString ( ) ;
2022-06-20 14:14:57 +08:00
var import2 = await loadSkinIntoOsu ( osu , new ImportTask ( exportStream , $"{exportFilename}.osk" ) ) ;
2022-04-11 22:57:15 +08:00
assertCorrectMetadata ( import2 , "name 1 [custom]" , "author 1" , osu ) ;
assertImportedOnce ( import1 , import2 ) ;
} ) ;
2021-10-20 16:03:30 +08:00
[Test]
public Task TestSameMetadataNameSameFolderName ( ) = > runSkinTest ( async osu = >
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "name 1" , "author 1" ) , "my custom skin 1" ) ) ;
var import2 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "name 1" , "author 1" ) , "my custom skin 1" ) ) ;
2020-09-11 15:29:14 +08:00
2021-10-20 16:12:44 +08:00
assertImportedOnce ( import1 , import2 ) ;
assertCorrectMetadata ( import1 , "name 1 [my custom skin 1]" , "author 1" , osu ) ;
2021-10-20 15:48:32 +08:00
} ) ;
2020-09-11 15:29:14 +08:00
2021-10-20 15:48:32 +08:00
#endregion
2021-10-22 10:03:28 +08:00
#region Cases where imports should be uniquely imported
2020-09-11 15:29:14 +08:00
2020-09-11 15:20:30 +08:00
[Test]
2021-10-20 15:48:32 +08:00
public Task TestImportTwiceWithSameMetadataButDifferentFilename ( ) = > runSkinTest ( async osu = >
2020-09-11 15:20:30 +08:00
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "test skin" , "skinner" ) , "skin.osk" ) ) ;
var import2 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "test skin" , "skinner" ) , "skin2.osk" ) ) ;
2020-09-11 15:20:30 +08:00
2021-10-20 16:12:44 +08:00
assertImportedBoth ( import1 , import2 ) ;
2021-10-20 15:48:32 +08:00
} ) ;
2020-09-11 15:20:30 +08:00
2021-10-20 15:48:32 +08:00
[Test]
public Task TestImportTwiceWithNoMetadataDifferentDownloadFilename ( ) = > runSkinTest ( async osu = >
{
// 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.
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( string . Empty , string . Empty ) , "download.osk" ) ) ;
var import2 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( string . Empty , string . Empty ) , "download2.osk" ) ) ;
2020-09-11 15:20:30 +08:00
2021-10-20 16:12:44 +08:00
assertImportedBoth ( import1 , import2 ) ;
2021-10-20 15:48:32 +08:00
} ) ;
2020-09-11 15:20:30 +08:00
2021-03-25 03:55:15 +08:00
[Test]
2021-10-20 15:48:32 +08:00
public Task TestImportTwiceWithSameFilenameDifferentMetadata ( ) = > runSkinTest ( async osu = >
2021-03-25 03:55:15 +08:00
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "test skin v2" , "skinner" ) , "skin.osk" ) ) ;
var import2 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "test skin v2.1" , "skinner" ) , "skin.osk" ) ) ;
2021-03-25 03:55:15 +08:00
2021-10-20 16:12:44 +08:00
assertImportedBoth ( import1 , import2 ) ;
assertCorrectMetadata ( import1 , "test skin v2 [skin]" , "skinner" , osu ) ;
assertCorrectMetadata ( import2 , "test skin v2.1 [skin]" , "skinner" , osu ) ;
2021-10-20 15:48:32 +08:00
} ) ;
2021-03-25 03:55:15 +08:00
2021-10-20 15:48:32 +08:00
[Test]
public Task TestSameMetadataNameDifferentFolderName ( ) = > runSkinTest ( async osu = >
{
2022-06-20 14:14:57 +08:00
var import1 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "name 1" , "author 1" ) , "my custom skin 1" ) ) ;
var import2 = await loadSkinIntoOsu ( osu , new ImportTask ( createOskWithIni ( "name 1" , "author 1" ) , "my custom skin 2" ) ) ;
2021-03-25 03:55:15 +08:00
2021-10-20 16:12:44 +08:00
assertImportedBoth ( import1 , import2 ) ;
assertCorrectMetadata ( import1 , "name 1 [my custom skin 1]" , "author 1" , osu ) ;
assertCorrectMetadata ( import2 , "name 1 [my custom skin 2]" , "author 1" , osu ) ;
2021-10-20 15:48:32 +08:00
} ) ;
2021-03-25 03:55:15 +08:00
2021-12-02 17:00:04 +08:00
[Test]
public Task TestExportThenImportDefaultSkin ( ) = > runSkinTest ( osu = >
{
var skinManager = osu . Dependencies . Get < SkinManager > ( ) ;
skinManager . EnsureMutableSkin ( ) ;
MemoryStream exportStream = new MemoryStream ( ) ;
Guid originalSkinId = skinManager . CurrentSkinInfo . Value . ID ;
skinManager . CurrentSkinInfo . Value . PerformRead ( s = >
{
Assert . IsFalse ( s . Protected ) ;
Assert . AreEqual ( typeof ( DefaultSkin ) , s . CreateInstance ( skinManager ) . GetType ( ) ) ;
new LegacySkinExporter ( osu . Dependencies . Get < Storage > ( ) ) . ExportModelTo ( s , exportStream ) ;
Assert . Greater ( exportStream . Length , 0 ) ;
} ) ;
var imported = skinManager . Import ( new ImportTask ( exportStream , "exported.osk" ) ) ;
2022-01-06 21:54:43 +08:00
imported . GetResultSafely ( ) . PerformRead ( s = >
2021-12-02 17:00:04 +08:00
{
Assert . IsFalse ( s . Protected ) ;
Assert . AreNotEqual ( originalSkinId , s . ID ) ;
Assert . AreEqual ( typeof ( DefaultSkin ) , s . CreateInstance ( skinManager ) . GetType ( ) ) ;
} ) ;
return Task . CompletedTask ;
} ) ;
[Test]
public Task TestExportThenImportClassicSkin ( ) = > runSkinTest ( osu = >
{
var skinManager = osu . Dependencies . Get < SkinManager > ( ) ;
skinManager . CurrentSkinInfo . Value = skinManager . DefaultLegacySkin . SkinInfo ;
skinManager . EnsureMutableSkin ( ) ;
MemoryStream exportStream = new MemoryStream ( ) ;
Guid originalSkinId = skinManager . CurrentSkinInfo . Value . ID ;
skinManager . CurrentSkinInfo . Value . PerformRead ( s = >
{
Assert . IsFalse ( s . Protected ) ;
Assert . AreEqual ( typeof ( DefaultLegacySkin ) , s . CreateInstance ( skinManager ) . GetType ( ) ) ;
new LegacySkinExporter ( osu . Dependencies . Get < Storage > ( ) ) . ExportModelTo ( s , exportStream ) ;
Assert . Greater ( exportStream . Length , 0 ) ;
} ) ;
var imported = skinManager . Import ( new ImportTask ( exportStream , "exported.osk" ) ) ;
2022-01-06 21:54:43 +08:00
imported . GetResultSafely ( ) . PerformRead ( s = >
2021-12-02 17:00:04 +08:00
{
Assert . IsFalse ( s . Protected ) ;
Assert . AreNotEqual ( originalSkinId , s . ID ) ;
Assert . AreEqual ( typeof ( DefaultLegacySkin ) , s . CreateInstance ( skinManager ) . GetType ( ) ) ;
} ) ;
return Task . CompletedTask ;
} ) ;
2021-10-20 15:48:32 +08:00
#endregion
2021-03-25 03:55:15 +08:00
2022-01-26 12:37:33 +08:00
private void assertCorrectMetadata ( Live < SkinInfo > import1 , string name , string creator , OsuGameBase osu )
2021-08-23 19:25:46 +08:00
{
2021-11-29 16:57:17 +08:00
import1 . PerformRead ( i = >
{
Assert . That ( i . Name , Is . EqualTo ( name ) ) ;
Assert . That ( i . Creator , Is . EqualTo ( creator ) ) ;
2021-10-20 16:03:30 +08:00
2021-11-29 16:57:17 +08:00
// for extra safety let's reconstruct the skin, reading from the skin.ini.
var instance = i . CreateInstance ( ( IStorageResourceProvider ) osu . Dependencies . Get ( typeof ( SkinManager ) ) ) ;
2021-10-20 16:03:30 +08:00
2021-11-29 16:57:17 +08:00
Assert . That ( instance . Configuration . SkinInfo . Name , Is . EqualTo ( name ) ) ;
Assert . That ( instance . Configuration . SkinInfo . Creator , Is . EqualTo ( creator ) ) ;
} ) ;
2021-10-20 15:48:32 +08:00
}
2021-08-23 19:25:46 +08:00
2022-01-26 12:37:33 +08:00
private void assertImportedBoth ( Live < SkinInfo > import1 , Live < SkinInfo > import2 )
2021-10-20 15:48:32 +08:00
{
2021-11-29 16:57:17 +08:00
import1 . PerformRead ( i1 = > import2 . PerformRead ( i2 = >
{
Assert . That ( i2 . ID , Is . Not . EqualTo ( i1 . ID ) ) ;
Assert . That ( i2 . Hash , Is . Not . EqualTo ( i1 . Hash ) ) ;
Assert . That ( i2 . Files . First ( ) , Is . Not . EqualTo ( i1 . Files . First ( ) ) ) ;
} ) ) ;
2021-10-20 15:48:32 +08:00
}
2021-08-23 19:25:46 +08:00
2022-01-26 12:37:33 +08:00
private void assertImportedOnce ( Live < SkinInfo > import1 , Live < SkinInfo > import2 )
2021-10-20 15:48:32 +08:00
{
2021-11-29 16:57:17 +08:00
import1 . PerformRead ( i1 = > import2 . PerformRead ( i2 = >
{
Assert . That ( i2 . ID , Is . EqualTo ( i1 . ID ) ) ;
Assert . That ( i2 . Hash , Is . EqualTo ( i1 . Hash ) ) ;
Assert . That ( i2 . Files . First ( ) , Is . EqualTo ( i1 . Files . First ( ) ) ) ;
} ) ) ;
2021-08-23 19:25:46 +08:00
}
2021-10-20 16:03:30 +08:00
private MemoryStream createEmptyOsk ( )
{
var zipStream = new MemoryStream ( ) ;
using var zip = ZipArchive . Create ( ) ;
zip . SaveTo ( zipStream ) ;
return zipStream ;
}
private MemoryStream createOskWithNonIniFile ( )
{
var zipStream = new MemoryStream ( ) ;
using var zip = ZipArchive . Create ( ) ;
zip . AddEntry ( "hitcircle.png" , new MemoryStream ( new byte [ ] { 0 , 1 , 2 , 3 } ) ) ;
zip . SaveTo ( zipStream ) ;
return zipStream ;
}
2021-11-02 13:04:25 +08:00
private MemoryStream createOskWithIni ( string name , string author , bool makeUnique = false , string iniFilename = @"skin.ini" , bool includeSectionHeader = true )
2020-09-11 15:20:30 +08:00
{
var zipStream = new MemoryStream ( ) ;
using var zip = ZipArchive . Create ( ) ;
2021-11-02 13:04:25 +08:00
zip . AddEntry ( iniFilename , generateSkinIni ( name , author , makeUnique , includeSectionHeader ) ) ;
2020-09-11 15:20:30 +08:00
zip . SaveTo ( zipStream ) ;
return zipStream ;
}
2021-11-02 13:04:25 +08:00
private MemoryStream generateSkinIni ( string name , string author , bool makeUnique = true , bool includeSectionHeader = true )
2020-09-11 15:20:30 +08:00
{
var stream = new MemoryStream ( ) ;
var writer = new StreamWriter ( stream ) ;
2021-11-02 13:04:25 +08:00
if ( includeSectionHeader )
writer . WriteLine ( "[General]" ) ;
2020-09-11 15:20:30 +08:00
writer . WriteLine ( $"Name: {name}" ) ;
writer . WriteLine ( $"Author: {author}" ) ;
2021-08-23 19:25:46 +08:00
if ( makeUnique )
{
writer . WriteLine ( ) ;
writer . WriteLine ( $"# unique {Guid.NewGuid()}" ) ;
}
2020-09-11 15:20:30 +08:00
writer . Flush ( ) ;
return stream ;
}
2021-10-20 15:48:32 +08:00
private async Task runSkinTest ( Func < OsuGameBase , Task > action , [ CallerMemberName ] string callingMethodName = @"" )
{
2021-12-24 19:17:20 +08:00
using ( HeadlessGameHost host = new CleanRunHeadlessGameHost ( callingMethodName : callingMethodName ) )
2021-10-20 15:48:32 +08:00
{
try
{
var osu = LoadOsuIntoHost ( host ) ;
await action ( osu ) ;
}
finally
{
host . Exit ( ) ;
}
}
}
2022-06-20 14:14:57 +08:00
private async Task < Live < SkinInfo > > loadSkinIntoOsu ( OsuGameBase osu , ImportTask import = null )
2020-09-11 15:20:30 +08:00
{
var skinManager = osu . Dependencies . Get < SkinManager > ( ) ;
2022-06-20 14:14:57 +08:00
return await skinManager . Import ( import ) ;
2020-09-11 15:20:30 +08:00
}
}
}