Skip to content

Commit b95e125

Browse files
randleeclaude
andcommitted
fix: Resolve Windows CI test failures in TextOutputParser
The TextOutputParser had two issues causing test failures on Windows: 1. Regex pattern mismatch: The ChangeLinePattern used '[M]' for modified changes, but the PlainTextFormatter uses '[~]'. Updated the regex to match all actual change markers: +, -, ~, >, @, =, ? 2. Windows line endings: The regex multiline mode with '$' anchor didn't handle '\r\n' properly. Added line ending normalization to convert all '\r\n' and '\r' to '\n' before parsing. 3. Unchanged items handling: Added filtering for 'unchanged' change type in both AddLineNumbersFromChange and CollectLineRangesFromChange to match the behavior of JsonOutputParser. Context lines (unchanged) are now excluded from line range counts, ensuring cross-format consistency. Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent c311f55 commit b95e125

File tree

1 file changed

+31
-10
lines changed

1 file changed

+31
-10
lines changed

tests/RoslynDiff.TestUtilities/Parsers/TextOutputParser.cs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ namespace RoslynDiff.TestUtilities.Parsers;
1111
public class TextOutputParser : ILineNumberParser
1212
{
1313
// Regex patterns for parsing text output
14+
// Change markers: + (added), - (removed), ~ (modified), > (moved), @ (renamed), = (unchanged), ? (unknown)
1415
private static readonly Regex LineReferencePattern = new(@"\(line (\d+)(?:-(\d+))?\)", RegexOptions.Compiled);
1516
private static readonly Regex LineReferencePattern2 = new(@"line (\d+)(?:-(\d+))?", RegexOptions.Compiled);
16-
private static readonly Regex ChangeLinePattern = new(@"^\s*(\[[\+\-M]\])\s+(\w+):\s+(.+?)(?:\s+\(line (\d+)(?:-(\d+))?\))?$", RegexOptions.Compiled | RegexOptions.Multiline);
17+
private static readonly Regex ChangeLinePattern = new(@"^\s*(\[[\+\-~>=@\?]\])\s+(\w+):\s+(.+?)(?:\s+\(line (\d+)(?:-(\d+))?\))?\s*$", RegexOptions.Compiled | RegexOptions.Multiline);
1718

1819
/// <inheritdoc/>
1920
public string FormatName => "Text";
@@ -75,12 +76,15 @@ public static ParsedDiffResult Parse(string textContent)
7576
};
7677
}
7778

79+
// Normalize line endings for cross-platform compatibility
80+
var normalizedContent = textContent.Replace("\r\n", "\n").Replace("\r", "\n");
81+
7882
var changes = new List<ParsedChange>();
7983
var errors = new List<string>();
8084
var metadata = new Dictionary<string, string>();
8185

8286
// Parse header lines for metadata
83-
var lines = textContent.Split('\n');
87+
var lines = normalizedContent.Split('\n');
8488
string? oldPath = null;
8589
string? newPath = null;
8690
string? mode = null;
@@ -103,7 +107,7 @@ public static ParsedDiffResult Parse(string textContent)
103107
}
104108

105109
// Parse change lines (e.g., "[+] Method: Multiply (line 5-8)")
106-
var matches = ChangeLinePattern.Matches(textContent);
110+
var matches = ChangeLinePattern.Matches(normalizedContent);
107111
foreach (Match match in matches)
108112
{
109113
var changeIndicator = match.Groups[1].Value;
@@ -114,7 +118,10 @@ public static ParsedDiffResult Parse(string textContent)
114118
{
115119
"[+]" => "added",
116120
"[-]" => "removed",
117-
"[M]" => "modified",
121+
"[~]" => "modified",
122+
"[>]" => "moved",
123+
"[@]" => "renamed",
124+
"[=]" => "unchanged",
118125
_ => "unknown"
119126
};
120127

@@ -258,8 +265,18 @@ public List<string> ExtractLineReferences(string content)
258265
/// <summary>
259266
/// Recursively adds line numbers from a change and its children.
260267
/// </summary>
268+
/// <remarks>
269+
/// Skips unchanged items (context lines) to only include actual changes.
270+
/// </remarks>
261271
private static void AddLineNumbersFromChange(ParsedChange change, HashSet<int> lineNumbers)
262272
{
273+
// Skip unchanged items - these are context lines, not actual changes
274+
var isUnchanged = change.ChangeType.Equals("unchanged", StringComparison.OrdinalIgnoreCase);
275+
if (isUnchanged)
276+
{
277+
return;
278+
}
279+
263280
if (change.LineRange != null)
264281
{
265282
for (int i = change.LineRange.Start; i <= change.LineRange.End; i++)
@@ -286,17 +303,21 @@ private static void AddLineNumbersFromChange(ParsedChange change, HashSet<int> l
286303
/// Recursively collects line ranges from a change and its children.
287304
/// </summary>
288305
/// <remarks>
289-
/// Only collects line ranges from non-removed, non-container items. Removed items have
290-
/// line numbers from the OLD file, not the NEW file. Container items (Namespace, Class,
291-
/// Interface, etc.) have line ranges that span their children, which would cause
292-
/// false overlap detection.
306+
/// Only collects line ranges from non-removed, non-unchanged, non-container items.
307+
/// Removed items have line numbers from the OLD file, not the NEW file.
308+
/// Unchanged items are context lines, not actual changes.
309+
/// Container items (Namespace, Class, Interface, etc.) have line ranges that span
310+
/// their children, which would cause false overlap detection.
293311
/// Also skips parent nodes with children to avoid hierarchical parent-child overlaps.
294312
/// </remarks>
295313
private static void CollectLineRangesFromChange(ParsedChange change, List<LineRange> ranges)
296314
{
297315
// Skip removed items - their line numbers are from the old file context
298316
var isRemoved = change.ChangeType.Equals("removed", StringComparison.OrdinalIgnoreCase);
299317

318+
// Skip unchanged items - these are context lines, not actual changes
319+
var isUnchanged = change.ChangeType.Equals("unchanged", StringComparison.OrdinalIgnoreCase);
320+
300321
// Skip container types whose line ranges span their children
301322
var isContainer = change.Kind != null &&
302323
(change.Kind.Equals("namespace", StringComparison.OrdinalIgnoreCase) ||
@@ -311,8 +332,8 @@ private static void CollectLineRangesFromChange(ParsedChange change, List<LineRa
311332
// Parent nodes naturally encompass their children's line ranges.
312333
if (change.Children.Count == 0)
313334
{
314-
// Only collect from non-removed, non-container items (new file context, actual changes)
315-
if (change.LineRange != null && !isRemoved && !isContainer)
335+
// Only collect from non-removed, non-unchanged, non-container items (new file context, actual changes)
336+
if (change.LineRange != null && !isRemoved && !isUnchanged && !isContainer)
316337
{
317338
ranges.Add(change.LineRange);
318339
}

0 commit comments

Comments
 (0)