Skip to content

Commit

Permalink
Merge pull request #1147 from Socolin/fix-color
Browse files Browse the repository at this point in the history
Only un-escape unicode character encoding color codes Fix #1124
  • Loading branch information
OsirisTerje authored Sep 4, 2024
2 parents 80ace95 + 99e3538 commit 13f8d23
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 44 deletions.
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.UnEscapeUnicodeCharacters();
public string Output => Node.SelectSingleNode("output")?.InnerText.UnEscapeUnicodeColorCodesCharacters();


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.UnEscapeUnicodeCharacters();
var description = attachment.SelectSingleNode("description")?.InnerText.UnEscapeUnicodeColorCodesCharacters();
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.UnEscapeUnicodeCharacters();
StackTrace = failureNode.SelectSingleNode("stack-trace")?.InnerText.UnEscapeUnicodeCharacters();
FailureMessage = failureNode.SelectSingleNode("message")?.InnerText.UnEscapeUnicodeColorCodesCharacters();
StackTrace = failureNode.SelectSingleNode("stack-trace")?.InnerText.UnEscapeUnicodeColorCodesCharacters();
}
ReasonMessage = Node.SelectSingleNode("reason/message")?.InnerText.UnEscapeUnicodeCharacters();
ReasonMessage = Node.SelectSingleNode("reason/message")?.InnerText.UnEscapeUnicodeColorCodesCharacters();
}

public string ReasonMessage { get; }
Expand Down
8 changes: 4 additions & 4 deletions src/NUnitTestAdapter/NUnitEngine/NUnitTestEventTestCase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ public NUnitTestEventTestCase(XmlNode node)
if (failureNode != null)
{
Failure = new NUnitFailure(
failureNode.SelectSingleNode("message")?.InnerText.UnEscapeUnicodeCharacters(),
failureNode.SelectSingleNode("stack-trace")?.InnerText.UnEscapeUnicodeCharacters());
failureNode.SelectSingleNode("message")?.InnerText.UnEscapeUnicodeColorCodesCharacters(),
failureNode.SelectSingleNode("stack-trace")?.InnerText.UnEscapeUnicodeColorCodesCharacters());
}

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

Expand All @@ -73,7 +73,7 @@ public string StackTrace
int i = 1;
foreach (XmlNode assertionStacktraceNode in Node.SelectNodes("assertions/assertion/stack-trace"))
{
stackTrace += $"{i++}) " + assertionStacktraceNode.InnerText.UnEscapeUnicodeCharacters();
stackTrace += $"{i++}) " + assertionStacktraceNode.InnerText.UnEscapeUnicodeColorCodesCharacters();
stackTrace += "\n";
}

Expand Down
102 changes: 71 additions & 31 deletions src/NUnitTestAdapter/NUnitEngine/UnicodeEscapeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,90 @@ namespace NUnit.VisualStudio.TestAdapter.NUnitEngine;

internal static class UnicodeEscapeHelper
{
public static string UnEscapeUnicodeCharacters(this string text)
private const int EscapeAsciiValue = 0x1B;

public static string UnEscapeUnicodeColorCodesCharacters(this string text)
{
if (text == null)
return null;
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;
// 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++)
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))
{
char c = text[position];
if (c == '\\' && TryUnEscapeOneCharacter(text, position, out var escapedChar, out var extraCharacterRead))
{
stringBuilder.Append(escapedChar);
position += extraCharacterRead;
}
else
{
stringBuilder.Append(c);
}
stringBuilder.Append(escapedChar);
position += extraCharacterRead;
}
else
{
stringBuilder.Append(c);
}

return stringBuilder.ToString();
}

return stringBuilder.ToString();
}

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

extraCharacterRead = 0;
escapedChar = '\0';
if (position + unicodeEscapeSample.Length >= text.Length)
return false;
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;

extraCharacterRead = unicodeEscapeSample.Length;
if (!int.TryParse(text.Substring(position + 2, unicodeEscapeSample.Length - 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var escapeValue))
return false;
// Here we only want to escape color escape character when used in a context of a ANSI color code
// See https://github.com/nunit/nunit3-vs-adapter/issues/1124 for more information.
if (escapeValue != EscapeAsciiValue)
return false;

if (!IsAnsiColorCodeSequence(text, position + extraCharacterRead + 1))
return false;

escapedChar = (char)escapeValue;

escapedChar = (char)escapeValue;
return true;
}

return true;
private static bool IsAnsiColorCodeSequence(string text, int position)
{
var start = false;
while (position < text.Length)
{
var c = text[position++];
// Look for the begining [
if (c == '[' && !start)
{
start = true;
continue;
}

// Found the 'm' at the end
if (c == 'm' && start)
return true;

// [ was not found
if (!start)
return false;

// Ignore all number and ;
var isDigit = c is >= '0' and <= '9';
if (!isDigit && c != ';')
return false;
}

// At the end without the ending 'm'
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ namespace NUnit.VisualStudio.TestAdapter.Tests.NUnitEngineTests;

public class UnicodeEscapeHelperTests
{
[TestCase("\\u001b", "\u001b")]
[TestCase("\\u001b", "\\u001b")]
[TestCase("\\u001", "\\u001")]
[TestCase("\\u01", "\\u01")]
[TestCase("\\u1", "\\u1")]
[TestCase("\\u001b6", "\u001b6")]
[TestCase("\\u001b6", "\\u001b6")]
[TestCase("\\u001b[0m", "\u001b[0m")]
[TestCase("\\u001b[36m", "\u001b[36m")]
[TestCase("\\u001b[48;5;122mTest", "\u001b[48;5;122mTest")]
[TestCase("some-text", "some-text")]
public void UnEscapeUnicodeCharacters_ShouldReplaceBackslashU(string value, string expected)
public void UnEscapeUnicodeColorCodesCharactersShouldReplaceBackslashU(string value, string expected)
{
Assert.That(value.UnEscapeUnicodeCharacters(), Is.EqualTo(expected));
Assert.That(value.UnEscapeUnicodeColorCodesCharacters(), Is.EqualTo(expected));
}
}

0 comments on commit 13f8d23

Please sign in to comment.