diff --git a/src/DiffEngineTray.Tests/TrackerLockedMoveTest.cs b/src/DiffEngineTray.Tests/TrackerLockedMoveTest.cs index e3960fb8..7ece94cf 100644 --- a/src/DiffEngineTray.Tests/TrackerLockedMoveTest.cs +++ b/src/DiffEngineTray.Tests/TrackerLockedMoveTest.cs @@ -102,6 +102,36 @@ public async Task NoResolver_KeepsMovePending() await Assert.That(await File.ReadAllTextAsync(target1)).IsEqualTo("old"); } + /// + /// A move that fails is re-added, so whatever KillProcesses did to it has to leave it usable. + /// It used to leave a disposed Process on the entry, and the Accept-open hot key and + /// "Open diff tool" both read that property - throwing "No process is associated with this + /// object" on the UI thread, where nothing catches it. + /// + [Test] + public async Task Ignore_DropsTheKilledProcessFromThePendingMove() + { + await using var tracker = new RecordingTracker( + (_, _) => LockedFilesResponse.Ignore); + var lockProcess = FileLockUtils.StartFileLockProcess(target1); + // Stands in for the diff tool: a killable process the move is tracking + var toolProcess = FileLockUtils.StartFileLockProcess(temp2); + try + { + var tracked = tracker.AddMove(temp1, target1, "theExe", "theArguments", true, toolProcess.Id); + tracker.Accept(tracked); + + var pending = tracker.Moves.Single(); + await Assert.That(pending.Process).IsNull(); + // What the hot key and DiffToolLauncher do with it + await Assert.That(pending.Process is { HasExited: false }).IsFalse(); + } + finally + { + FileLockUtils.Cleanup(toolProcess); + FileLockUtils.Cleanup(lockProcess); + } + } static string CreateFile(string content) { var path = Path.Combine(Path.GetTempPath(), $"TrackerLockedMoveTest_{Guid.NewGuid()}.txt"); diff --git a/src/DiffEngineTray/Tracker.cs b/src/DiffEngineTray/Tracker.cs index 7a5a4523..5ab19434 100644 --- a/src/DiffEngineTray/Tracker.cs +++ b/src/DiffEngineTray/Tracker.cs @@ -645,6 +645,12 @@ static void KillProcesses(TrackedMove move) } move.Process.KillAndDispose(); + + // The move can come back: a locked target, the user picking Ignore, or the retries running + // out all re-add this same object. Leaving a disposed Process on it made the Accept-open + // hot key and "Open diff tool" throw "No process is associated with this object" on the UI + // thread, where nothing catches it + move.Process = null; } ///