Skip to content

Commit

Permalink
Pass non escaped strings to generated project (dotnet#2136)
Browse files Browse the repository at this point in the history
  • Loading branch information
YegorStepanov committed Oct 6, 2022
1 parent 95bb2aa commit ecb25bf
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 13 deletions.
8 changes: 2 additions & 6 deletions src/BenchmarkDotNet/Exporters/FullNameProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ private static string GetArgument(object argumentValue, Type argumentType)
case object[] array when array.Length == 1:
return GetArgument(array[0], argumentType);
case string text:
return $"\"{EscapeWhitespaces(text)}\"";
return text.EscapeSpecialCharacters(true);
case char character:
return $"'{character}'";
return character.EscapeSpecialCharacter(true);
case DateTime time:
return time.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK");
case Type type:
Expand Down Expand Up @@ -183,10 +183,6 @@ private static string GetArray(IEnumerable collection)
return buffer.ToString();
}

private static string EscapeWhitespaces(string text)
=> text.Replace("\t", "\\t")
.Replace("\r\n", "\\r\\n");

private static string GetTypeArgumentName(Type type)
{
if (Aliases.TryGetValue(type, out string alias))
Expand Down
10 changes: 10 additions & 0 deletions src/BenchmarkDotNet/Extensions/StringAndTextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ internal static string EscapeSpecialCharacters(this string str, bool quote)
return Microsoft.CodeAnalysis.CSharp.SymbolDisplay.FormatLiteral(str, quote);
}

/// <summary>
/// Escapes UNICODE control character
/// </summary>
/// <param name="c">char to escape</param>
/// <param name="quote">True to put (single) quotes around the character literal.</param>
internal static string EscapeSpecialCharacter(this char c, bool quote)
{
return Microsoft.CodeAnalysis.CSharp.SymbolDisplay.FormatLiteral(c, quote);
}

/// <summary>
/// replaces all invalid file name chars with their number representation
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/BenchmarkDotNet/Helpers/SourceCodeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ public static string ToSourceCode(object value)
case bool b:
return b.ToLowerCase();
case string text:
return $"$@\"{text.Replace("\"", "\"\"").Replace("{", "{{").Replace("}", "}}")}\"";
return text.EscapeSpecialCharacters(true);
case char c:
return c == '\\' ? "'\\\\'" : $"'{value}'";
return c.EscapeSpecialCharacter(true);
case float f:
return ToSourceCode(f);
case double d:
Expand Down
51 changes: 51 additions & 0 deletions tests/BenchmarkDotNet.IntegrationTests/ParamsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,57 @@ public class InvalidFileNamesInParams
public void Benchmark() => Console.WriteLine("// " + Field);
}

[Fact]
public void SpecialCharactersInStringAreSupported() => CanExecute<CompileSpecialCharactersInString>();

public class CompileSpecialCharactersInString
{
[Params("\0")] public string Null;
[Params("\t")] public string Tab;
[Params("\n")] public string NewLine;
[Params("\\")] public string Slash;
[Params("\"")] public string Quote;
[Params("\u0061")] public string Unicode;
[Params("{")] public string Bracket;

[Params("\n \0 \n")] public string Combo;

[Params("C:\\file1.txt")] public string Path1;
[Params(@"C:\file2.txt")] public string Path2;

[Benchmark]
public void Benchmark()
{
var isPassedAsSingleCharacter =
Null.Length == 1 &&
Tab.Length == 1 &&
NewLine.Length == 1 &&
Slash.Length == 1 &&
Quote.Length == 1 &&
Unicode.Length == 1 &&
Bracket.Length == 1;

if (!isPassedAsSingleCharacter)
throw new InvalidOperationException("Some Param has an invalid escaped string");
}
}

[Fact]
public void SpecialCharactersInCharAreSupported() => CanExecute<CompileSpecialCharactersInChar>();

public class CompileSpecialCharactersInChar
{
[Params('\0')] public char Null;
[Params('\t')] public char Tab;
[Params('\n')] public char NewLine;
[Params('\\')] public char Slash;
[Params('\"')] public char Quote;
[Params('\u0061')] public char Unicode;

[Benchmark]
public void Benchmark() { }
}

[Fact]
public void ParamsMustBeEscapedProperly() => CanExecute<NeedEscaping>();

Expand Down
39 changes: 34 additions & 5 deletions tests/BenchmarkDotNet.Tests/SourceCodeHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ namespace BenchmarkDotNet.Tests
// TODO: add decimal, typeof, CreateInstance, TimeValue, IntPtr, IFormattable
public class SourceCodeHelperTests
{
private ITestOutputHelper output;
private readonly ITestOutputHelper output;

public SourceCodeHelperTests(ITestOutputHelper output) => this.output = output;

[Theory]
[InlineData(null, "null")]
[InlineData(false, "false")]
[InlineData(true, "true")]
[InlineData("string", "$@\"string\"")]
[InlineData("string/\\", @"$@""string/\""")]
[InlineData("string", "\"string\"")]
[InlineData("string/\\", @"""string/\\""")]
[InlineData('a', "'a'")]
[InlineData('\\', "'\\\\'")]
[InlineData(0.123f, "0.123f")]
Expand All @@ -43,7 +43,7 @@ public void SupportsGuid()
[Fact]
public void CanEscapeJson()
{
const string expected = "$@\"{{ \"\"message\"\": \"\"Hello, World!\"\" }}\"";
const string expected = "\"{ \\\"message\\\": \\\"Hello, World!\\\" }\"";

var actual = SourceCodeHelper.ToSourceCode("{ \"message\": \"Hello, World!\" }");

Expand All @@ -53,11 +53,40 @@ public void CanEscapeJson()
[Fact]
public void CanEscapePath()
{
const string expected = @"$@""C:\Projects\BenchmarkDotNet\samples\BenchmarkDotNet.Samples""";
const string expected = @"""C:\\Projects\\BenchmarkDotNet\\samples\\BenchmarkDotNet.Samples""";

var actual = SourceCodeHelper.ToSourceCode(@"C:\Projects\BenchmarkDotNet\samples\BenchmarkDotNet.Samples");

Assert.Equal(expected, actual);
}

[Fact]
public void CanEscapeControlCharacters()
{
const string expected = @""" \0 \b \f \n \t \v \"" a a a a { } """;

var actual = SourceCodeHelper.ToSourceCode(" \0 \b \f \n \t \v \" \u0061 \x0061 \x61 \U00000061 { } ");

Assert.Equal(expected, actual);
}

[Theory]
[InlineData('\0', @"'\0'")]
[InlineData('\b', @"'\b'")]
[InlineData('\f', @"'\f'")]
[InlineData('\n', @"'\n'")]
[InlineData('\t', @"'\t'")]
[InlineData('\v', @"'\v'")]
[InlineData('\'', @"'\''")]
[InlineData('\u0061', "'a'")]
[InlineData('"', "'\"'")]
[InlineData('{', "'{'")]
[InlineData('}', "'}'")]
public void CanEscapeControlCharactersInChar(char original, string excepted)
{
var actual = SourceCodeHelper.ToSourceCode(original);

Assert.Equal(excepted, actual);
}
}
}

0 comments on commit ecb25bf

Please sign in to comment.