diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/IVerifierExtensions.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/IVerifierExtensions.cs
index 0bffc225c..c43333919 100644
--- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/IVerifierExtensions.cs
+++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/IVerifierExtensions.cs
@@ -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
{
@@ -14,6 +18,9 @@ namespace Microsoft.CodeAnalysis.Testing
///
public static class IVerifierExtensions
{
+ private static readonly InlineDiffBuilder s_diffWithoutLineEndings = new InlineDiffBuilder(new Differ());
+ private static readonly InlineDiffBuilder s_diffWithLineEndings = new InlineDiffBuilder(new DifferWithLineEndings());
+
///
/// Asserts that two strings are equal, and prints a diff between the two if they are not.
///
@@ -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)
@@ -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 = "";
+ private const string LineFeedText = "";
+
+ 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 chunker)
+ => s_differ.CreateCustomDiffs(oldText, newText, ignoreWhiteSpace, chunker);
+
+ public DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, Func 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 chunker = s =>
+ {
+ var lines = new List();
+
+ 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);
+ }
}
}
diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/VerifierExtensionsTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/VerifierExtensionsTests.cs
new file mode 100644
index 000000000..eb850de69
--- /dev/null
+++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/VerifierExtensionsTests.cs
@@ -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(() => new DefaultVerifier().EqualOrDiff(baseline, modified));
+ Assert.Equal(
+ $"Actual and expected values differ. Expected shown in baseline of diff:{Environment.NewLine}"
+ + $"-Line 1{Environment.NewLine}"
+ + $"+Line 1{Environment.NewLine}"
+ + $" Line 2{Environment.NewLine}"
+ + $"-Line 3{Environment.NewLine}"
+ + $"-Line 4{Environment.NewLine}"
+ + $"+Line 3{Environment.NewLine}"
+ + $"+Line 4{Environment.NewLine}"
+ + $" Line 5{Environment.NewLine}"
+ + $"-Line 6{Environment.NewLine}"
+ + $"+Line 6{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(() => new DefaultVerifier().EqualOrDiff(baseline, modified));
+ Assert.Equal(
+ $"Actual and expected values differ. Expected shown in baseline of diff:{Environment.NewLine}"
+ + $"-Line 1{Environment.NewLine}"
+ + $"-Line 2{Environment.NewLine}"
+ + $"+Line 1{Environment.NewLine}"
+ + $"+Line 2{Environment.NewLine}"
+ + $" Line 3{Environment.NewLine}"
+ + $"-Line 4{Environment.NewLine}"
+ + $"-Line 5{Environment.NewLine}"
+ + $"+Line 4{Environment.NewLine}"
+ + $"+Line 5{Environment.NewLine}"
+ + $" Line 6{Environment.NewLine}",
+ exception.Message);
+ }
+ }
+}