Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/DiffEngine.Tests/TextFileConventionResolutionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/// <summary>
/// A text file convention is invisible to the extension lookup, which is why Launch and Kill had
/// to stop using it.
/// <para>
/// Launch asked TryFindByExtension, which can only consult IsTextExtension, while LaunchAsync asks
/// TryFindForInputFilePath, which honours a text file convention. So a file matched by a
/// convention rather than by its extension - a name with no extension, a dotfile - opened a diff
/// tool asynchronously and reported NoDiffToolFound synchronously, and Kill then logged that it
/// could not find one for a pair LaunchAsync had opened, leaving the tool on screen.
/// </para>
/// <para>
/// This pins the gap between the two lookups. It does not drive DiffRunner.Launch, because
/// resolving a convention matched file can only ever land on the first text tool installed on the
/// machine - there is no way to register a fake one for it - and a test that launches the
/// developer's real diff tool is not worth having. The change in Launch and Kill is a change of
/// which of these two calls they make, and is verified by reading.
/// </para>
/// </summary>
[NotInParallel]
public class TextFileConventionResolutionTests
{
[Test]
public async Task AConventionIsInvisibleToTheExtensionLookup()
{
var name = $"conventionprobe{Guid.NewGuid():N}";
FileExtensions.AddTextFileConvention(path => Path.GetFileNameWithoutExtension(path).StartsWith(name, StringComparison.Ordinal));

var path = Path.Combine(Path.GetTempPath(), $"{name}.unknownextension");

// What LaunchAsync resolves with
var byPath = DiffTools.TryFindForInputFilePath(path, out var forAsync);
// What Launch and Kill used to resolve with
var byExtension = DiffTools.TryFindByExtension(Path.GetExtension(path), out _);

if (!byPath)
{
// No text tool resolved on this machine, so there is nothing for either to find and
// the two cannot disagree
return;
}

await Assert.That(forAsync).IsNotNull();
// The gap itself: the convention is invisible to the extension lookup
await Assert.That(byExtension).IsFalse();

await Assert.That(forAsync!.SupportsText).IsTrue();
}
}
8 changes: 4 additions & 4 deletions src/DiffEngine/DiffRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ public static LaunchResult Launch(string tempFile, string targetFile, Encoding?

return InnerLaunch(
([NotNullWhen(true)] out tool) =>
{
var extension = Path.GetExtension(tempFile);
return DiffTools.TryFindByExtension(extension, out tool);
},
// The same resolution LaunchAsync uses. Asking by extension alone cannot see a
// text file convention, so a file matched by one launched asynchronously and
// reported NoDiffToolFound synchronously
DiffTools.TryFindForInputFilePath(tempFile, out tool),
tempFile,
targetFile,
encoding);
Expand Down
9 changes: 6 additions & 3 deletions src/DiffEngine/DiffRunner_Kill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ public static void Kill(string tempFile, string targetFile)
return;
}

var extension = Path.GetExtension(tempFile);
if (!DiffTools.TryFindByExtension(extension, out var diffTool))
// TryFindForInputFilePath rather than by extension, so this resolves the same tool the
// launch did. By extension alone a file matched by a text file convention resolved to
// nothing here, and Kill logged "Extension not found" for a pair LaunchAsync had opened -
// leaving the tool on screen for a test that now passes
if (!DiffTools.TryFindForInputFilePath(tempFile, out var diffTool))
{
Logging.Write($"Extension not found. {extension}");
Logging.Write($"No diff tool for. {tempFile}");
return;
}

Expand Down
Loading