Skip to content

Commit e1c8cb8

Browse files
authored
Filter hint improvements (#1644)
* dont allow for --filter typeName anymore, always use globs doc does not need to be updated as it was not recommended and not mentioned anywhere * change the filter suggestions to use full names and globs only
1 parent 8c28c87 commit e1c8cb8

File tree

5 files changed

+53
-42
lines changed

5 files changed

+53
-42
lines changed
Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using BenchmarkDotNet.Attributes;
4+
using BenchmarkDotNet.Configs;
55
using BenchmarkDotNet.Extensions;
6+
using BenchmarkDotNet.Running;
67
using JetBrains.Annotations;
78

89
namespace BenchmarkDotNet.ConsoleArguments
@@ -11,27 +12,23 @@ public class CorrectionsSuggester
1112
{
1213
// note This is a heuristic value, we suppose that user can make three or fewer typos.
1314
private static int PossibleTyposCount => 3;
14-
private readonly HashSet<string> possibleBenchmarkNames = new HashSet<string>();
15-
private readonly HashSet<string> allBenchmarkNames = new HashSet<string>();
15+
private readonly HashSet<string> possibleBenchmarkNameFilters = new HashSet<string>();
16+
private readonly HashSet<string> actualFullBenchmarkNames = new HashSet<string>();
1617

1718
public CorrectionsSuggester(IReadOnlyList<Type> types)
1819
{
19-
var benchmarkNames = new HashSet<string>();
20-
foreach (var type in types)
20+
foreach (var benchmarkRunInfo in TypeFilter.Filter(DefaultConfig.Instance, types))
2121
{
22-
var namesCollection = type.GetMethods()
23-
.Where(methodInfo => methodInfo.HasAttribute<BenchmarkAttribute>())
24-
.Select(methodInfo => $@"{(type.IsGenericType
25-
? type.GetDisplayName()
26-
: type.FullName)}.{methodInfo.Name}")
27-
.ToArray();
28-
benchmarkNames.AddRange(namesCollection);
29-
30-
var names = namesCollection.Select(name => name.Split('.', '+')).SelectMany(GetAllPartialNames);
31-
possibleBenchmarkNames.AddRange(names);
32-
}
22+
foreach (var benchmarkCase in benchmarkRunInfo.BenchmarksCases)
23+
{
24+
string fullBenchmarkName = benchmarkCase.Descriptor.GetFilterName();
25+
26+
actualFullBenchmarkNames.Add(fullBenchmarkName);
3327

34-
allBenchmarkNames.AddRange(benchmarkNames);
28+
var names = GetAllPartialNames(fullBenchmarkName.Split('.'));
29+
possibleBenchmarkNameFilters.AddRange(names);
30+
}
31+
}
3532
}
3633

3734
public string[] SuggestFor([NotNull] string userInput)
@@ -40,7 +37,7 @@ public string[] SuggestFor([NotNull] string userInput)
4037
throw new ArgumentNullException(nameof(userInput));
4138

4239
var calculator = new LevenshteinDistanceCalculator();
43-
return possibleBenchmarkNames
40+
return possibleBenchmarkNameFilters
4441
.Select(name => (name: name, distance: calculator.Calculate(userInput, name)))
4542
.Where(tuple => tuple.distance <= PossibleTyposCount)
4643
.OrderBy(tuple => tuple.distance)
@@ -49,13 +46,35 @@ public string[] SuggestFor([NotNull] string userInput)
4946
.ToArray();
5047
}
5148

52-
public string[] GetAllBenchmarkNames() => allBenchmarkNames.ToArray();
49+
public string[] GetAllBenchmarkNames() => actualFullBenchmarkNames.ToArray();
5350

