diff --git a/resharper/src/xunitcontrib.runner.resharper.provider/UnitTestElementFactory.cs b/resharper/src/xunitcontrib.runner.resharper.provider/UnitTestElementFactory.cs
index 0e90ad9..66f51cf 100644
--- a/resharper/src/xunitcontrib.runner.resharper.provider/UnitTestElementFactory.cs
+++ b/resharper/src/xunitcontrib.runner.resharper.provider/UnitTestElementFactory.cs
@@ -7,6 +7,7 @@
using JetBrains.ReSharper.UnitTestFramework.Elements;
using Xunit.Sdk;
using JetBrains.Util;
+using XunitContrib.Runner.ReSharper.RemoteRunner;
namespace XunitContrib.Runner.ReSharper.UnitTestProvider
{
@@ -143,7 +144,8 @@ public XunitTestTheoryElement GetOrCreateTestTheory(IProject project, XunitTestM
private static string GetTestTheoryShortName(string theoryName, XunitTestMethodElement methodElement)
{
var prefix = methodElement.TypeName.FullName + ".";
- return theoryName.StartsWith(prefix) ? theoryName.Substring(prefix.Length) : theoryName;
+ var name = theoryName.StartsWith(prefix) ? theoryName.Substring(prefix.Length) : theoryName;
+ return DisplayNameUtil.Escape(name);
}
private static string GetTestTheoryId(XunitTestMethodElement methodElement, string shortName)
diff --git a/resharper/src/xunitcontrib.runner.resharper.runner/DisplayNameUtil.cs b/resharper/src/xunitcontrib.runner.resharper.runner/DisplayNameUtil.cs
new file mode 100644
index 0000000..fe5aba4
--- /dev/null
+++ b/resharper/src/xunitcontrib.runner.resharper.runner/DisplayNameUtil.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Text;
+
+namespace XunitContrib.Runner.ReSharper.RemoteRunner
+{
+ public class DisplayNameUtil
+ {
+ public static string Escape(string name)
+ {
+ var sb = new StringBuilder();
+ foreach (var c in name)
+ sb.Append(EscapeControlChar(c));
+
+ return sb.ToString();
+ }
+
+ private static string EscapeControlChar(char c)
+ {
+ switch (c)
+ {
+ case '\0':
+ return "\\0";
+ case '\a':
+ return "\\a";
+ case '\b':
+ return "\\b";
+ case '\f':
+ return "\\f";
+ case '\n':
+ return "\\n";
+ case '\r':
+ return "\\r";
+ case '\t':
+ return "\\t";
+ case '\v':
+ return "\\v";
+
+ case '\x0085':
+ case '\x2028':
+ case '\x2029':
+ return string.Format("\\x{0:X4}", (ushort)c);
+
+ default:
+ return char.IsControl(c) || (ushort)c > 128
+ ? String.Format("\\x{0:X4}", (ushort)c)
+ : c.ToString();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/resharper/src/xunitcontrib.runner.resharper.runner/TaskProvider.cs b/resharper/src/xunitcontrib.runner.resharper.runner/TaskProvider.cs
index 8a3e9ca..141a89d 100644
--- a/resharper/src/xunitcontrib.runner.resharper.runner/TaskProvider.cs
+++ b/resharper/src/xunitcontrib.runner.resharper.runner/TaskProvider.cs
@@ -102,7 +102,8 @@ public TheoryTaskInfo GetTheoryTask(string name, string type, string method)
private static string GetTheoryShortName(string name, string type)
{
var prefix = type + ".";
- return name.StartsWith(prefix) ? name.Substring(prefix.Length) : name;
+ var shortName = name.StartsWith(prefix) ? name.Substring(prefix.Length) : name;
+ return DisplayNameUtil.Escape(shortName);
}
private static bool IsTheory(string name, string type, string method)
diff --git a/resharper/src/xunitcontrib.runner.resharper.runner/xunitcontrib.runner.resharper.runner.8.2.csproj b/resharper/src/xunitcontrib.runner.resharper.runner/xunitcontrib.runner.resharper.runner.8.2.csproj
index a0e89d0..34f7a82 100644
--- a/resharper/src/xunitcontrib.runner.resharper.runner/xunitcontrib.runner.resharper.runner.8.2.csproj
+++ b/resharper/src/xunitcontrib.runner.resharper.runner/xunitcontrib.runner.resharper.runner.8.2.csproj
@@ -55,6 +55,7 @@
Properties\ProductInfo.ReSharper.8.2.cs
+
diff --git a/resharper/test/data/Runner/Gold/EscapedDataAttributeStrings.cs b/resharper/test/data/Runner/Gold/EscapedDataAttributeStrings.cs
new file mode 100644
index 0000000..3563465
--- /dev/null
+++ b/resharper/test/data/Runner/Gold/EscapedDataAttributeStrings.cs
@@ -0,0 +1,19 @@
+using Xunit;
+using Xunit.Extensions;
+
+namespace Foo
+{
+ public class PassingTheory
+ {
+ [Theory]
+ [InlineData("$ \\ \b \f \n \r \t \v")]
+ public void TestMethod(string value)
+ {
+ }
+ }
+}
+
+// xunit2 doesn't define Xunit.Extensions
+namespace Xunit.Extensions
+{
+}
diff --git a/resharper/test/data/Runner/Gold/EscapedDataAttributeStrings.xunit1.dll.gold b/resharper/test/data/Runner/Gold/EscapedDataAttributeStrings.xunit1.dll.gold
new file mode 100644
index 0000000..dcfb1dd
--- /dev/null
+++ b/resharper/test/data/Runner/Gold/EscapedDataAttributeStrings.xunit1.dll.gold
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resharper/test/data/Runner/Gold/EscapedDataAttributeStrings.xunit2.dll.gold b/resharper/test/data/Runner/Gold/EscapedDataAttributeStrings.xunit2.dll.gold
new file mode 100644
index 0000000..c625ac0
--- /dev/null
+++ b/resharper/test/data/Runner/Gold/EscapedDataAttributeStrings.xunit2.dll.gold
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resharper/test/src/tests/AcceptanceTests/Runner/OrderedFactGoldTests.cs b/resharper/test/src/tests/AcceptanceTests/Runner/OrderedFactGoldTests.cs
index 6896d01..34ec409 100644
--- a/resharper/test/src/tests/AcceptanceTests/Runner/OrderedFactGoldTests.cs
+++ b/resharper/test/src/tests/AcceptanceTests/Runner/OrderedFactGoldTests.cs
@@ -46,5 +46,11 @@ public void TestAmbiguouslyNamedTestMethods()
// TODO: This misses a test to continue running next class. Ordering.
DoOneTestWithStrictOrdering("AmbiguouslyNamedTestMethods");
}
+
+ [Test]
+ public void TestEscapedStringsInDataAttributes()
+ {
+ DoOneTestWithStrictOrdering("EscapedDataAttributeStrings");
+ }
}
}
\ No newline at end of file