chore(claude-do): merge [B1] UnifiedDiffParser.Parse vom UI-Thread nehmen (DiffViewe
ClaudeDo-Task: e2003fc8-f029-44d0-a66a-51b57a95583f
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using ClaudeDo.Localization;
|
||||
using ClaudeDo.Ui.Localization;
|
||||
@@ -140,6 +141,115 @@ public class DiffViewerViewModelTests
|
||||
Assert.Equal("DIFF-B", vm.DisplayedDiff);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Planning_SelectSubtask_PopulatesPlanningFilesAsync()
|
||||
{
|
||||
const string diff = "diff --git a/foo.cs b/foo.cs\n" +
|
||||
"--- a/foo.cs\n" +
|
||||
"+++ b/foo.cs\n" +
|
||||
"@@ -1,1 +1,1 @@\n" +
|
||||
"-old\n" +
|
||||
"+new\n";
|
||||
var fake = new FakePlanningWorker
|
||||
{
|
||||
AggregateResult = new[] { new SubtaskDiffDto("s1", "First", "b1", "base1", "head1", null, diff) }
|
||||
};
|
||||
var vm = new DiffViewerViewModel(null!, fake, TestSettings());
|
||||
vm.ConfigurePlanning("plan-1", "main");
|
||||
|
||||
await vm.LoadAsync();
|
||||
|
||||
// OnDisplayedDiffChanged offloads the parse via Task.Run and writes PlanningFiles back
|
||||
// fire-and-forget, so it may not have landed yet when LoadAsync returns.
|
||||
var deadline = DateTime.UtcNow.AddSeconds(5);
|
||||
while (DateTime.UtcNow < deadline && vm.PlanningFiles.Count == 0) await Task.Delay(10);
|
||||
|
||||
var file = Assert.Single(vm.PlanningFiles);
|
||||
Assert.Equal("foo.cs", file.Path);
|
||||
}
|
||||
|
||||
private static string DiffFor(string path) =>
|
||||
$"diff --git a/{path} b/{path}\n--- a/{path}\n+++ b/{path}\n@@ -1,1 +1,1 @@\n-old\n+new\n";
|
||||
|
||||
[Fact]
|
||||
public async Task Planning_SelectSubtask_RunsAndEndsParseOp()
|
||||
{
|
||||
var fake = new FakePlanningWorker
|
||||
{
|
||||
AggregateResult = new[]
|
||||
{
|
||||
new SubtaskDiffDto("s1", "First", "b1", "base1", "head1", null, DiffFor("a.cs")),
|
||||
new SubtaskDiffDto("s2", "Second", "b2", "base2", "head2", null, DiffFor("b.cs")),
|
||||
}
|
||||
};
|
||||
var vm = new DiffViewerViewModel(null!, fake, TestSettings());
|
||||
vm.ConfigurePlanning("plan-1", "main");
|
||||
await vm.LoadAsync();
|
||||
|
||||
// Let the auto-selected first subtask's parse settle before starting the assertion.
|
||||
var settleDeadline = DateTime.UtcNow.AddSeconds(5);
|
||||
while (DateTime.UtcNow < settleDeadline && vm.ParseOp.IsRunning) await Task.Delay(10);
|
||||
|
||||
// ParseOp.Begin runs synchronously (before the offloaded parse's first await point can
|
||||
// return), so this handler is guaranteed to observe IsRunning=true regardless of how
|
||||
// fast the background parse itself completes — no race with the poll loop below.
|
||||
var sawRunning = false;
|
||||
vm.ParseOp.PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(OperationStatus.IsRunning) && vm.ParseOp.IsRunning)
|
||||
sawRunning = true;
|
||||
};
|
||||
|
||||
vm.SelectedSubtask = vm.Subtasks[1];
|
||||
|
||||
var deadline = DateTime.UtcNow.AddSeconds(5);
|
||||
while (DateTime.UtcNow < deadline && vm.ParseOp.IsRunning) await Task.Delay(10);
|
||||
|
||||
Assert.True(sawRunning);
|
||||
Assert.False(vm.ParseOp.IsRunning);
|
||||
var file = Assert.Single(vm.PlanningFiles);
|
||||
Assert.Equal("b.cs", file.Path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Planning_RapidSubtaskSwitches_ParseOpEndsAndOnlyLatestWins()
|
||||
{
|
||||
var fake = new FakePlanningWorker
|
||||
{
|
||||
AggregateResult = new[]
|
||||
{
|
||||
new SubtaskDiffDto("s1", "First", "b1", "base1", "head1", null, DiffFor("a.cs")),
|
||||
new SubtaskDiffDto("s2", "Second", "b2", "base2", "head2", null, DiffFor("b.cs")),
|
||||
}
|
||||
};
|
||||
var vm = new DiffViewerViewModel(null!, fake, TestSettings());
|
||||
vm.ConfigurePlanning("plan-1", "main");
|
||||
await vm.LoadAsync();
|
||||
|
||||
var settleDeadline = DateTime.UtcNow.AddSeconds(5);
|
||||
while (DateTime.UtcNow < settleDeadline && vm.ParseOp.IsRunning) await Task.Delay(10);
|
||||
|
||||
var sawRunning = false;
|
||||
vm.ParseOp.PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(OperationStatus.IsRunning) && vm.ParseOp.IsRunning)
|
||||
sawRunning = true;
|
||||
};
|
||||
|
||||
// Two changes fired back-to-back without awaiting in between: the first parse is
|
||||
// overtaken by the second before it can write PlanningFiles.
|
||||
vm.SelectedSubtask = vm.Subtasks[1];
|
||||
vm.SelectedSubtask = vm.Subtasks[0];
|
||||
|
||||
var deadline = DateTime.UtcNow.AddSeconds(5);
|
||||
while (DateTime.UtcNow < deadline && vm.ParseOp.IsRunning) await Task.Delay(10);
|
||||
|
||||
Assert.True(sawRunning);
|
||||
Assert.False(vm.ParseOp.IsRunning);
|
||||
var file = Assert.Single(vm.PlanningFiles);
|
||||
Assert.Equal("a.cs", file.Path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Planning_ToggleCombined_Success_DisplaysUnifiedDiff()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user