Skip to content

Commit

Permalink
Unescape \u in test output so ANSI color codes can be interpreted cor…
Browse files Browse the repository at this point in the history
…rectly (#984)

* Unescape \u when reading test output. This fix colors when running test in `dotnet test`

* Disable windows specific test when running on linux

* Add Unix test alternative for DumpXmlTests.ThatPathIsCorrectlyParsedInDiscoveryPhase

* Fix azure devops pipeline on windows

* Fix build / test pipeline on azure devops (Linux)

* Convert UnEscapeUnicodeCharacters to an extension method
  • Loading branch information
Socolin authored Jun 26, 2022
1 parent be785cd commit 6a6e422
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 17 deletions.
15 changes: 13 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ jobs:
pool:
vmImage: 'windows-latest'
steps:
- task: UseDotNet@2
displayName: Install .NET Core 2.1 runtime
inputs:
packageType: runtime
version: 2.1.x
- powershell: .\build.ps1 --target=CI --configuration=$(BuildConfiguration)
displayName: Build, package, and test

Expand All @@ -17,9 +22,15 @@ jobs:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
displayName: Install required .NET Core SDK
displayName: Install .NET Core 6.0 sdk
inputs:
useGlobalJson: true
version: 6.x

- task: UseDotNet@2
displayName: Install .NET Core 5.0 runtime
inputs:
packageType: runtime
version: 5.x

- task: UseDotNet@2
displayName: Install .NET Core 3.1 runtime
Expand Down
4 changes: 2 additions & 2 deletions src/NUnitTestAdapter/NUnitEngine/NUnitTestEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ protected NUnitTestEvent(XmlNode node) : base(node)

public string MethodName => Node.GetAttribute("methodname");
public string ClassName => Node.GetAttribute("classname");
public string Output => Node.SelectSingleNode("output")?.InnerText;
public string Output => Node.SelectSingleNode("output")?.InnerText.UnEscapeUnicodeCharacters();


public CheckedTime StartTime()
Expand Down Expand Up @@ -165,7 +165,7 @@ public IEnumerable<NUnitAttachment> NUnitAttachments
foreach (XmlNode attachment in Node.SelectNodes("attachments/attachment"))
{
var path = attachment.SelectSingleNode("filePath")?.InnerText ?? string.Empty;
var description = attachment.SelectSingleNode("description")?.InnerText;
var description = attachment.SelectSingleNode("description")?.InnerText.UnEscapeUnicodeCharacters();
nUnitAttachments.Add(new NUnitAttachment(path, description));
}
return nUnitAttachments;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ public NUnitTestEventSuiteFinished(XmlNode node) : base(node)
var failureNode = Node.SelectSingleNode("failure");
if (failureNode != null)
{
FailureMessage = failureNode.SelectSingleNode("message")?.InnerText;
StackTrace = failureNode.SelectSingleNode("stack-trace")?.InnerText;
FailureMessage = failureNode.SelectSingleNode("message")?.InnerText.UnEscapeUnicodeCharacters();
StackTrace = failureNode.SelectSingleNode("stack-trace")?.InnerText.UnEscapeUnicodeCharacters();
}
ReasonMessage = Node.SelectSingleNode("reason/message")?.InnerText;
ReasonMessage = Node.SelectSingleNode("reason/message")?.InnerText.UnEscapeUnicodeCharacters();
}

public string ReasonMessage { get; }
Expand Down
25 changes: 15 additions & 10 deletions src/NUnitTestAdapter/NUnitEngine/NUnitTestEventTestCase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System.Xml;
using System;
using System.Globalization;
using System.Text;
using System.Xml;

namespace NUnit.VisualStudio.TestAdapter.NUnitEngine
{
Expand All @@ -17,36 +20,38 @@ public interface INUnitTestEventTestCase : INUnitTestEvent
}



/// <summary>
/// Handles the NUnit 'test-case' event.
/// </summary>
public class NUnitTestEventTestCase : NUnitTestEvent, INUnitTestEventTestCase
{
public NUnitTestEventTestCase(INUnitTestEventForXml node) : this(node.Node)
public NUnitTestEventTestCase(INUnitTestEventForXml node)
: this(node.Node)
{
}

public NUnitTestEventTestCase(string testEvent) : this(XmlHelper.CreateXmlNode(testEvent))
public NUnitTestEventTestCase(string testEvent)
: this(XmlHelper.CreateXmlNode(testEvent))
{
}

public NUnitFailure Failure { get; }

public NUnitTestEventTestCase(XmlNode node) : base(node)
public NUnitTestEventTestCase(XmlNode node)
: base(node)
{
if (node.Name != "test-case")
throw new NUnitEventWrongTypeException($"Expected 'test-case', got {node.Name}");
var failureNode = Node.SelectSingleNode("failure");
if (failureNode != null)
{
Failure = new NUnitFailure(
failureNode.SelectSingleNode("message")?.InnerText,
failureNode.SelectSingleNode("stack-trace")?.InnerText);
failureNode.SelectSingleNode("message")?.InnerText.UnEscapeUnicodeCharacters(),
failureNode.SelectSingleNode("stack-trace")?.InnerText.UnEscapeUnicodeCharacters());
}
ReasonMessage = Node.SelectSingleNode("reason/message")?.InnerText;
}

