Skip to content

Commit

Permalink
Show differences when only end-of-line changes
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Jun 28, 2021
1 parent db69a5b commit c300880
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DiffPlex;
using DiffPlex.DiffBuilder;
using DiffPlex.DiffBuilder.Model;
using DiffPlex.Model;

namespace Microsoft.CodeAnalysis.Testing
{
Expand All @@ -14,6 +18,9 @@ namespace Microsoft.CodeAnalysis.Testing
/// </summary>
public static class IVerifierExtensions
{
private static readonly InlineDiffBuilder s_diffWithoutLineEndings = new InlineDiffBuilder(new Differ());
private static readonly InlineDiffBuilder s_diffWithLineEndings = new InlineDiffBuilder(new DifferWithLineEndings());

/// <summary>
/// Asserts that two strings are equal, and prints a diff between the two if they are not.
/// </summary>
Expand All @@ -27,14 +34,19 @@ public static void EqualOrDiff(this IVerifier verifier, string expected, string

if (expected != actual)
{
var diffBuilder = new InlineDiffBuilder(new Differ());
var diff = diffBuilder.BuildDiffModel(expected, actual, ignoreWhitespace: false);
var diff = s_diffWithoutLineEndings.BuildDiffModel(expected, actual, ignoreWhitespace: false);
var messageBuilder = new StringBuilder();
messageBuilder.AppendLine(
string.IsNullOrEmpty(message)
? "Actual and expected values differ. Expected shown in baseline of diff:"
: message);

if (!diff.Lines.Any(line => line.Type == ChangeType.Inserted || line.Type == ChangeType.Deleted))
{
// We have a failure only caused by line ending differences; recalculate with line endings visible
diff = s_diffWithLineEndings.BuildDiffModel(expected, actual, ignoreWhitespace: false);
}

foreach (var line in diff.Lines)
{
switch (line.Type)
Expand All @@ -56,5 +68,80 @@ public static void EqualOrDiff(this IVerifier verifier, string expected, string
verifier.Fail(messageBuilder.ToString());
}
}

private class DifferWithLineEndings : IDiffer
{
private const string CarriageReturnText = "<CR>";
private const string LineFeedText = "<LF>";

private static readonly char[] s_endOfLineCharacters = { '\r', '\n' };
private static readonly Differ s_differ = new Differ();

public DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace)
=> s_differ.CreateCharacterDiffs(oldText, newText, ignoreWhitespace);

public DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase)
=> s_differ.CreateCharacterDiffs(oldText, newText, ignoreWhitespace, ignoreCase);

public DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, Func<string, string[]> chunker)
=> s_differ.CreateCustomDiffs(oldText, newText, ignoreWhiteSpace, chunker);

public DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, Func<string, string[]> chunker)
=> s_differ.CreateCustomDiffs(oldText, newText, ignoreWhiteSpace, ignoreCase, chunker);

public DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace)
=> CreateLineDiffs(oldText, newText, ignoreWhitespace, ignoreCase: false);

public DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase)
{
Func<string, string[]> chunker = s =>
{
var lines = new List<string>();

var nextChar = 0;
while (nextChar < s.Length)
{
var nextEol = s.IndexOfAny(s_endOfLineCharacters, nextChar);
if (nextEol == -1)
{
lines.Add(s.Substring(nextChar));
break;
}

var currentLine = s.Substring(nextChar, nextEol - nextChar);

switch (s[nextEol])
{
case '\r':
currentLine += CarriageReturnText;
if (nextEol < s.Length - 1 && s[nextEol + 1] == '\n')
{
currentLine += LineFeedText;
nextEol++;
}

break;

case '\n':
currentLine += LineFeedText;
break;
}

lines.Add(currentLine);
nextChar = nextEol + 1;
}

return lines.ToArray();
};

return CreateCustomDiffs(oldText, newText, ignoreWhitespace, ignoreCase, chunker);
}

public DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, char[] separators)
=> s_differ.CreateWordDiffs(oldText, newText, ignoreWhitespace, separators);

public DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase, char[] separators)
=> s_differ.CreateWordDiffs(oldText, newText, ignoreWhitespace, ignoreCase, separators);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using Xunit;

namespace Microsoft.CodeAnalysis.Testing
{
public class VerifierExtensionsTests
{
[Fact]
[WorkItem(876, "https://github.com/dotnet/roslyn-sdk/issues/876")]
public void VerifyContentWithMixedLineEndings1()
{
var baseline =
"Line 1\r\n"
+ "Line 2\r\n"
+ "Line 3\r\n"
+ "Line 4\r\n"
+ "Line 5\r\n"
+ "Line 6\r\n";
var modified =
"Line 1\r"
+ "Line 2\r\n"
+ "Line 3\n"
+ "Line 4\r"
+ "Line 5\r\n"
+ "Line 6\n";

var exception = Assert.Throws<InvalidOperationException>(() => new DefaultVerifier().EqualOrDiff(baseline, modified));
Assert.Equal(
$"Actual and expected values differ. Expected shown in baseline of diff:{Environment.NewLine}"
+ $"-Line 1<CR><LF>{Environment.NewLine}"
+ $"+Line 1<CR>{Environment.NewLine}"
+ $" Line 2<CR><LF>{Environment.NewLine}"
+ $"-Line 3<CR><LF>{Environment.NewLine}"
+ $"-Line 4<CR><LF>{Environment.NewLine}"
+ $"+Line 3<LF>{Environment.NewLine}"
+ $"+Line 4<CR>{Environment.NewLine}"
+ $" Line 5<CR><LF>{Environment.NewLine}"
+ $"-Line 6<CR><LF>{Environment.NewLine}"
+ $"+Line 6<LF>{Environment.NewLine}",
exception.Message);
}

[Fact]
[WorkItem(876, "https://github.com/dotnet/roslyn-sdk/issues/876")]
public void VerifyContentWithMixedLineEnding2()
{
var baseline =
"Line 1\n"
+ "Line 2\n"
+ "Line 3\n"
+ "Line 4\n"
+ "Line 5\n"
+ "Line 6\n";
var modified =
"Line 1\r"
+ "Line 2\r\n"
+ "Line 3\n"
+ "Line 4\r"
+ "Line 5\r\n"
+ "Line 6\n";

var exception = Assert.Throws<InvalidOperationException>(() => new DefaultVerifier().EqualOrDiff(baseline, modified));
Assert.Equal(
$"Actual and expected values differ. Expected shown in baseline of diff:{Environment.NewLine}"
+ $"-Line 1<LF>{Environment.NewLine}"
+ $"-Line 2<LF>{Environment.NewLine}"
+ $"+Line 1<CR>{Environment.NewLine}"
+ $"+Line 2<CR><LF>{Environment.NewLine}"
+ $" Line 3<LF>{Environment.NewLine}"
+ $"-Line 4<LF>{Environment.NewLine}"
+ $"-Line 5<LF>{Environment.NewLine}"
+ $"+Line 4<CR>{Environment.NewLine}"
+ $"+Line 5<CR><LF>{Environment.NewLine}"
+ $" Line 6<LF>{Environment.NewLine}",
exception.Message);
}
}
}

0 comments on commit c300880

Please sign in to comment.