From b7034543f3f3e3c3d697a62d8d65d6ba67539f1a Mon Sep 17 00:00:00 2001 From: Yegor Stepanov Date: Wed, 2 Nov 2022 00:49:03 +0300 Subject: [PATCH] Fix #1715 with the ability to revert the old behavior --- src/BenchmarkDotNet/Reports/SummaryStyle.cs | 13 ++++++- .../Running/BenchmarkConverter.cs | 14 ++++--- .../Running/ClassicBenchmarkConverter.cs | 3 +- src/BenchmarkDotNet/Running/Descriptor.cs | 10 +++-- .../Running/BenchmarkConverterTests.cs | 38 ++++++++++++++++++- 5 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/BenchmarkDotNet/Reports/SummaryStyle.cs b/src/BenchmarkDotNet/Reports/SummaryStyle.cs index 4b92f2de8c..ce9dc81018 100644 --- a/src/BenchmarkDotNet/Reports/SummaryStyle.cs +++ b/src/BenchmarkDotNet/Reports/SummaryStyle.cs @@ -26,8 +26,11 @@ public class SummaryStyle : IEquatable public RatioStyle RatioStyle { get; } + public bool OldBehaviorForBenchmarkName { get; } + public SummaryStyle([CanBeNull] CultureInfo cultureInfo, bool printUnitsInHeader, SizeUnit sizeUnit, TimeUnit timeUnit, bool printUnitsInContent = true, - bool printZeroValuesInContent = false, int maxParameterColumnWidth = DefaultMaxParameterColumnWidth, RatioStyle ratioStyle = RatioStyle.Value) + bool printZeroValuesInContent = false, int maxParameterColumnWidth = DefaultMaxParameterColumnWidth, RatioStyle ratioStyle = RatioStyle.Value, + bool oldBehaviorForBenchmarkName = false) { if (maxParameterColumnWidth < DefaultMaxParameterColumnWidth) throw new ArgumentOutOfRangeException(nameof(maxParameterColumnWidth), $"{DefaultMaxParameterColumnWidth} is the minimum."); @@ -41,6 +44,7 @@ public SummaryStyle([CanBeNull] CultureInfo cultureInfo, bool printUnitsInHeader MaxParameterColumnWidth = maxParameterColumnWidth; RatioStyle = ratioStyle; CodeSizeUnit = SizeUnit.B; + OldBehaviorForBenchmarkName = oldBehaviorForBenchmarkName; } public SummaryStyle WithTimeUnit(TimeUnit timeUnit) @@ -61,6 +65,9 @@ public SummaryStyle WithCultureInfo(CultureInfo cultureInfo) public SummaryStyle WithRatioStyle(RatioStyle ratioStyle) => new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, ratioStyle); + public SummaryStyle WithOldBehaviorForBenchmarkName(bool oldBehaviorForBenchmarkName) + => new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, RatioStyle, oldBehaviorForBenchmarkName); + public bool Equals(SummaryStyle other) { if (ReferenceEquals(null, other)) @@ -75,7 +82,8 @@ public bool Equals(SummaryStyle other) && Equals(CodeSizeUnit, other.CodeSizeUnit) && Equals(TimeUnit, other.TimeUnit) && MaxParameterColumnWidth == other.MaxParameterColumnWidth - && RatioStyle == other.RatioStyle; + && RatioStyle == other.RatioStyle + && OldBehaviorForBenchmarkName == other.OldBehaviorForBenchmarkName; } public override bool Equals(object obj) => obj is SummaryStyle summary && Equals(summary); @@ -92,6 +100,7 @@ public override int GetHashCode() hashCode = (hashCode * 397) ^ (TimeUnit != null ? TimeUnit.GetHashCode() : 0); hashCode = (hashCode * 397) ^ MaxParameterColumnWidth; hashCode = (hashCode * 397) ^ RatioStyle.GetHashCode(); + hashCode = (hashCode * 397) ^ OldBehaviorForBenchmarkName.GetHashCode(); return hashCode; } } diff --git a/src/BenchmarkDotNet/Running/BenchmarkConverter.cs b/src/BenchmarkDotNet/Running/BenchmarkConverter.cs index 754cf9f9d1..371149b12a 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkConverter.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkConverter.cs @@ -51,7 +51,7 @@ private static BenchmarkRunInfo MethodsToBenchmarksWithFullConfig(Type type, Met var iterationSetupMethods = GetAttributedMethods(allMethods, "IterationSetup"); var iterationCleanupMethods = GetAttributedMethods(allMethods, "IterationCleanup"); - var targets = GetTargets(benchmarkMethods, type, globalSetupMethods, globalCleanupMethods, iterationSetupMethods, iterationCleanupMethods).ToArray(); + var targets = GetTargets(benchmarkMethods, type, globalSetupMethods, globalCleanupMethods, iterationSetupMethods, iterationCleanupMethods, configPerType.SummaryStyle.OldBehaviorForBenchmarkName).ToArray(); var parameterDefinitions = GetParameterDefinitions(type); var parameterInstancesList = parameterDefinitions.Expand(configPerType.SummaryStyle); @@ -115,7 +115,8 @@ private static IEnumerable GetTargets( Tuple[] globalSetupMethods, Tuple[] globalCleanupMethods, Tuple[] iterationSetupMethods, - Tuple[] iterationCleanupMethods) + Tuple[] iterationCleanupMethods, + bool oldBehaviorForBenchmarkName) { return targetMethods .Select(methodInfo => CreateDescriptor(type, @@ -125,7 +126,8 @@ private static IEnumerable GetTargets( GetTargetedMatchingMethod(methodInfo, iterationSetupMethods), GetTargetedMatchingMethod(methodInfo, iterationCleanupMethods), methodInfo.ResolveAttribute(), - targetMethods)); + targetMethods, + oldBehaviorForBenchmarkName)); } private static MethodInfo GetTargetedMatchingMethod(MethodInfo benchmarkMethod, Tuple[] methods) @@ -152,7 +154,8 @@ private static Descriptor CreateDescriptor( MethodInfo iterationSetupMethod, MethodInfo iterationCleanupMethod, BenchmarkAttribute attr, - MethodInfo[] targetMethods) + MethodInfo[] targetMethods, + bool oldBehaviorForBenchmarkName) { var target = new Descriptor( type, @@ -165,7 +168,8 @@ private static Descriptor CreateDescriptor( baseline: attr.Baseline, categories: GetCategories(methodInfo), operationsPerInvoke: attr.OperationsPerInvoke, - methodIndex: Array.IndexOf(targetMethods, methodInfo)); + methodIndex: Array.IndexOf(targetMethods, methodInfo), + oldBehaviorForBenchmarkName: oldBehaviorForBenchmarkName); AssertMethodHasCorrectSignature("Benchmark", methodInfo); AssertMethodIsAccessible("Benchmark", methodInfo); AssertMethodIsNotGeneric("Benchmark", methodInfo); diff --git a/src/BenchmarkDotNet/Running/ClassicBenchmarkConverter.cs b/src/BenchmarkDotNet/Running/ClassicBenchmarkConverter.cs index 15e01156da..d9905ce25c 100644 --- a/src/BenchmarkDotNet/Running/ClassicBenchmarkConverter.cs +++ b/src/BenchmarkDotNet/Running/ClassicBenchmarkConverter.cs @@ -114,7 +114,8 @@ public static BenchmarkRunInfo[] SourceToBenchmarks(string source, IConfig confi return BenchmarkCase.Create( new Descriptor(target.Type, target.WorkloadMethod, target.GlobalSetupMethod, target.GlobalCleanupMethod, target.IterationSetupMethod, target.IterationCleanupMethod, - target.WorkloadMethodDisplayInfo, benchmarkContent, target.Baseline, target.Categories, target.OperationsPerInvoke), + target.WorkloadMethodDisplayInfo, benchmarkContent, target.Baseline, target.Categories, target.OperationsPerInvoke, + oldBehaviorForBenchmarkName: config?.SummaryStyle?.OldBehaviorForBenchmarkName ?? false), b.Job, b.Parameters, b.Config); diff --git a/src/BenchmarkDotNet/Running/Descriptor.cs b/src/BenchmarkDotNet/Running/Descriptor.cs index b66e63872b..f3e4d2373d 100644 --- a/src/BenchmarkDotNet/Running/Descriptor.cs +++ b/src/BenchmarkDotNet/Running/Descriptor.cs @@ -41,7 +41,8 @@ public Descriptor( bool baseline = false, string[] categories = null, int operationsPerInvoke = 1, - int methodIndex = 0) + int methodIndex = 0, + bool oldBehaviorForBenchmarkName = false) { Type = type; WorkloadMethod = workloadMethod; @@ -51,7 +52,7 @@ public Descriptor( IterationCleanupMethod = iterationCleanupMethod; OperationsPerInvoke = operationsPerInvoke; AdditionalLogic = additionalLogic ?? string.Empty; - WorkloadMethodDisplayInfo = FormatDescription(description) ?? workloadMethod?.Name ?? "Untitled"; + WorkloadMethodDisplayInfo = FormatDescription(description, oldBehaviorForBenchmarkName) ?? workloadMethod?.Name ?? "Untitled"; Baseline = baseline; Categories = categories ?? Array.Empty(); MethodIndex = methodIndex; @@ -59,8 +60,11 @@ public Descriptor( public override string ToString() => DisplayInfo; - private static string FormatDescription([CanBeNull] string description) + private static string FormatDescription([CanBeNull] string description, bool oldBehaviorForBenchmarkName) { + if (!oldBehaviorForBenchmarkName) + return description; + var specialSymbols = new[] { ' ', '\'', '[', ']' }; return description != null && specialSymbols.Any(description.Contains) ? "'" + description + "'" diff --git a/tests/BenchmarkDotNet.Tests/Running/BenchmarkConverterTests.cs b/tests/BenchmarkDotNet.Tests/Running/BenchmarkConverterTests.cs index 5e9f9072e4..d720236adf 100644 --- a/tests/BenchmarkDotNet.Tests/Running/BenchmarkConverterTests.cs +++ b/tests/BenchmarkDotNet.Tests/Running/BenchmarkConverterTests.cs @@ -4,6 +4,7 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Reports; using BenchmarkDotNet.Running; using Perfolizer.Mathematics.OutlierDetection; using Xunit; @@ -278,5 +279,40 @@ public class PrivateIterationCleanup [IterationCleanup] private void X() { } [Benchmark] public void A() { } } + + [Fact] + public void BenchmarkNameWithSpecialCharactersIsNotEscapedByDefault() + { + var info = BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkNamesWithSpecialCharacters)); + + Assert.Equal("with space", info.BenchmarksCases[0].Descriptor.WorkloadMethodDisplayInfo); + Assert.Equal("na\'me", info.BenchmarksCases[1].Descriptor.WorkloadMethodDisplayInfo); + Assert.Equal("[name]", info.BenchmarksCases[2].Descriptor.WorkloadMethodDisplayInfo); + } + + [Fact] + public void OldBenchmarkNameBehaviorCanBeEnabledWithSummaryStyle() + { + var config = DefaultConfig.Instance.WithSummaryStyle( + SummaryStyle.Default.WithOldBehaviorForBenchmarkName(true)); + + var info = BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkNamesWithSpecialCharacters), config); + + Assert.Equal("'with space'", info.BenchmarksCases[0].Descriptor.WorkloadMethodDisplayInfo); + Assert.Equal("'na\'me'", info.BenchmarksCases[1].Descriptor.WorkloadMethodDisplayInfo); + Assert.Equal("'[name]'", info.BenchmarksCases[2].Descriptor.WorkloadMethodDisplayInfo); + } + + public sealed class BenchmarkNamesWithSpecialCharacters + { + [Benchmark(Description = "with space")] + public void A() { } + + [Benchmark(Description = "na\'me")] + public void B() { } + + [Benchmark(Description = "[name]")] + public void C() { } + } } -} +} \ No newline at end of file