diff --git a/IconHelper.Test/ProcessDirectoryTests.cs b/IconHelper.Test/ProcessDirectoryTests.cs index 9a7ddf3..7f017c9 100644 --- a/IconHelper.Test/ProcessDirectoryTests.cs +++ b/IconHelper.Test/ProcessDirectoryTests.cs @@ -187,6 +187,37 @@ public void SkipsFileManagerMetadataWithoutCountingItAsFailed() Assert.AreEqual(1, Directory.GetFiles(output).Length); } + [TestMethod] + public void TreatsFindersCustomFolderIconFileAsMetadata() + { + Assert.IsTrue(IconHelper.IsFileManagerMetadata("Icon\r"), "Finder's custom folder icon file is metadata, not an icon."); + Assert.IsFalse(IconHelper.IsFileManagerMetadata("Icon"), "Only the CR-suffixed name is Finder's; a plain Icon file is still an input."); + Assert.IsFalse(IconHelper.IsFileManagerMetadata("icon\r"), "Finder always writes the name with a capital I."); + } + + [TestMethod] + public void SkipsFindersCustomFolderIconFileWithoutCountingItAsFailed() + { + // Windows can't create a file name containing a carriage return, and a folder that held one + // would not reach the tool there anyway, so the batch-level check runs on Linux and macOS. + if (OperatingSystem.IsWindows()) + { + Assert.Inconclusive("Windows cannot create a file named \"Icon\\r\"."); + } + + using TempDirectory temp = new(); + string input = temp.Combine("in"); + string output = temp.Combine("out"); + Directory.CreateDirectory(input); + File.WriteAllBytes(Path.Join(input, "Icon\r"), []); + WritePng(Path.Join(input, "star.png"), 64); + + BatchResult result = IconHelper.ProcessDirectory(ArgumentsFor(input, output), NamedColors.White); + + Assert.AreEqual(new BatchResult(1, 0), result, "Finder's Icon\\r file should be neither written nor failed."); + Assert.AreEqual(IconHelper.ExitSuccess, IconHelper.ExitCodeFor(result)); + } + [TestMethod] public void ReportsAndSkipsAFileThatCannotBeOpened() { diff --git a/IconHelper/IconHelper.cs b/IconHelper/IconHelper.cs index eceabea..8725059 100644 --- a/IconHelper/IconHelper.cs +++ b/IconHelper/IconHelper.cs @@ -190,12 +190,15 @@ internal static BatchResult ProcessDirectory(Arguments args, Color color) /// /// Names that Finder and Explorer drop into any folder they open: dot-files such as .DS_Store and - /// ._ AppleDouble files, plus Thumbs.db, ehthumbs.db and desktop.ini. They are not icons, so they - /// count as neither written nor failed; counting them as failures made every batch on such a - /// folder exit 2 and taught scripts to ignore the code that reports real decode failures. + /// ._ AppleDouble files, plus Thumbs.db, ehthumbs.db and desktop.ini, and the "Icon\r" file Finder + /// writes when a folder is given a custom icon (its data fork is empty; the icon lives in the + /// resource fork). They are not icons, so they count as neither written nor failed; counting them + /// as failures made every batch on such a folder exit 2 and taught scripts to ignore the code that + /// reports real decode failures. /// internal static bool IsFileManagerMetadata(string fileName) => fileName.StartsWith('.') + || fileName.Equals("Icon\r", StringComparison.Ordinal) || fileName.Equals("Thumbs.db", StringComparison.OrdinalIgnoreCase) || fileName.Equals("ehthumbs.db", StringComparison.OrdinalIgnoreCase) || fileName.Equals("desktop.ini", StringComparison.OrdinalIgnoreCase);