diff --git a/src/DiffEngineViewer.Tests/TrackedLabelTests.cs b/src/DiffEngineViewer.Tests/TrackedLabelTests.cs new file mode 100644 index 00000000..f74cdf99 --- /dev/null +++ b/src/DiffEngineViewer.Tests/TrackedLabelTests.cs @@ -0,0 +1,82 @@ +/// +/// Labels for tracked moves and deletes. One verified file name in two projects of a solution is +/// the ordinary case - every project has a SampleTests with a sample.verified.txt - and the label +/// is the only thing on the row. +/// +public class TrackedLabelTests +{ + [Test] + public async Task Two_moves_of_one_file_name_are_told_apart() + { + var state = Tracked( + Move("ProjectA"), + Move("ProjectB")); + + await Assert.That(Labels(state)).IsEquivalentTo( + [ + "ProjectA/sample.verified.txt", + "ProjectB/sample.verified.txt" + ]); + } + + [Test] + public async Task Two_deletes_of_one_file_name_are_told_apart() + { + var state = Tracked( + Delete("ProjectA"), + Delete("ProjectB")); + + await Assert.That(Labels(state)).IsEquivalentTo( + [ + "ProjectA/extra.verified.txt", + "ProjectB/extra.verified.txt" + ]); + } + + /// + /// A name that is already distinct is left as it is: the label is the shortest form that tells + /// one entry from another, and the path is what the tooltip is for. + /// + [Test] + public async Task One_move_keeps_its_bare_name() + { + var state = Tracked(Move("ProjectA")); + + await Assert.That(Labels(state)).IsEquivalentTo(["sample.verified.txt"]); + } + + static IReadOnlyList Labels(SessionState state) => + QueueProjection.Rows(state) + .Where(_ => _.Kind == QueueRowKind.Entry) + .Select(_ => _.Label.Trim()) + .ToList(); + + static SessionState Tracked(params QueueEntry[] entries) + { + var state = SessionState.Start(ViewerMode.Inline, Fixtures.Columns, Fixtures.Rows); + foreach (var entry in entries) + { + state = ViewerSession.EnqueueTracked(state, entry); + } + + return state; + } + + static QueueEntry Move(string project) => + QueueEntry.ForMove( + $"move:{project}", + "sample.verified.txt", + "SolutionA", + $"temp/{project}/sample.received.txt", + $"code/SolutionA/{project}/sample.verified.txt", + FileSide.OfText("received"), + FileSide.OfText("expected")); + + static QueueEntry Delete(string project) => + QueueEntry.ForDelete( + $"delete:{project}", + "extra.verified.txt", + "SolutionA", + $"code/SolutionA/{project}/extra.verified.txt", + FileSide.OfText("expected")); +} diff --git a/src/DiffEngineViewer/QueueProjection.cs b/src/DiffEngineViewer/QueueProjection.cs index a62f83c8..aa103523 100644 --- a/src/DiffEngineViewer/QueueProjection.cs +++ b/src/DiffEngineViewer/QueueProjection.cs @@ -328,6 +328,20 @@ static QueueItem EntryRow( ? $"{entry.Patch.SourceFile.ToLowerInvariant()}|{entry.TestName}" : null; + /// + /// The path an entry's label can be grown from: the source file for an inline entry, and the + /// file a tracked one is about. Tracked entries used to have none, so two verified files with + /// the same name in two projects of one solution were left showing the same label. + /// + static string? LabelPath(QueueEntry entry) => + entry.Kind switch + { + QueueEntryKind.Inline => entry.Patch?.SourceFile, + QueueEntryKind.Move => entry.TargetFile ?? entry.LeftFile, + QueueEntryKind.Delete => entry.LeftFile, + _ => null + }; + /// /// The label an entry shows when it stands alone: the test name when one is known, else the /// call site or the tracked file name. Collisions within a solution — the same file name and @@ -357,7 +371,7 @@ static string[] Labels(IReadOnlyList entries) { var entry = entries[index]; var baseLabel = entry.TestName ?? entry.Name; - labels[index] = WithDirectories(entry.Patch!.SourceFile, baseLabel, depth); + labels[index] = WithDirectories(LabelPath(entry)!, baseLabel, depth); } } @@ -367,7 +381,7 @@ static string[] Labels(IReadOnlyList entries) foreach (var index in Collisions(entries, labels)) { var entry = entries[index]; - labels[index] = $"{entry.TestName ?? entry.Name} ({Path.GetFileName(entry.Patch!.SourceFile)})"; + labels[index] = $"{entry.TestName ?? entry.Name} ({Path.GetFileName(LabelPath(entry)!)})"; } return labels; @@ -378,7 +392,7 @@ static List Collisions(IReadOnlyList entries, string[] labels) var collisions = new List(); for (var index = 0; index < entries.Count; index++) { - if (entries[index] is not { Kind: QueueEntryKind.Inline, Patch: not null }) + if (LabelPath(entries[index]) is null) { continue; }