51+
// A.B.C should get translated into
52+
// A*
53+
// A.B*
54+
// *B*
55+
// *C
5456
private static IEnumerable<string> GetAllPartialNames(string[] nameParts)
5557
{
5658
for (int partLength = 1; partLength <= nameParts.Length; partLength++)
57-
for (int i = 0; i < nameParts.Length - partLength + 1; i++)
58-
yield return string.Join(".", nameParts.Skip(i).Take(partLength));
59+
{
60+
for (int i = 0; i < nameParts.Length - partLength + 1; i++)
61+
{
62+
string permutation = string.Join(".", nameParts.Skip(i).Take(partLength));
63+
64+
if (i == 0 && partLength == nameParts.Length)
65+
{
66+
yield return permutation; // we don't want to offer *fullname*
67+
}
68+
else if (i == 0)
69+
{
70+
yield return $"{permutation}*";
71+
}
72+
else
73+
{
74+
yield return $"*{permutation}*";
75+
}
76+
}
77+
}
5978
}
6079
}
6180
}

src/BenchmarkDotNet/Filters/GlobFilter.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
using System.Linq;
22
using System.Text.RegularExpressions;
3-
using BenchmarkDotNet.Extensions;
4-
using BenchmarkDotNet.Portability;
53
using BenchmarkDotNet.Running;
64

75
namespace BenchmarkDotNet.Filters
@@ -20,9 +18,8 @@ public bool Predicate(BenchmarkCase benchmarkCase)
2018
{
2119
var benchmark = benchmarkCase.Descriptor.WorkloadMethod;
2220
string fullBenchmarkName = benchmarkCase.Descriptor.GetFilterName();
23-
string typeName = benchmark.DeclaringType.GetDisplayName();
2421

25-
return patterns.Any(pattern => typeName.EqualsWithIgnoreCase(pattern.userValue) || pattern.regex.IsMatch(fullBenchmarkName));
22+
return patterns.Any(pattern => pattern.regex.IsMatch(fullBenchmarkName));
2623
}
2724

2825
// https://stackoverflow.com/a/6907849/5852046 not perfect but should work for all we need

tests/BenchmarkDotNet.Tests/CorrectionsSuggesterTests.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public void FilterByCompositeNamespace_LevenshteinOrdering()
6161
typeof(NamespaceB.NamespaceC.MyClassC),
6262
typeof(AnotherNamespace.InnerNamespaceA.MyClassA)
6363
}).SuggestFor("NmespaceB.NamespaceC");
64-
Assert.Equal(new[] { "NamespaceB.NamespaceC", "NamespaceA.NamespaceC" }, suggestedNames);
64+
Assert.Equal(new[] { "NamespaceB.NamespaceC*", "NamespaceA.NamespaceC*" }, suggestedNames);
6565
}
6666

6767
[Fact]
@@ -74,7 +74,7 @@ public void FilterByNamespace_LevenshteinOrdering()
7474
typeof(AnotherNamespace.MyClassZ),
7575
typeof(NamespaceB.NamespaceC.MyClassC)
7676
}).SuggestFor("Nmespace");
77-
Assert.Equal(new[] { "NamespaceA", "NamespaceB", "NamespaceC" }, suggestedNames);
77+
Assert.Equal(new[] { "NamespaceA*", "NamespaceB*" }, suggestedNames);
7878
}
7979

8080
[Fact]
@@ -86,15 +86,15 @@ public void FilterByInnerNamespace_LevenshteinOrdering()
8686
typeof(AnotherNamespace.InnerNamespaceB.MyClassA),
8787
typeof(Lexicographical.MyClassLexicAACDE)
8888
}).SuggestFor("InerNamespaceB");
89-
Assert.Equal(new[] { "InnerNamespaceB", "InnerNamespaceA" }, suggestedNames);
89+
Assert.Equal(new[] { "*InnerNamespaceB*" }, suggestedNames);
9090
}
9191

9292
[Fact]
9393
public void FilterByClassFromDifferentNamespaces()
9494
{
9595
var suggestedNames = new CorrectionsSuggester(new[] { typeof(MyClassA), typeof(NamespaceA.MyClassA) })
9696
.SuggestFor("MyClasA");
97-
Assert.Equal(new[] { "MyClassA" }, suggestedNames);
97+
Assert.Equal(new[] { "*MyClassA*" }, suggestedNames);
9898
}
9999

