diff --git a/src/DiffEngine.Tests/ExtensionCaseTests.cs b/src/DiffEngine.Tests/ExtensionCaseTests.cs new file mode 100644 index 00000000..38ed8808 --- /dev/null +++ b/src/DiffEngine.Tests/ExtensionCaseTests.cs @@ -0,0 +1,39 @@ +/// +/// Extensions are matched the way file systems produce them. +/// +/// ExtensionLookup, PathLookup and BinaryExtensions all compared ordinally while every +/// registration is lowercase, so .PNG, .JPG and .Docx - which is exactly what Windows and macOS +/// hand back - matched nothing. The tool resolved for foo.png and not for foo.PNG. +/// Viewer/ImageExtensions already uses OrdinalIgnoreCase for the same kind of lookup. +/// +/// +[NotInParallel] +public class ExtensionCaseTests +{ + [Test] + public async Task ExtensionsResolveWhateverTheirCasing() + { + var extension = $".zz{Guid.NewGuid():N}"; + var tool = DiffTools.AddTool( + name: $"CaseProbe{Guid.NewGuid():N}", + autoRefresh: false, + isMdi: false, + supportsText: false, + requiresTarget: true, + useShellExecute: false, + launchArguments: new( + Left: (temp, target) => $"\"{temp}\" \"{target}\"", + Right: (temp, target) => $"\"{target}\" \"{temp}\""), + exePath: Environment.ProcessPath!, + binaryExtensions: [extension]); + + await Assert.That(tool).IsNotNull(); + + await Assert.That(DiffTools.TryFindByExtension(extension, out _)).IsTrue(); + await Assert.That(DiffTools.TryFindByExtension(extension.ToUpperInvariant(), out _)).IsTrue(); + + await Assert.That(DiffTools.TryFindForInputFilePath($"file{extension.ToUpperInvariant()}", out _)).IsTrue(); + + await Assert.That(tool!.BinaryExtensions.Contains(extension.ToUpperInvariant())).IsTrue(); + } +} diff --git a/src/DiffEngine/DiffTools.cs b/src/DiffEngine/DiffTools.cs index 103cbf35..aeb3fb06 100644 --- a/src/DiffEngine/DiffTools.cs +++ b/src/DiffEngine/DiffTools.cs @@ -2,8 +2,12 @@ public static partial class DiffTools { - static Dictionary ExtensionLookup = []; - static Dictionary PathLookup = []; + // Case insensitive, because the keys are file extensions and executable paths and the file + // systems that produce them are. An ordinal lookup meant .PNG, .JPG and .Docx - which is what + // Windows and macOS hand back - matched none of the lowercase registrations, so the tool + // resolved for foo.png and not for foo.PNG. Viewer/ImageExtensions already does this + static Dictionary ExtensionLookup = new(StringComparer.OrdinalIgnoreCase); + static Dictionary PathLookup = new(StringComparer.OrdinalIgnoreCase); static Dictionary ToolLookup = []; static ResolvedTool? firstTextTool; static List resolved = []; diff --git a/src/DiffEngine/ResolvedTool.cs b/src/DiffEngine/ResolvedTool.cs index d94e547b..24f1f4de 100644 --- a/src/DiffEngine/ResolvedTool.cs +++ b/src/DiffEngine/ResolvedTool.cs @@ -62,7 +62,9 @@ Extensions must begin with a period. } } - BinaryExtensions = binaryExtensions.ToFrozenSet(); + // Case insensitive for the same reason ExtensionLookup is: a binary extension is compared + // against whatever casing the file system produced + BinaryExtensions = binaryExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); RequiresTarget = requiresTarget; SupportsText = supportsText;