ReasonMessage = Node.SelectSingleNode("reason/message")?.InnerText.UnEscapeUnicodeCharacters();
}
public string ReasonMessage { get; }

public bool HasReason => !string.IsNullOrEmpty(ReasonMessage);
Expand All @@ -62,7 +67,7 @@ public string StackTrace
string stackTrace = string.Empty;
foreach (XmlNode assertionStacktraceNode in Node.SelectNodes("assertions/assertion/stack-trace"))
{
stackTrace += assertionStacktraceNode.InnerText;
stackTrace += assertionStacktraceNode.InnerText.UnEscapeUnicodeCharacters();
}

return stackTrace;
Expand Down
56 changes: 56 additions & 0 deletions src/NUnitTestAdapter/NUnitEngine/UnicodeEscapeHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Globalization;
using System.Text;

namespace NUnit.VisualStudio.TestAdapter.NUnitEngine
{
internal static class UnicodeEscapeHelper
{
public static string UnEscapeUnicodeCharacters(this string text)
{
if (text == null)
return null;

// Small optimization, if there are no "\u", then there is no need to rewrite the string
var firstEscapeIndex = text.IndexOf("\\u", StringComparison.Ordinal);
if (firstEscapeIndex == -1)
return text;

var stringBuilder = new StringBuilder(text.Substring(0, firstEscapeIndex));
for (var position = firstEscapeIndex; position < text.Length; position++)
{
char c = text[position];
if (c == '\\' && TryUnEscapeOneCharacter(text, position, out var escapedChar, out var extraCharacterRead))
{
stringBuilder.Append(escapedChar);
position += extraCharacterRead;
}
else
{
stringBuilder.Append(c);
}
}

return stringBuilder.ToString();
}

private static bool TryUnEscapeOneCharacter(string text, int position, out char escapedChar, out int extraCharacterRead)
{
const string unicodeEscapeSample = "u0000";

extraCharacterRead = 0;
escapedChar = '\0';
if (position + unicodeEscapeSample.Length >= text.Length)
return false;


extraCharacterRead = unicodeEscapeSample.Length;
if (!int.TryParse(text.Substring(position + 2, unicodeEscapeSample.Length - 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var escapeValue))
return false;

escapedChar = (char)escapeValue;

return true;
}
}
}
1 change: 1 addition & 0 deletions src/NUnitTestAdapterTests/CurrentDirectoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class CurrentDirectoryTests
[TestCase(@"C:\Windows\Whatever")]
[TestCase(@"C:\Program Files\Something")]
[TestCase(@"C:\Program Files (x86)\Something")]
[Platform("Win")]
public void ThatWeFindForbiddenFolders(string folder)
{
var sut = new NUnit3TestExecutor();
Expand Down
14 changes: 14 additions & 0 deletions src/NUnitTestAdapterTests/DumpXmlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public void ThatRandomNameContainsValidCharacters()
[TestCase(@"C:\MyFolder\Whatever.dll", @"C:\MyFolder\Dump\D_Whatever.dll.dump")]
[TestCase(@"C:\MyFolder\Whatever.dll", @"C:\MyFolder\Dump")]
[TestCase(@"C:\MyFolder\Whatever.dll", @"C:\MyFolder")]
[Platform("Win")]
public void ThatPathIsCorrectlyParsedInDiscoveryPhase(string path, string expected)
{
var file = Substitute.For<IFile>();
Expand All @@ -74,6 +75,19 @@ public void ThatPathIsCorrectlyParsedInDiscoveryPhase(string path, string expect
file.Received().WriteAllText(Arg.Is<string>(o => o.StartsWith(expected, StringComparison.OrdinalIgnoreCase)), Arg.Any<string>());
}

[TestCase(@"/some/Folder/Whatever.dll", @"/some/Folder/Dump/D_Whatever.dll.dump")]
[TestCase(@"/some/Folder/Whatever.dll", @"/some/Folder/Dump")]
[TestCase(@"/some/Folder/Whatever.dll", @"/some/Folder")]
[Platform("Unix")]
public void ThatPathIsCorrectlyParsedInDiscoveryPhaseOnUnix(string path, string expected)
{
var file = Substitute.For<IFile>();
var sut = new DumpXml(path, file);
sut.AddString("whatever");
sut.DumpForDiscovery();
file.Received().WriteAllText(Arg.Is<string>(o => o.StartsWith(expected, StringComparison.OrdinalIgnoreCase)), Arg.Any<string>());
}


[Test]
public void ThatEmptyContainsHeaders()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using NUnit.Framework;
using NUnit.VisualStudio.TestAdapter.NUnitEngine;

namespace NUnit.VisualStudio.TestAdapter.Tests.NUnitEngineTests
{
public class UnicodeEscapeHelperTests
{
[TestCase("\\u001b", "\u001b")]
[TestCase("\\u001", "\\u001")]
[TestCase("\\u01", "\\u01")]
[TestCase("\\u1", "\\u1")]
[TestCase("\\u001b6", "\u001b6")]
[TestCase("some-text", "some-text")]
public void UnEscapeUnicodeCharacters_ShouldReplaceBackslashU(string value, string expected)
{
Assert.That(value.UnEscapeUnicodeCharacters(), Is.EqualTo(expected));
}
}
}

0 comments on commit 6a6e422

Please sign in to comment.