100100
[Fact]
@@ -105,15 +105,15 @@ public void FilterByClass_LevenshteinOrdering()
105105
typeof(MyClassA), typeof(MyClassB), typeof(MyClassC), typeof(MyClassZ), typeof(NamespaceB.NamespaceC.MyClassA),
106106
typeof(MyClassZ.MyClassY)
107107
}).SuggestFor("MyClasZ");
108-
Assert.Equal(new[] { "MyClassZ", "MyClassA", "MyClassB", "MyClassC", "MyClassY" }, suggestedNames);
108+
Assert.Equal(new[] { "*MyClassZ*" }, suggestedNames);
109109
}
110110

111111
[Fact]
112112
public void FilterByNamespaceClassMethod_LevenshteinOrdering()
113113
{
114114
var suggestedNames = new CorrectionsSuggester(new[]
115115
{
116-
typeof(MyClassA),
116+
typeof(NamespaceB.MyClassA),
117117
typeof(NamespaceA.MyClassA),
118118
typeof(NamespaceB.NamespaceC.MyClassA)
119119
}).SuggestFor("NamespaceA.MyClasA.MethodA");
@@ -122,8 +122,6 @@ public void FilterByNamespaceClassMethod_LevenshteinOrdering()
122122
"NamespaceA.MyClassA.MethodA",
123123
"NamespaceA.MyClassA.MethodB",
124124
"NamespaceB.MyClassA.MethodA",
125-
"NamespaceC.MyClassA.MethodA",
126-
"NamespaceC.MyClassA.MethodB"
127125
}, suggestedNames);
128126
}
129127

@@ -135,7 +133,7 @@ public void FilterGeneric_LevenshteinOrdering()
135133
typeof(Generics.GenericA<int>),
136134
typeof(Generics.GenericB<int>)
137135
}).SuggestFor("GeneriA<Int32>");
138-
Assert.Equal(new[] { "GenericA<Int32>", "GenericB<Int32>" }, suggestedNames);
136+
Assert.Equal(new[] { "*GenericA<Int32>*" }, suggestedNames);
139137
}
140138
}
141139
}

tests/BenchmarkDotNet.Tests/GlobFilterTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ namespace BenchmarkDotNet.Tests
99
public class GlobFilterTests
1010
{
1111
[Theory]
12-
[InlineData(nameof(TypeWithBenchmarks), true)] // type name
13-
[InlineData("typewithbenchmarks", true)] // type name lowercase
14-
[InlineData("TYPEWITHBENCHMARKS", true)] // type name uppercase
12+
[InlineData(nameof(TypeWithBenchmarks), false)] // type name
13+
[InlineData("typewithbenchmarks", false)] // type name lowercase
14+
[InlineData("TYPEWITHBENCHMARKS", false)] // type name uppercase
1515
[InlineData("*TypeWithBenchmarks*", true)] // regular expression
1616
[InlineData("*typewithbenchmarks*", true)] // regular expression lowercase
1717
[InlineData("*TYPEWITHBENCHMARKS*", true)] // regular expression uppercase

tests/BenchmarkDotNet.Tests/TypeFilterTests.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,13 @@ public void CanSelectClassesUsingPatters()
103103
}
104104

105105
[Fact]
106-
public void CanSelectClassesUsingTypeNames()
106+
public void CanNotSelectClassesUsingTypeNames()
107107
{
108108
var benchmarks = Filter(
109109
new[] { typeof(ClassA), typeof(ClassB) },
110110
new[] { "--filter", "ClassC", "ClassA" });
111111

112-
// ClassC not matched as it has NO methods with the [Benchmark] attribute
113-
Assert.Equal(2, benchmarks.Count);
114-
Assert.Contains("ClassA.Method1", benchmarks);
115-
Assert.Contains("ClassA.Method2", benchmarks);
112+
Assert.Empty(benchmarks); // it's not supported anymore
116113
}
117114

118115
[Fact]

0 commit comments

Comments
 (0)