From 7679b9e3cf1da4aad78ecd605b55d2e84674f527 Mon Sep 17 00:00:00 2001 From: paulober <44974737+paulober@users.noreply.github.com> Date: Tue, 23 Dec 2025 21:19:35 +0100 Subject: [PATCH 1/9] Add support for Composer Icons Signed-off-by: paulober <44974737+paulober@users.noreply.github.com> --- msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs | 34 +++++++++------- .../TaskTests/ACToolTaskTest.cs | 40 +++++++++++++++++++ 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs index f7dfbcd5a93d..111f8c21184a 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs @@ -257,7 +257,7 @@ void FindXCAssetsDirectory (string main, string secondary, out string mainResult mainResult = main; secondaryResult = secondary; - while (!string.IsNullOrEmpty (mainResult) && !mainResult.EndsWith (".xcassets", StringComparison.OrdinalIgnoreCase)) { + while (!string.IsNullOrEmpty (mainResult) && !mainResult.EndsWith (".xcassets", StringComparison.OrdinalIgnoreCase) && !mainResult.EndsWith (".icon", StringComparison.OrdinalIgnoreCase)) { mainResult = Path.GetDirectoryName (mainResult)!; if (!string.IsNullOrEmpty (secondaryResult)) secondaryResult = Path.GetDirectoryName (secondaryResult)!; @@ -296,13 +296,13 @@ public override bool Execute () var vpath = BundleResource.GetVirtualProjectPath (this, imageAsset); var catalogFullPath = imageAsset.GetMetadata ("FullPath"); - // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, etc) + // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, .icon, etc) var catalog = Path.GetDirectoryName (vpath); catalogFullPath = Path.GetDirectoryName (catalogFullPath); var assetType = Path.GetExtension (catalog).TrimStart ('.'); - // keep walking up the directory structure until we get to the .xcassets directory + // keep walking up the directory structure until we get to the .xcassets or .icon directory FindXCAssetsDirectory (catalog, catalogFullPath, out var catalog2, out var catalogFullPath2); catalog = catalog2; catalogFullPath = catalogFullPath2; @@ -329,11 +329,11 @@ public override bool Execute () continue; } - // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these - if (Path.GetFileName (vpath) != "Contents.json") - continue; - - items.Add (asset); + // Handle both Contents.json (for .xcassets) and icon.json (for .icon folders) + var fileName = Path.GetFileName (vpath); + if (fileName == "Contents.json" || fileName == "icon.json") { + items.Add (asset); + } } // clone any *.xcassets dirs that need cloning @@ -374,8 +374,9 @@ public override bool Execute () File.Copy (src, dest, true); - // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these - if (Path.GetFileName (vpath) != "Contents.json") + // Handle both Contents.json (for .xcassets) and icon.json (for .icon folders) + var fileName = Path.GetFileName (vpath); + if (fileName != "Contents.json" && fileName != "icon.json") continue; item = new TaskItem (dest); @@ -384,8 +385,9 @@ public override bool Execute () FindXCAssetsDirectory (Path.GetFullPath (dest), "", out var catalogFullPath, out var _); items.Add (new AssetInfo (item, vpath, asset.Catalog, catalogFullPath, asset.AssetType)); } else { - // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these - if (Path.GetFileName (vpath) != "Contents.json") + // Handle both Contents.json (for .xcassets) and icon.json (for .icon folders) + var fileName = Path.GetFileName (vpath); + if (fileName != "Contents.json" && fileName != "icon.json") continue; items.Add (asset); @@ -393,7 +395,7 @@ public override bool Execute () } } - // Note: `items` contains only the Contents.json files at this point + // Note: `items` contains only the Contents.json and icon.json files at this point for (int i = 0; i < items.Count; i++) { var asset = items [i]; var assetItem = asset.Item; @@ -409,8 +411,9 @@ public override bool Execute () brandAssetsInAssets.Add (Path.GetFileNameWithoutExtension (Path.GetDirectoryName (vpath))); } } else { - if (assetType.Equals ("appiconset", StringComparison.OrdinalIgnoreCase)) + if (assetType.Equals ("appiconset", StringComparison.OrdinalIgnoreCase) || assetType.Equals ("icon", StringComparison.OrdinalIgnoreCase)) { appIconsInAssets.Add (Path.GetFileNameWithoutExtension (Path.GetDirectoryName (vpath))); + } } if (unique.Add (catalog)) { @@ -420,7 +423,8 @@ public override bool Execute () catalogs.Add (item); } - if (SdkPlatform != "WatchSimulator") { + // Only process Contents.json files for on-demand resources (not icon.json files) + if (SdkPlatform != "WatchSimulator" && Path.GetFileName (vpath) == "Contents.json") { var text = File.ReadAllText (assetItem.ItemSpec); if (string.IsNullOrEmpty (text)) diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs index 75f52139b7af..acbb82abd4a1 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs @@ -634,5 +634,45 @@ public void XSAppIconAssetsAndAppIcon (ApplePlatform platform) ExecuteTask (actool, 1); Assert.AreEqual ("Can't specify both 'XSAppIconAssets' in the Info.plist and 'AppIcon' in the project file. Please select one or the other.", Engine.Logger.ErrorEvents [0].Message, "Error message"); } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.MacOSX)] + public void IconFileSupport (ApplePlatform platform) + { + // Test that .icon folders (Icon Composer format) are recognized as app icons + var projectDir = Cache.CreateTemporaryDirectory (); + var iconFolderPath = Path.Combine (projectDir, "Resources", "AppIcon.icon"); + var assetsPath = Path.Combine (iconFolderPath, "Assets"); + Directory.CreateDirectory (assetsPath); + + // Create a dummy icon.json file (simplified structure for testing) + var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); + File.WriteAllText (iconJsonPath, @"{""version"":1}"); + + // Create a dummy image file in the Assets folder + var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); + File.WriteAllText (imagePath, "dummy image"); + + var actool = CreateACToolTask ( + platform, + projectDir, + out var _, + iconJsonPath + "|Resources/AppIcon.icon/icon.json", + imagePath + "|Resources/AppIcon.icon/Assets/icon_512x512.png" + ); + actool.AppIcon = "AppIcon"; + + // The task should recognize AppIcon as a valid icon + // Note: This will fail at actool execution since we don't have a real .icon structure, + // but we're testing that the icon is recognized in the validation phase + ExecuteTask (actool, expectedErrorCount: 1); // Expected to fail at actool execution + + // Verify that no error was logged about the icon not being found + var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); + Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), + "Should not report that AppIcon is not found among image resources"); + } } } From 8d29605d445255e924cc3267b2b65b5581263065 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 Feb 2026 11:48:34 +0100 Subject: [PATCH 2/9] [msbuild] Improve support for Composer Icons (.icon) - Add .icon glob patterns to ImageAsset auto-include and BundleResource exclusion in Microsoft.Sdk.DefaultItems.template.props - Fix tvOS support: register .icon assets in both brandAssetsInAssets and imageStacksInAssets for primary/alternate icon validation - Add tvOS test case to IconFileSupport - Add tests: IconFileSupportWithIncludeAllAppIcons, IconFileSupportAsAlternateIcon, InexistentIconFile, MixedXCAssetsAndIconFile - Update DefaultCompilationIncludes.md and build-items.md documentation Fixes #24132 --- docs/building-apps/build-items.md | 3 +- dotnet/DefaultCompilationIncludes.md | 9 +- .../Microsoft.Sdk.DefaultItems.template.props | 3 +- msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs | 5 +- .../TaskTests/ACToolTaskTest.cs | 171 +++++++++++++++++- 5 files changed, 183 insertions(+), 8 deletions(-) diff --git a/docs/building-apps/build-items.md b/docs/building-apps/build-items.md index ff18c071b9e6..2fd81202cf18 100644 --- a/docs/building-apps/build-items.md +++ b/docs/building-apps/build-items.md @@ -186,7 +186,8 @@ Only applicable to iOS and tvOS projects. ## ImageAsset -An item group that contains image assets. +An item group that contains image assets, including files inside asset catalogs +(\*.xcassets) and Icon Composer directories (\*.icon). ## InterfaceDefinition diff --git a/dotnet/DefaultCompilationIncludes.md b/dotnet/DefaultCompilationIncludes.md index 3dec5cdb0a5f..9f14799e6634 100644 --- a/dotnet/DefaultCompilationIncludes.md +++ b/dotnet/DefaultCompilationIncludes.md @@ -33,6 +33,13 @@ All \*.pdf, \*.jpg, \*.png and \*.json files inside asset catalogs (\*.xcassets) in the project directory or any subdirectory are included by default (as `ImageAsset` items). +## Icon Composer files + +All files inside Icon Composer directories (\*.icon) in the project directory +or any subdirectory are included by default (as `ImageAsset` items). Icon +Composer files are created by Xcode's Icon Composer tool (Xcode 26+) and +contain layered app icons with `icon.json` metadata. + ## Atlas Textures All \*.png files inside \*.atlas directories in the project directory or any @@ -52,7 +59,7 @@ included by default (as `Metal` items). All files in the Resources/ subdirectory, except any items in the `Compile` or `EmbeddedResource` item groups, and except the ones mentioned above -(\*.scnassets, \*.storyboard, \*.xib, \*.xcassets, \*.atlas, \*.mlmodel, +(\*.scnassets, \*.storyboard, \*.xib, \*.xcassets, \*.icon, \*.atlas, \*.mlmodel, \*.metal) are included by default (as `BundleResource` items). [1]: https://docs.microsoft.com/en-us/dotnet/core/tools/csproj#default-compilation-includes-in-net-core-projects diff --git a/dotnet/targets/Microsoft.Sdk.DefaultItems.template.props b/dotnet/targets/Microsoft.Sdk.DefaultItems.template.props index d705ae3f56c2..afc08fe18531 100644 --- a/dotnet/targets/Microsoft.Sdk.DefaultItems.template.props +++ b/dotnet/targets/Microsoft.Sdk.DefaultItems.template.props @@ -24,7 +24,7 @@ - + $([MSBuild]::MakeRelative ('$(MSBuildProjectDirectory)', '%(FullPath)')) false true @@ -65,6 +65,7 @@ $(DefaultItemExcludes); $(DefaultExcludesInProjectFolder); $(_ResourcePrefix)\**\*.xcassets\**\*.*; + $(_ResourcePrefix)\**\*.icon\**\*.*; $(_ResourcePrefix)\**\*.storyboard;**\*.xib; $(_ResourcePrefix)\**\*.atlas\*.png; $(_ResourcePrefix)\**\*.mlmodel; diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs index 111f8c21184a..e90031218586 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/ACTool.cs @@ -405,9 +405,10 @@ public override bool Execute () var assetType = asset.AssetType; if (Platform == ApplePlatform.TVOS) { - if (assetType.Equals ("imagestack", StringComparison.OrdinalIgnoreCase)) { + if (assetType.Equals ("imagestack", StringComparison.OrdinalIgnoreCase) || assetType.Equals ("icon", StringComparison.OrdinalIgnoreCase)) { imageStacksInAssets.Add (Path.GetFileNameWithoutExtension (Path.GetDirectoryName (vpath))); - } else if (assetType.Equals ("brandassets", StringComparison.OrdinalIgnoreCase)) { + } + if (assetType.Equals ("brandassets", StringComparison.OrdinalIgnoreCase) || assetType.Equals ("icon", StringComparison.OrdinalIgnoreCase)) { brandAssetsInAssets.Add (Path.GetFileNameWithoutExtension (Path.GetDirectoryName (vpath))); } } else { diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs index acbb82abd4a1..b18b5b438fc0 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs @@ -637,6 +637,7 @@ public void XSAppIconAssetsAndAppIcon (ApplePlatform platform) [Test] [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] [TestCase (ApplePlatform.MacCatalyst)] [TestCase (ApplePlatform.MacOSX)] public void IconFileSupport (ApplePlatform platform) @@ -646,11 +647,11 @@ public void IconFileSupport (ApplePlatform platform) var iconFolderPath = Path.Combine (projectDir, "Resources", "AppIcon.icon"); var assetsPath = Path.Combine (iconFolderPath, "Assets"); Directory.CreateDirectory (assetsPath); - + // Create a dummy icon.json file (simplified structure for testing) var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); File.WriteAllText (iconJsonPath, @"{""version"":1}"); - + // Create a dummy image file in the Assets folder var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); File.WriteAllText (imagePath, "dummy image"); @@ -671,8 +672,172 @@ public void IconFileSupport (ApplePlatform platform) // Verify that no error was logged about the icon not being found var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); - Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), + Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), "Should not report that AppIcon is not found among image resources"); } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.MacOSX)] + public void IconFileSupportWithIncludeAllAppIcons (ApplePlatform platform) + { + // Test that .icon folders work with IncludeAllAppIcons + var projectDir = Cache.CreateTemporaryDirectory (); + var iconFolderPath = Path.Combine (projectDir, "Resources", "AppIcon.icon"); + var assetsPath = Path.Combine (iconFolderPath, "Assets"); + Directory.CreateDirectory (assetsPath); + + var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); + File.WriteAllText (iconJsonPath, @"{""version"":1}"); + + var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); + File.WriteAllText (imagePath, "dummy image"); + + var actool = CreateACToolTask ( + platform, + projectDir, + out var _, + iconJsonPath + "|Resources/AppIcon.icon/icon.json", + imagePath + "|Resources/AppIcon.icon/Assets/icon_512x512.png" + ); + actool.AppIcon = "AppIcon"; + actool.IncludeAllAppIcons = true; + + ExecuteTask (actool, expectedErrorCount: 1); + + var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); + Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), + "Should not report that AppIcon is not found among image resources"); + } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.MacOSX)] + public void IconFileSupportAsAlternateIcon (ApplePlatform platform) + { + // Test that .icon folders work as alternate app icons + var projectDir = Cache.CreateTemporaryDirectory (); + var iconFolderPath = Path.Combine (projectDir, "Resources", "AlternateIcon.icon"); + var assetsPath = Path.Combine (iconFolderPath, "Assets"); + Directory.CreateDirectory (assetsPath); + + var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); + File.WriteAllText (iconJsonPath, @"{""version"":1}"); + + var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); + File.WriteAllText (imagePath, "dummy image"); + + // Also need a primary icon for the alternate icon test to make sense + var primaryIconPath = Path.Combine (projectDir, "Resources", "AppIcon.icon"); + var primaryAssetsPath = Path.Combine (primaryIconPath, "Assets"); + Directory.CreateDirectory (primaryAssetsPath); + + var primaryIconJsonPath = Path.Combine (primaryIconPath, "icon.json"); + File.WriteAllText (primaryIconJsonPath, @"{""version"":1}"); + + var primaryImagePath = Path.Combine (primaryAssetsPath, "icon_512x512.png"); + File.WriteAllText (primaryImagePath, "dummy image"); + + var actool = CreateACToolTask ( + platform, + projectDir, + out var _, + iconJsonPath + "|Resources/AlternateIcon.icon/icon.json", + imagePath + "|Resources/AlternateIcon.icon/Assets/icon_512x512.png", + primaryIconJsonPath + "|Resources/AppIcon.icon/icon.json", + primaryImagePath + "|Resources/AppIcon.icon/Assets/icon_512x512.png" + ); + actool.AppIcon = "AppIcon"; + actool.AlternateAppIcons = new ITaskItem [] { new TaskItem ("AlternateIcon") }; + + ExecuteTask (actool, expectedErrorCount: 1); + + var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); + Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AlternateAppIcon")), + "Should not report that AlternateIcon is not found among image resources"); + Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), + "Should not report that AppIcon is not found among image resources"); + } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.MacOSX)] + public void InexistentIconFile (ApplePlatform platform) + { + // Test that an inexistent .icon-based app icon is correctly reported + var projectDir = Cache.CreateTemporaryDirectory (); + var iconFolderPath = Path.Combine (projectDir, "Resources", "AppIcon.icon"); + var assetsPath = Path.Combine (iconFolderPath, "Assets"); + Directory.CreateDirectory (assetsPath); + + var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); + File.WriteAllText (iconJsonPath, @"{""version"":1}"); + + var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); + File.WriteAllText (imagePath, "dummy image"); + + var actool = CreateACToolTask ( + platform, + projectDir, + out var _, + iconJsonPath + "|Resources/AppIcon.icon/icon.json", + imagePath + "|Resources/AppIcon.icon/Assets/icon_512x512.png" + ); + actool.AppIcon = "InexistentIcon"; + + ExecuteTask (actool, 1); + + var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); + Assert.IsTrue (errorMessages.Any (m => m.Contains ("Can't find the AppIcon 'InexistentIcon'")), + "Should report that InexistentIcon is not found among image resources"); + } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.MacOSX)] + public void MixedXCAssetsAndIconFile (ApplePlatform platform) + { + // Test that .icon folders and .xcassets can coexist in the validation phase + var projectDir = Path.Combine (Configuration.SourceRoot, "tests", "dotnet", "AppWithXCAssets", platform.AsString ()); + var files = Directory.GetFiles (Path.Combine (projectDir, "Resources", "Images.xcassets"), "*", SearchOption.AllDirectories); + var imageAssets = files.Select (v => v + "|" + v.Substring (projectDir.Length + 1)).ToList (); + + // Add a .icon folder alongside the existing .xcassets + var tmpDir = Cache.CreateTemporaryDirectory (); + var iconFolderPath = Path.Combine (tmpDir, "ComposerIcon.icon"); + var assetsPath = Path.Combine (iconFolderPath, "Assets"); + Directory.CreateDirectory (assetsPath); + + var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); + File.WriteAllText (iconJsonPath, @"{""version"":1}"); + + var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); + File.WriteAllText (imagePath, "dummy image"); + + imageAssets.Add (iconJsonPath + "|Resources/ComposerIcon.icon/icon.json"); + imageAssets.Add (imagePath + "|Resources/ComposerIcon.icon/Assets/icon_512x512.png"); + + var actool = CreateACToolTask ( + platform, + projectDir, + out var _, + imageAssets.ToArray () + ); + actool.AppIcon = "AppIcons"; + + // actool may fail on the dummy .icon content, but the validation phase should pass + actool.Execute (); + + var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); + Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), + "Should not report that AppIcon is not found when mixing .xcassets and .icon"); + } } } From 4d27b7f0355c1c74c7857984cca9ad14373968c7 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 Feb 2026 12:16:12 +0100 Subject: [PATCH 3/9] Add AppWithComposerIcon test project and ComposerIcon integration test Add a test project that uses .icon (Xcode Icon Composer) directories instead of .xcassets for app icons. The test builds the project for all platforms and verifies that raw .icon files don't end up in the app bundle as BundleResources (they should be processed by actool). --- .../dotnet/AppWithComposerIcon/AppDelegate.cs | 35 ++++++++++++++++++ .../MacCatalyst/AppWithComposerIcon.csproj | 7 ++++ .../Resources/AppIcon.icon/Assets/back.png | Bin 0 -> 5705 bytes .../Resources/AppIcon.icon/Assets/front.png | Bin 0 -> 5705 bytes .../Resources/AppIcon.icon/icon.json | 19 ++++++++++ .../iOS/AppWithComposerIcon.csproj | 7 ++++ .../Resources/AppIcon.icon/Assets/back.png | Bin 0 -> 5705 bytes .../Resources/AppIcon.icon/Assets/front.png | Bin 0 -> 5705 bytes .../iOS/Resources/AppIcon.icon/icon.json | 19 ++++++++++ .../macOS/AppWithComposerIcon.csproj | 7 ++++ .../Resources/AppIcon.icon/Assets/back.png | Bin 0 -> 5705 bytes .../Resources/AppIcon.icon/Assets/front.png | Bin 0 -> 5705 bytes .../macOS/Resources/AppIcon.icon/icon.json | 19 ++++++++++ .../dotnet/AppWithComposerIcon/shared.csproj | 23 ++++++++++++ .../tvOS/AppWithComposerIcon.csproj | 7 ++++ .../Resources/AppIcon.icon/Assets/back.png | Bin 0 -> 5705 bytes .../Resources/AppIcon.icon/Assets/front.png | Bin 0 -> 5705 bytes .../tvOS/Resources/AppIcon.icon/icon.json | 19 ++++++++++ tests/dotnet/UnitTests/AppIconTest.cs | 30 +++++++++++++++ 19 files changed, 192 insertions(+) create mode 100644 tests/dotnet/AppWithComposerIcon/AppDelegate.cs create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/Assets/back.png create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/Assets/front.png create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/Assets/back.png create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/Assets/front.png create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/Assets/back.png create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/Assets/front.png create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json create mode 100644 tests/dotnet/AppWithComposerIcon/shared.csproj create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/Assets/back.png create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/Assets/front.png create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json diff --git a/tests/dotnet/AppWithComposerIcon/AppDelegate.cs b/tests/dotnet/AppWithComposerIcon/AppDelegate.cs new file mode 100644 index 000000000000..2ec34ad0f408 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/AppDelegate.cs @@ -0,0 +1,35 @@ +using System; +using Foundation; + +#if !__MACOS__ +using UIKit; +#endif + +#nullable enable + +namespace AppWithComposerIcon { +#if !(__MACCATALYST__ || __MACOS__) +public class AppDelegate : UIApplicationDelegate { +public override bool FinishedLaunching (UIApplication app, NSDictionary options) +{ +return true; +} +} +#endif + +public class Program { +static int Main (string [] args) +{ +#if __MACCATALYST__ || __MACOS__ +GC.KeepAlive (typeof (NSObject)); // prevent linking away the platform assembly + +Console.WriteLine (Environment.GetEnvironmentVariable ("MAGIC_WORD")); + +return args.Length; +#else +UIApplication.Main (args, null, typeof (AppDelegate)); +return 0; +#endif +} +} +} diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj new file mode 100644 index 000000000000..b55a3971d948 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj @@ -0,0 +1,7 @@ + + + +net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst + + + diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/Assets/back.png b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/Assets/back.png new file mode 100644 index 0000000000000000000000000000000000000000..00c6bc6118f0ee916acf51845aec89a4b11970e4 GIT binary patch literal 5705 zcmeAS@N?(olHy`uVBq!ia0y~yU;#2&7&zE~RK2WrGXsN|fTxRNNX4zUHyi~)QjRlI zgu=ABJQBWbWPMk8aVj%V1VLs3NEHLf7KQ{6fsaX?i4ZH<8aRN4 zC4fxCXC#sWl1v8~i=-D1vw=~838A`K@c zFu-bQWjG080r46sE+@foq;MpXNh8Ba323ldBArXqSfr`pBn0%JNg@dh9SlG~g41ai zjiVI;C@PTRqJdEXRJRU-k|Y7>3}lxfITAaQcBusDj=^9!2{{H5LW3X{A>l@X%V`zR z382(OE5k_%=)qDSGz_Aq-)Mz^-YH>g5CEpv1Ry513?wBD(kMTUW_I*w94wiAv_8O^ zIz2jNnIT<)V$2mi~mG|00P0prw_C zGfA@~OKzuFm|_Dr87{LgbMMXW?B3JK@@SOrrfu*ptFv*mAtAdpTDGfU_D@nH- zu1Y8m4d8+q(=en!#fCRQ=Anp2uBK4n5~8*A1EXl`7aKzRP?DN4)hp7+cMX)(V4(RJ zCwp5)Et5U=182!==m+t?fX;4H(+Oct3$ThZ;ZA^T+`He-x3M3z$kL!<><27LM+xWR e+^J}Ej&;}h!)5t8$NPUks2cA8 literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json new file mode 100644 index 000000000000..2fcee2039b37 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json @@ -0,0 +1,19 @@ +{ + "version" : 1, + "layers" : [ + { + "filename" : "back.png", + "size" : { + "width" : 1024, + "height" : 1024 + } + }, + { + "filename" : "front.png", + "size" : { + "width" : 1024, + "height" : 1024 + } + } + ] +} diff --git a/tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj new file mode 100644 index 000000000000..0586c0e98058 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj @@ -0,0 +1,7 @@ + + + +net$(BundledNETCoreAppTargetFrameworkVersion)-ios + + + diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/Assets/back.png b/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/Assets/back.png new file mode 100644 index 0000000000000000000000000000000000000000..00c6bc6118f0ee916acf51845aec89a4b11970e4 GIT binary patch literal 5705 zcmeAS@N?(olHy`uVBq!ia0y~yU;#2&7&zE~RK2WrGXsN|fTxRNNX4zUHyi~)QjRlI zgu=ABJQBWbWPMk8aVj%V1VLs3NEHLf7KQ{6fsaX?i4ZH<8aRN4 zC4fxCXC#sWl1v8~i=-D1vw=~838A`K@c zFu-bQWjG080r46sE+@foq;MpXNh8Ba323ldBArXqSfr`pBn0%JNg@dh9SlG~g41ai zjiVI;C@PTRqJdEXRJRU-k|Y7>3}lxfITAaQcBusDj=^9!2{{H5LW3X{A>l@X%V`zR z382(OE5k_%=)qDSGz_Aq-)Mz^-YH>g5CEpv1Ry513?wBD(kMTUW_I*w94wiAv_8O^ zIz2jNnIT<)V$2mi~mG|00P0prw_C zGfA@~OKzuFm|_Dr87{LgbMMXW?B3JK@@SOrrfu*ptFv*mAtAdpTDGfU_D@nH- zu1Y8m4d8+q(=en!#fCRQ=Anp2uBK4n5~8*A1EXl`7aKzRP?DN4)hp7+cMX)(V4(RJ zCwp5)Et5U=182!==m+t?fX;4H(+Oct3$ThZ;ZA^T+`He-x3M3z$kL!<><27LM+xWR e+^J}Ej&;}h!)5t8$NPUks2cA8 literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json new file mode 100644 index 000000000000..2fcee2039b37 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json @@ -0,0 +1,19 @@ +{ + "version" : 1, + "layers" : [ + { + "filename" : "back.png", + "size" : { + "width" : 1024, + "height" : 1024 + } + }, + { + "filename" : "front.png", + "size" : { + "width" : 1024, + "height" : 1024 + } + } + ] +} diff --git a/tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj new file mode 100644 index 000000000000..912322842db6 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj @@ -0,0 +1,7 @@ + + + +net$(BundledNETCoreAppTargetFrameworkVersion)-macos + + + diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/Assets/back.png b/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/Assets/back.png new file mode 100644 index 0000000000000000000000000000000000000000..00c6bc6118f0ee916acf51845aec89a4b11970e4 GIT binary patch literal 5705 zcmeAS@N?(olHy`uVBq!ia0y~yU;#2&7&zE~RK2WrGXsN|fTxRNNX4zUHyi~)QjRlI zgu=ABJQBWbWPMk8aVj%V1VLs3NEHLf7KQ{6fsaX?i4ZH<8aRN4 zC4fxCXC#sWl1v8~i=-D1vw=~838A`K@c zFu-bQWjG080r46sE+@foq;MpXNh8Ba323ldBArXqSfr`pBn0%JNg@dh9SlG~g41ai zjiVI;C@PTRqJdEXRJRU-k|Y7>3}lxfITAaQcBusDj=^9!2{{H5LW3X{A>l@X%V`zR z382(OE5k_%=)qDSGz_Aq-)Mz^-YH>g5CEpv1Ry513?wBD(kMTUW_I*w94wiAv_8O^ zIz2jNnIT<)V$2mi~mG|00P0prw_C zGfA@~OKzuFm|_Dr87{LgbMMXW?B3JK@@SOrrfu*ptFv*mAtAdpTDGfU_D@nH- zu1Y8m4d8+q(=en!#fCRQ=Anp2uBK4n5~8*A1EXl`7aKzRP?DN4)hp7+cMX)(V4(RJ zCwp5)Et5U=182!==m+t?fX;4H(+Oct3$ThZ;ZA^T+`He-x3M3z$kL!<><27LM+xWR e+^J}Ej&;}h!)5t8$NPUks2cA8 literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json new file mode 100644 index 000000000000..2fcee2039b37 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json @@ -0,0 +1,19 @@ +{ + "version" : 1, + "layers" : [ + { + "filename" : "back.png", + "size" : { + "width" : 1024, + "height" : 1024 + } + }, + { + "filename" : "front.png", + "size" : { + "width" : 1024, + "height" : 1024 + } + } + ] +} diff --git a/tests/dotnet/AppWithComposerIcon/shared.csproj b/tests/dotnet/AppWithComposerIcon/shared.csproj new file mode 100644 index 000000000000..72954b94ed0b --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/shared.csproj @@ -0,0 +1,23 @@ + + + +Exe + +AppWithComposerIcon +com.xamarin.appwithcomposericon + +true + + + + + +AppIcon + + + + + + + + diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj new file mode 100644 index 000000000000..8319610bec72 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj @@ -0,0 +1,7 @@ + + + +net$(BundledNETCoreAppTargetFrameworkVersion)-tvos + + + diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/Assets/back.png b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/Assets/back.png new file mode 100644 index 0000000000000000000000000000000000000000..00c6bc6118f0ee916acf51845aec89a4b11970e4 GIT binary patch literal 5705 zcmeAS@N?(olHy`uVBq!ia0y~yU;#2&7&zE~RK2WrGXsN|fTxRNNX4zUHyi~)QjRlI zgu=ABJQBWbWPMk8aVj%V1VLs3NEHLf7KQ{6fsaX?i4ZH<8aRN4 zC4fxCXC#sWl1v8~i=-D1vw=~838A`K@c zFu-bQWjG080r46sE+@foq;MpXNh8Ba323ldBArXqSfr`pBn0%JNg@dh9SlG~g41ai zjiVI;C@PTRqJdEXRJRU-k|Y7>3}lxfITAaQcBusDj=^9!2{{H5LW3X{A>l@X%V`zR z382(OE5k_%=)qDSGz_Aq-)Mz^-YH>g5CEpv1Ry513?wBD(kMTUW_I*w94wiAv_8O^ zIz2jNnIT<)V$2mi~mG|00P0prw_C zGfA@~OKzuFm|_Dr87{LgbMMXW?B3JK@@SOrrfu*ptFv*mAtAdpTDGfU_D@nH- zu1Y8m4d8+q(=en!#fCRQ=Anp2uBK4n5~8*A1EXl`7aKzRP?DN4)hp7+cMX)(V4(RJ zCwp5)Et5U=182!==m+t?fX;4H(+Oct3$ThZ;ZA^T+`He-x3M3z$kL!<><27LM+xWR e+^J}Ej&;}h!)5t8$NPUks2cA8 literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json new file mode 100644 index 000000000000..2fcee2039b37 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json @@ -0,0 +1,19 @@ +{ + "version" : 1, + "layers" : [ + { + "filename" : "back.png", + "size" : { + "width" : 1024, + "height" : 1024 + } + }, + { + "filename" : "front.png", + "size" : { + "width" : 1024, + "height" : 1024 + } + } + ] +} diff --git a/tests/dotnet/UnitTests/AppIconTest.cs b/tests/dotnet/UnitTests/AppIconTest.cs index e8bea97b7c7e..49c00113ff3f 100644 --- a/tests/dotnet/UnitTests/AppIconTest.cs +++ b/tests/dotnet/UnitTests/AppIconTest.cs @@ -672,5 +672,35 @@ void TestXCAssetsImpl (ApplePlatform platform, string runtimeIdentifiers, Dictio throw; } } + + [TestCase (ApplePlatform.iOS, "iossimulator-x64")] + [TestCase (ApplePlatform.TVOS, "tvossimulator-x64")] + [TestCase (ApplePlatform.MacCatalyst, "maccatalyst-x64")] + [TestCase (ApplePlatform.MacOSX, "osx-x64")] + public void ComposerIcon (ApplePlatform platform, string runtimeIdentifiers) + { + Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers); + Configuration.IgnoreIfIgnoredPlatform (platform); + + var project = "AppWithComposerIcon"; + var projectPath = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath); + Clean (projectPath); + + var properties = GetDefaultProperties (runtimeIdentifiers); + DotNet.AssertBuild (projectPath, properties); + + var resourcesDirectory = GetResourcesDirectory (platform, appPath); + + // Verify that the raw .icon files are not in the app bundle as BundleResources + var iconJsonInBundle = Path.Combine (resourcesDirectory, "AppIcon.icon", "icon.json"); + Assert.That (iconJsonInBundle, Does.Not.Exist, "icon.json should not be in the app bundle as a raw BundleResource"); + + var frontPngInBundle = Path.Combine (resourcesDirectory, "AppIcon.icon", "Assets", "front.png"); + Assert.That (frontPngInBundle, Does.Not.Exist, "front.png should not be in the app bundle as a raw BundleResource"); + + var backPngInBundle = Path.Combine (resourcesDirectory, "AppIcon.icon", "Assets", "back.png"); + Assert.That (backPngInBundle, Does.Not.Exist, "back.png should not be in the app bundle as a raw BundleResource"); + } } } + From 3dad65e5cfff4ff6cc005b2f64475a82231e0029 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 Feb 2026 12:51:33 +0100 Subject: [PATCH 4/9] Add .xcassets fallback and graceful skip for icon export failure Add a basic .xcassets directory alongside .icon in the test project so the build can succeed even when actool's icon export subprocess fails (status 255 on some systems). The test gracefully skips with Assert.Ignore when icon export is not supported. --- .../Resources/Images.xcassets/Contents.json | 6 ++++++ .../Images.xcassets/Image.imageset/Contents.json | 12 ++++++++++++ .../Images.xcassets/Image.imageset/Icon16.png | Bin 0 -> 83 bytes .../iOS/Resources/Images.xcassets/Contents.json | 6 ++++++ .../Images.xcassets/Image.imageset/Contents.json | 12 ++++++++++++ .../Images.xcassets/Image.imageset/Icon16.png | Bin 0 -> 83 bytes .../macOS/Resources/Images.xcassets/Contents.json | 6 ++++++ .../Images.xcassets/Image.imageset/Contents.json | 12 ++++++++++++ .../Images.xcassets/Image.imageset/Icon16.png | Bin 0 -> 83 bytes .../tvOS/Resources/Images.xcassets/Contents.json | 6 ++++++ .../Images.xcassets/Image.imageset/Contents.json | 12 ++++++++++++ .../Images.xcassets/Image.imageset/Icon16.png | Bin 0 -> 83 bytes tests/dotnet/UnitTests/AppIconTest.cs | 11 ++++++++++- 13 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Contents.json create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Contents.json create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Icon16.png create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Contents.json create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Contents.json create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Icon16.png create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Contents.json create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Contents.json create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Icon16.png create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Contents.json create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Contents.json create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Icon16.png diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Contents.json b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Contents.json new file mode 100644 index 000000000000..73c00596a7fc --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Contents.json b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Contents.json new file mode 100644 index 000000000000..103bde20f61c --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Icon16.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Icon16.png b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..a07ad0dab3fd27e91d90db830d434828fa8066af GIT binary patch literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ql2i3Ar-fhJ!Y0YvR9pvc%>nb e*^O6152z$A^PoJRjO1ycDh5wiKbLh*2~7aq;uG@# literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Contents.json b/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Contents.json new file mode 100644 index 000000000000..73c00596a7fc --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Contents.json b/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Contents.json new file mode 100644 index 000000000000..103bde20f61c --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Icon16.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Icon16.png b/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..a07ad0dab3fd27e91d90db830d434828fa8066af GIT binary patch literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ql2i3Ar-fhJ!Y0YvR9pvc%>nb e*^O6152z$A^PoJRjO1ycDh5wiKbLh*2~7aq;uG@# literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Contents.json b/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Contents.json new file mode 100644 index 000000000000..73c00596a7fc --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Contents.json b/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Contents.json new file mode 100644 index 000000000000..103bde20f61c --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Icon16.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Icon16.png b/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..a07ad0dab3fd27e91d90db830d434828fa8066af GIT binary patch literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ql2i3Ar-fhJ!Y0YvR9pvc%>nb e*^O6152z$A^PoJRjO1ycDh5wiKbLh*2~7aq;uG@# literal 0 HcmV?d00001 diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Contents.json b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Contents.json new file mode 100644 index 000000000000..73c00596a7fc --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Contents.json b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Contents.json new file mode 100644 index 000000000000..103bde20f61c --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Icon16.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Icon16.png b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..a07ad0dab3fd27e91d90db830d434828fa8066af GIT binary patch literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ql2i3Ar-fhJ!Y0YvR9pvc%>nb e*^O6152z$A^PoJRjO1ycDh5wiKbLh*2~7aq;uG@# literal 0 HcmV?d00001 diff --git a/tests/dotnet/UnitTests/AppIconTest.cs b/tests/dotnet/UnitTests/AppIconTest.cs index 49c00113ff3f..5b7479c83279 100644 --- a/tests/dotnet/UnitTests/AppIconTest.cs +++ b/tests/dotnet/UnitTests/AppIconTest.cs @@ -687,7 +687,16 @@ public void ComposerIcon (ApplePlatform platform, string runtimeIdentifiers) Clean (projectPath); var properties = GetDefaultProperties (runtimeIdentifiers); - DotNet.AssertBuild (projectPath, properties); + var rv = DotNet.Execute ("build", projectPath, properties, assert_success: false); + if (rv.ExitCode != 0) { + var errors = BinLog.GetBuildLogErrors (rv.BinLogPath).ToArray (); + // If the only error is that the partial-info.plist wasn't generated, + // it's because the icon export subprocess failed. This can happen + // on some systems where the icon composer format isn't fully supported. + if (errors.Length == 1 && errors [0].Message?.Contains ("Partial Info.plist file was not generated") == true) + Assert.Ignore ("Icon Composer export is not supported on this system (actool icon export failed)."); + Assert.Fail ($"Build failed with {errors.Length} error(s):\n{string.Join ("\n", errors.Select (v => v.Message))}"); + } var resourcesDirectory = GetResourcesDirectory (platform, appPath); From c58f712201abc25363e8615173cce063e6c2bc20 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 Feb 2026 12:59:43 +0100 Subject: [PATCH 5/9] Fix indentation in csproj files to use tabs --- .../MacCatalyst/AppWithComposerIcon.csproj | 8 +++--- .../iOS/AppWithComposerIcon.csproj | 8 +++--- .../macOS/AppWithComposerIcon.csproj | 8 +++--- .../dotnet/AppWithComposerIcon/shared.csproj | 28 +++++++++---------- .../tvOS/AppWithComposerIcon.csproj | 8 +++--- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj index b55a3971d948..6b0e2c773180 100644 --- a/tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj +++ b/tests/dotnet/AppWithComposerIcon/MacCatalyst/AppWithComposerIcon.csproj @@ -1,7 +1,7 @@ - -net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst - - + + net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst + + diff --git a/tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj index 0586c0e98058..86d408734aa8 100644 --- a/tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj +++ b/tests/dotnet/AppWithComposerIcon/iOS/AppWithComposerIcon.csproj @@ -1,7 +1,7 @@ - -net$(BundledNETCoreAppTargetFrameworkVersion)-ios - - + + net$(BundledNETCoreAppTargetFrameworkVersion)-ios + + diff --git a/tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj index 912322842db6..a77287b9ba00 100644 --- a/tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj +++ b/tests/dotnet/AppWithComposerIcon/macOS/AppWithComposerIcon.csproj @@ -1,7 +1,7 @@ - -net$(BundledNETCoreAppTargetFrameworkVersion)-macos - - + + net$(BundledNETCoreAppTargetFrameworkVersion)-macos + + diff --git a/tests/dotnet/AppWithComposerIcon/shared.csproj b/tests/dotnet/AppWithComposerIcon/shared.csproj index 72954b94ed0b..702960b9dd8e 100644 --- a/tests/dotnet/AppWithComposerIcon/shared.csproj +++ b/tests/dotnet/AppWithComposerIcon/shared.csproj @@ -1,23 +1,23 @@ - -Exe + + Exe -AppWithComposerIcon -com.xamarin.appwithcomposericon + AppWithComposerIcon + com.xamarin.appwithcomposericon -true - + true + - + - -AppIcon - + + AppIcon + - - + + - - + + diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj b/tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj index 8319610bec72..bd487ddcd88d 100644 --- a/tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj +++ b/tests/dotnet/AppWithComposerIcon/tvOS/AppWithComposerIcon.csproj @@ -1,7 +1,7 @@ - -net$(BundledNETCoreAppTargetFrameworkVersion)-tvos - - + + net$(BundledNETCoreAppTargetFrameworkVersion)-tvos + + From 24c7f45d1b56ea16eec06c7bdf257e531ec0736d Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 Feb 2026 13:00:44 +0100 Subject: [PATCH 6/9] Add Makefile logic to AppWithComposerIcon test project --- tests/dotnet/AppWithComposerIcon/MacCatalyst/Makefile | 1 + tests/dotnet/AppWithComposerIcon/iOS/Makefile | 1 + tests/dotnet/AppWithComposerIcon/macOS/Makefile | 1 + tests/dotnet/AppWithComposerIcon/shared.mk | 3 +++ tests/dotnet/AppWithComposerIcon/tvOS/Makefile | 1 + 5 files changed, 7 insertions(+) create mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Makefile create mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Makefile create mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Makefile create mode 100644 tests/dotnet/AppWithComposerIcon/shared.mk create mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Makefile diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Makefile b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Makefile b/tests/dotnet/AppWithComposerIcon/iOS/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/iOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Makefile b/tests/dotnet/AppWithComposerIcon/macOS/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/macOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/AppWithComposerIcon/shared.mk b/tests/dotnet/AppWithComposerIcon/shared.mk new file mode 100644 index 000000000000..de0244100278 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/shared.mk @@ -0,0 +1,3 @@ +TOP=../../../.. +TESTNAME=AppWithComposerIcon +include $(TOP)/tests/common/shared-dotnet.mk diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Makefile b/tests/dotnet/AppWithComposerIcon/tvOS/Makefile new file mode 100644 index 000000000000..110d078f4577 --- /dev/null +++ b/tests/dotnet/AppWithComposerIcon/tvOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk From 478d2c3da55eebd1480cd0be4d9702e7318f2b75 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 12 Feb 2026 13:50:40 +0100 Subject: [PATCH 7/9] Fix icon.json format and remove xcassets fallback The icon.json format used 'version'+'layers'+'filename' but the correct Icon Composer format uses 'groups' containing 'layers' with 'image-name'. The old format caused actool's icon export subprocess to fail with status 255. With the correct format, actool successfully compiles .icon files into app icons on all platforms. Also removed the Images.xcassets fallback from the test project (no longer needed) and updated the integration test to assert success instead of skipping. --- .../Resources/AppIcon.icon/icon.json | 25 +++++------ .../Resources/Images.xcassets/Contents.json | 6 --- .../Image.imageset/Contents.json | 12 ----- .../Images.xcassets/Image.imageset/Icon16.png | Bin 83 -> 0 bytes .../iOS/Resources/AppIcon.icon/icon.json | 25 +++++------ .../Resources/Images.xcassets/Contents.json | 6 --- .../Image.imageset/Contents.json | 12 ----- .../Images.xcassets/Image.imageset/Icon16.png | Bin 83 -> 0 bytes .../macOS/Resources/AppIcon.icon/icon.json | 25 +++++------ .../Resources/Images.xcassets/Contents.json | 6 --- .../Image.imageset/Contents.json | 12 ----- .../Images.xcassets/Image.imageset/Icon16.png | Bin 83 -> 0 bytes .../tvOS/Resources/AppIcon.icon/icon.json | 25 +++++------ .../Resources/Images.xcassets/Contents.json | 6 --- .../Image.imageset/Contents.json | 12 ----- .../Images.xcassets/Image.imageset/Icon16.png | Bin 83 -> 0 bytes tests/dotnet/UnitTests/AppIconTest.cs | 11 +---- .../TaskTests/ACToolTaskTest.cs | 42 +++++++++--------- 18 files changed, 66 insertions(+), 159 deletions(-) delete mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Contents.json delete mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Contents.json delete mode 100644 tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Icon16.png delete mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Contents.json delete mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Contents.json delete mode 100644 tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Icon16.png delete mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Contents.json delete mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Contents.json delete mode 100644 tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Icon16.png delete mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Contents.json delete mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Contents.json delete mode 100644 tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Icon16.png diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json index 2fcee2039b37..9cb9eee03bd1 100644 --- a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json +++ b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/AppIcon.icon/icon.json @@ -1,19 +1,16 @@ { - "version" : 1, - "layers" : [ + "groups" : [ { - "filename" : "back.png", - "size" : { - "width" : 1024, - "height" : 1024 - } - }, - { - "filename" : "front.png", - "size" : { - "width" : 1024, - "height" : 1024 - } + "layers" : [ + { + "image-name" : "back.png", + "name" : "back" + }, + { + "image-name" : "front.png", + "name" : "front" + } + ] } ] } diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Contents.json b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Contents.json deleted file mode 100644 index 73c00596a7fc..000000000000 --- a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Contents.json b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Contents.json deleted file mode 100644 index 103bde20f61c..000000000000 --- a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "Icon16.png", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Icon16.png b/tests/dotnet/AppWithComposerIcon/MacCatalyst/Resources/Images.xcassets/Image.imageset/Icon16.png deleted file mode 100644 index a07ad0dab3fd27e91d90db830d434828fa8066af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ql2i3Ar-fhJ!Y0YvR9pvc%>nb e*^O6152z$A^PoJRjO1ycDh5wiKbLh*2~7aq;uG@# diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json index 2fcee2039b37..9cb9eee03bd1 100644 --- a/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json +++ b/tests/dotnet/AppWithComposerIcon/iOS/Resources/AppIcon.icon/icon.json @@ -1,19 +1,16 @@ { - "version" : 1, - "layers" : [ + "groups" : [ { - "filename" : "back.png", - "size" : { - "width" : 1024, - "height" : 1024 - } - }, - { - "filename" : "front.png", - "size" : { - "width" : 1024, - "height" : 1024 - } + "layers" : [ + { + "image-name" : "back.png", + "name" : "back" + }, + { + "image-name" : "front.png", + "name" : "front" + } + ] } ] } diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Contents.json b/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Contents.json deleted file mode 100644 index 73c00596a7fc..000000000000 --- a/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Contents.json b/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Contents.json deleted file mode 100644 index 103bde20f61c..000000000000 --- a/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "Icon16.png", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Icon16.png b/tests/dotnet/AppWithComposerIcon/iOS/Resources/Images.xcassets/Image.imageset/Icon16.png deleted file mode 100644 index a07ad0dab3fd27e91d90db830d434828fa8066af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ql2i3Ar-fhJ!Y0YvR9pvc%>nb e*^O6152z$A^PoJRjO1ycDh5wiKbLh*2~7aq;uG@# diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json index 2fcee2039b37..9cb9eee03bd1 100644 --- a/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json +++ b/tests/dotnet/AppWithComposerIcon/macOS/Resources/AppIcon.icon/icon.json @@ -1,19 +1,16 @@ { - "version" : 1, - "layers" : [ + "groups" : [ { - "filename" : "back.png", - "size" : { - "width" : 1024, - "height" : 1024 - } - }, - { - "filename" : "front.png", - "size" : { - "width" : 1024, - "height" : 1024 - } + "layers" : [ + { + "image-name" : "back.png", + "name" : "back" + }, + { + "image-name" : "front.png", + "name" : "front" + } + ] } ] } diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Contents.json b/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Contents.json deleted file mode 100644 index 73c00596a7fc..000000000000 --- a/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Contents.json b/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Contents.json deleted file mode 100644 index 103bde20f61c..000000000000 --- a/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "Icon16.png", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Icon16.png b/tests/dotnet/AppWithComposerIcon/macOS/Resources/Images.xcassets/Image.imageset/Icon16.png deleted file mode 100644 index a07ad0dab3fd27e91d90db830d434828fa8066af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ql2i3Ar-fhJ!Y0YvR9pvc%>nb e*^O6152z$A^PoJRjO1ycDh5wiKbLh*2~7aq;uG@# diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json index 2fcee2039b37..9cb9eee03bd1 100644 --- a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json +++ b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/AppIcon.icon/icon.json @@ -1,19 +1,16 @@ { - "version" : 1, - "layers" : [ + "groups" : [ { - "filename" : "back.png", - "size" : { - "width" : 1024, - "height" : 1024 - } - }, - { - "filename" : "front.png", - "size" : { - "width" : 1024, - "height" : 1024 - } + "layers" : [ + { + "image-name" : "back.png", + "name" : "back" + }, + { + "image-name" : "front.png", + "name" : "front" + } + ] } ] } diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Contents.json b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Contents.json deleted file mode 100644 index 73c00596a7fc..000000000000 --- a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Contents.json b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Contents.json deleted file mode 100644 index 103bde20f61c..000000000000 --- a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "Icon16.png", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Icon16.png b/tests/dotnet/AppWithComposerIcon/tvOS/Resources/Images.xcassets/Image.imageset/Icon16.png deleted file mode 100644 index a07ad0dab3fd27e91d90db830d434828fa8066af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ql2i3Ar-fhJ!Y0YvR9pvc%>nb e*^O6152z$A^PoJRjO1ycDh5wiKbLh*2~7aq;uG@# diff --git a/tests/dotnet/UnitTests/AppIconTest.cs b/tests/dotnet/UnitTests/AppIconTest.cs index 5b7479c83279..7c84445556ae 100644 --- a/tests/dotnet/UnitTests/AppIconTest.cs +++ b/tests/dotnet/UnitTests/AppIconTest.cs @@ -687,16 +687,7 @@ public void ComposerIcon (ApplePlatform platform, string runtimeIdentifiers) Clean (projectPath); var properties = GetDefaultProperties (runtimeIdentifiers); - var rv = DotNet.Execute ("build", projectPath, properties, assert_success: false); - if (rv.ExitCode != 0) { - var errors = BinLog.GetBuildLogErrors (rv.BinLogPath).ToArray (); - // If the only error is that the partial-info.plist wasn't generated, - // it's because the icon export subprocess failed. This can happen - // on some systems where the icon composer format isn't fully supported. - if (errors.Length == 1 && errors [0].Message?.Contains ("Partial Info.plist file was not generated") == true) - Assert.Ignore ("Icon Composer export is not supported on this system (actool icon export failed)."); - Assert.Fail ($"Build failed with {errors.Length} error(s):\n{string.Join ("\n", errors.Select (v => v.Message))}"); - } + DotNet.Execute ("build", projectPath, properties); var resourcesDirectory = GetResourcesDirectory (platform, appPath); diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs index b18b5b438fc0..c816c559450d 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs @@ -648,13 +648,13 @@ public void IconFileSupport (ApplePlatform platform) var assetsPath = Path.Combine (iconFolderPath, "Assets"); Directory.CreateDirectory (assetsPath); - // Create a dummy icon.json file (simplified structure for testing) + // Create a placeholder icon.json file (simplified structure for testing) var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); - File.WriteAllText (iconJsonPath, @"{""version"":1}"); + File.WriteAllText (iconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); - // Create a dummy image file in the Assets folder + // Create a placeholder image file in the Assets folder var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); - File.WriteAllText (imagePath, "dummy image"); + File.WriteAllText (imagePath, "placeholder image"); var actool = CreateACToolTask ( platform, @@ -665,10 +665,8 @@ public void IconFileSupport (ApplePlatform platform) ); actool.AppIcon = "AppIcon"; - // The task should recognize AppIcon as a valid icon - // Note: This will fail at actool execution since we don't have a real .icon structure, - // but we're testing that the icon is recognized in the validation phase - ExecuteTask (actool, expectedErrorCount: 1); // Expected to fail at actool execution + // actool may fail on the placeholder .icon content, but the validation phase should pass + actool.Execute (); // Verify that no error was logged about the icon not being found var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); @@ -690,10 +688,10 @@ public void IconFileSupportWithIncludeAllAppIcons (ApplePlatform platform) Directory.CreateDirectory (assetsPath); var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); - File.WriteAllText (iconJsonPath, @"{""version"":1}"); + File.WriteAllText (iconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); - File.WriteAllText (imagePath, "dummy image"); + File.WriteAllText (imagePath, "placeholder image"); var actool = CreateACToolTask ( platform, @@ -705,7 +703,8 @@ public void IconFileSupportWithIncludeAllAppIcons (ApplePlatform platform) actool.AppIcon = "AppIcon"; actool.IncludeAllAppIcons = true; - ExecuteTask (actool, expectedErrorCount: 1); + // actool may fail on the placeholder .icon content, but the validation phase should pass + actool.Execute (); var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), @@ -726,10 +725,10 @@ public void IconFileSupportAsAlternateIcon (ApplePlatform platform) Directory.CreateDirectory (assetsPath); var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); - File.WriteAllText (iconJsonPath, @"{""version"":1}"); + File.WriteAllText (iconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); - File.WriteAllText (imagePath, "dummy image"); + File.WriteAllText (imagePath, "placeholder image"); // Also need a primary icon for the alternate icon test to make sense var primaryIconPath = Path.Combine (projectDir, "Resources", "AppIcon.icon"); @@ -737,10 +736,10 @@ public void IconFileSupportAsAlternateIcon (ApplePlatform platform) Directory.CreateDirectory (primaryAssetsPath); var primaryIconJsonPath = Path.Combine (primaryIconPath, "icon.json"); - File.WriteAllText (primaryIconJsonPath, @"{""version"":1}"); + File.WriteAllText (primaryIconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); var primaryImagePath = Path.Combine (primaryAssetsPath, "icon_512x512.png"); - File.WriteAllText (primaryImagePath, "dummy image"); + File.WriteAllText (primaryImagePath, "placeholder image"); var actool = CreateACToolTask ( platform, @@ -754,7 +753,8 @@ public void IconFileSupportAsAlternateIcon (ApplePlatform platform) actool.AppIcon = "AppIcon"; actool.AlternateAppIcons = new ITaskItem [] { new TaskItem ("AlternateIcon") }; - ExecuteTask (actool, expectedErrorCount: 1); + // actool may fail on the placeholder .icon content, but the validation phase should pass + actool.Execute (); var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AlternateAppIcon")), @@ -777,10 +777,10 @@ public void InexistentIconFile (ApplePlatform platform) Directory.CreateDirectory (assetsPath); var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); - File.WriteAllText (iconJsonPath, @"{""version"":1}"); + File.WriteAllText (iconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); - File.WriteAllText (imagePath, "dummy image"); + File.WriteAllText (imagePath, "placeholder image"); var actool = CreateACToolTask ( platform, @@ -816,10 +816,10 @@ public void MixedXCAssetsAndIconFile (ApplePlatform platform) Directory.CreateDirectory (assetsPath); var iconJsonPath = Path.Combine (iconFolderPath, "icon.json"); - File.WriteAllText (iconJsonPath, @"{""version"":1}"); + File.WriteAllText (iconJsonPath, @"{""groups"":[{""layers"":[{""image-name"":""icon_512x512.png"",""name"":""icon""}]}]}"); var imagePath = Path.Combine (assetsPath, "icon_512x512.png"); - File.WriteAllText (imagePath, "dummy image"); + File.WriteAllText (imagePath, "placeholder image"); imageAssets.Add (iconJsonPath + "|Resources/ComposerIcon.icon/icon.json"); imageAssets.Add (imagePath + "|Resources/ComposerIcon.icon/Assets/icon_512x512.png"); @@ -832,7 +832,7 @@ public void MixedXCAssetsAndIconFile (ApplePlatform platform) ); actool.AppIcon = "AppIcons"; - // actool may fail on the dummy .icon content, but the validation phase should pass + // actool may fail on the placeholder .icon content, but the validation phase should pass actool.Execute (); var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); From 40f00db5ee03047a1dbb6d97d9cf0a0f4c0f8a7a Mon Sep 17 00:00:00 2001 From: GitHub Actions Autoformatter Date: Tue, 17 Feb 2026 10:47:41 +0000 Subject: [PATCH 8/9] Auto-format source code --- .../dotnet/AppWithComposerIcon/AppDelegate.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/dotnet/AppWithComposerIcon/AppDelegate.cs b/tests/dotnet/AppWithComposerIcon/AppDelegate.cs index 2ec34ad0f408..128f4f61dcb4 100644 --- a/tests/dotnet/AppWithComposerIcon/AppDelegate.cs +++ b/tests/dotnet/AppWithComposerIcon/AppDelegate.cs @@ -9,17 +9,17 @@ namespace AppWithComposerIcon { #if !(__MACCATALYST__ || __MACOS__) -public class AppDelegate : UIApplicationDelegate { -public override bool FinishedLaunching (UIApplication app, NSDictionary options) -{ -return true; -} -} + public class AppDelegate : UIApplicationDelegate { + public override bool FinishedLaunching (UIApplication app, NSDictionary options) + { + return true; + } + } #endif -public class Program { -static int Main (string [] args) -{ + public class Program { + static int Main (string [] args) + { #if __MACCATALYST__ || __MACOS__ GC.KeepAlive (typeof (NSObject)); // prevent linking away the platform assembly @@ -27,9 +27,9 @@ static int Main (string [] args) return args.Length; #else -UIApplication.Main (args, null, typeof (AppDelegate)); -return 0; + UIApplication.Main (args, null, typeof (AppDelegate)); + return 0; #endif -} -} + } + } } From c2cd21da2d0e3a9846f725973cc5b19367bbabec Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 20 Mar 2026 11:32:23 +0100 Subject: [PATCH 9/9] Address PR review comments for Composer Icons tests - ACToolTaskTest: Replace ad-hoc error assertions with shared AssertNoIconValidationErrors helper that checks all icon validation error patterns (E7130/E7127/E7128/E7129), not just one specific message. - AppIconTest: Add Assets.car existence assertion to ComposerIcon test, matching the pattern used by TestXCAssetsImpl. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/dotnet/UnitTests/AppIconTest.cs | 4 +++ .../TaskTests/ACToolTaskTest.cs | 34 +++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/tests/dotnet/UnitTests/AppIconTest.cs b/tests/dotnet/UnitTests/AppIconTest.cs index 7c84445556ae..0bdafa8ed9a5 100644 --- a/tests/dotnet/UnitTests/AppIconTest.cs +++ b/tests/dotnet/UnitTests/AppIconTest.cs @@ -700,6 +700,10 @@ public void ComposerIcon (ApplePlatform platform, string runtimeIdentifiers) var backPngInBundle = Path.Combine (resourcesDirectory, "AppIcon.icon", "Assets", "back.png"); Assert.That (backPngInBundle, Does.Not.Exist, "back.png should not be in the app bundle as a raw BundleResource"); + + // Verify that the compiled asset catalog exists in the app bundle + var assetsCar = Path.Combine (resourcesDirectory, "Assets.car"); + Assert.That (assetsCar, Does.Exist, "Assets.car"); } } } diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs index c816c559450d..1da179b58333 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ACToolTaskTest.cs @@ -668,10 +668,8 @@ public void IconFileSupport (ApplePlatform platform) // actool may fail on the placeholder .icon content, but the validation phase should pass actool.Execute (); - // Verify that no error was logged about the icon not being found - var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); - Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), - "Should not report that AppIcon is not found among image resources"); + // Verify that no icon validation errors were logged + AssertNoIconValidationErrors (); } [Test] @@ -706,9 +704,8 @@ public void IconFileSupportWithIncludeAllAppIcons (ApplePlatform platform) // actool may fail on the placeholder .icon content, but the validation phase should pass actool.Execute (); - var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); - Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), - "Should not report that AppIcon is not found among image resources"); + // Verify that no icon validation errors were logged + AssertNoIconValidationErrors (); } [Test] @@ -756,11 +753,8 @@ public void IconFileSupportAsAlternateIcon (ApplePlatform platform) // actool may fail on the placeholder .icon content, but the validation phase should pass actool.Execute (); - var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); - Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AlternateAppIcon")), - "Should not report that AlternateIcon is not found among image resources"); - Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), - "Should not report that AppIcon is not found among image resources"); + // Verify that no icon validation errors were logged + AssertNoIconValidationErrors (); } [Test] @@ -835,9 +829,21 @@ public void MixedXCAssetsAndIconFile (ApplePlatform platform) // actool may fail on the placeholder .icon content, but the validation phase should pass actool.Execute (); + // Verify that no icon validation errors were logged + AssertNoIconValidationErrors (); + } + + void AssertNoIconValidationErrors () + { var errorMessages = Engine.Logger.ErrorEvents.Select (e => e.Message).ToList (); - Assert.IsFalse (errorMessages.Any (m => m.Contains ("Can't find the AppIcon")), - "Should not report that AppIcon is not found when mixing .xcassets and .icon"); + Assert.That (errorMessages, Has.None.Contain ("Can't find the AppIcon"), + "Should not report that AppIcon is not found among image resources"); + Assert.That (errorMessages, Has.None.Contain ("Can't find the AlternateAppIcon"), + "Should not report that AlternateAppIcon is not found among image resources"); + Assert.That (errorMessages, Has.None.Contain ("is specified as both 'AppIcon' and 'AlternateAppIcon'"), + "Should not report icon conflict between AppIcon and AlternateAppIcon"); + Assert.That (errorMessages, Has.None.Contain ("Can't specify both 'XSAppIconAssets'"), + "Should not report XSAppIconAssets conflict"); } } }