Skip to content

Commit 7e9fa0c

Browse files
committed
Add Parameterization Validator
1 parent 38a183e commit 7e9fa0c

File tree

4 files changed

+166
-1
lines changed

4 files changed

+166
-1
lines changed

src/BenchmarkDotNet/Configs/DefaultConfig.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public IEnumerable<IAnalyser> GetAnalysers()
6060
public IEnumerable<IValidator> GetValidators()
6161
{
6262
yield return BaselineValidator.FailOnError;
63+
yield return ParameterizationValidator.FailOnError;
6364
yield return SetupCleanupValidator.FailOnError;
6465
#if !DEBUG
6566
yield return JitOptimizationsValidator.FailOnError;
@@ -107,4 +108,4 @@ public string ArtifactsPath
107108

108109
public IEnumerable<IColumnHidingRule> GetColumnHidingRules() => Array.Empty<IColumnHidingRule>();
109110
}
110-
}
111+
}

src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public static class ImmutableConfigBuilder
1919
private static readonly IValidator[] MandatoryValidators =
2020
{
2121
BaselineValidator.FailOnError,
22+
ParameterizationValidator.FailOnError,
2223
SetupCleanupValidator.FailOnError,
2324
RunModeValidator.FailOnError,
2425
DiagnosersValidator.Composite,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using BenchmarkDotNet.Attributes;
6+
using BenchmarkDotNet.Extensions;
7+
8+
namespace BenchmarkDotNet.Validators
9+
{
10+
public class ParameterizationValidator : IValidator
11+
{
12+
public static readonly ParameterizationValidator FailOnError = new ParameterizationValidator();
13+
14+
public bool TreatsWarningsAsErrors => true;
15+
16+
private const BindingFlags ReflectionFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
17+
18+
public IEnumerable<ValidationError> Validate(ValidationParameters input) =>
19+
ValidateAttributes<ParamsAttribute>(input)
20+
.Concat(ValidateAttributes<ParamsSourceAttribute>(input));
21+
22+
private IEnumerable<ValidationError> ValidateAttributes<T>(ValidationParameters input) where T : Attribute
23+
{
24+
return input.Benchmarks.Select(benchmark => benchmark.Descriptor.Type)
25+
.Distinct()
26+
.SelectMany(type => type.GetTypeMembersWithGivenAttribute<T>(ReflectionFlags))
27+
.Distinct()
28+
.Where(member => !member.IsPublic)
29+
.Select(member => GetNonPublicError(member.Name, GetAttributeName(typeof(T))))
30+
.Where(error => error != null);
31+
}
32+
33+
private ValidationError GetNonPublicError(string memberName, string attributeName)
34+
{
35+
return new ValidationError(
36+
TreatsWarningsAsErrors,
37+
$"Member \"{memberName}\" must be public if it has the [{attributeName}] attribute applied to it");
38+
}
39+
40+
private static string GetAttributeName(Type type) => type.Name.Replace("Attribute", string.Empty);
41+
}
42+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using BenchmarkDotNet.Attributes;
4+
using BenchmarkDotNet.Running;
5+
using BenchmarkDotNet.Validators;
6+
using Xunit;
7+
8+
namespace BenchmarkDotNet.Tests.Validators
9+
{
10+
public class ParameterizationValidatorTests
11+
{
12+
[Fact]
13+
public void PublicIsSupported()
14+
{
15+
var validationErrors = ParameterizationValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(PublicParameterization)));
16+
17+
Assert.Empty(validationErrors);
18+
}
19+
20+
public class PublicParameterization
21+
{
22+
#pragma warning disable CS0649
23+
[Params(1)]
24+
public int Field;
25+
26+
[Params(1)]
27+
public int Property { get; set; }
28+
29+
[ParamsSource(nameof(GetValues))]
30+
public int FieldSource;
31+
32+
[ParamsSource(nameof(GetValues))]
33+
public int PropertySource { get; set; }
34+
#pragma warning restore CS0649
35+
36+
public IEnumerable<object> GetValues()
37+
{
38+
yield return 1;
39+
}
40+
41+
[Benchmark]
42+
public void Foo() { }
43+
}
44+
45+
[Fact]
46+
public void InternalIsNotSupported()
47+
{
48+
var validationErrors = ParameterizationValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(InternalParameterization))).ToArray();
49+
50+
Assert.NotEmpty(validationErrors);
51+
Assert.StartsWith("Member \"Field\" must be public if it has the [Params]", validationErrors[0].Message);
52+
Assert.StartsWith("Member \"Property\" must be public if it has the [Params]", validationErrors[1].Message);
53+
Assert.StartsWith("Member \"FieldSource\" must be public if it has the [ParamsSource]", validationErrors[2].Message);
54+
Assert.StartsWith("Member \"PropertySource\" must be public if it has the [ParamsSource]", validationErrors[3].Message);
55+
}
56+
57+
public class InternalParameterization
58+
{
59+
#pragma warning disable CS0649
60+
[Params(1)]
61+
internal int Field;
62+
63+
[Params(1)]
64+
internal int Property { get; set; }
65+
66+
[ParamsSource(nameof(GetValues))]
67+
internal int FieldSource;
68+
69+
[ParamsSource(nameof(GetValues))]
70+
internal int PropertySource { get; set; }
71+
#pragma warning restore CS0649
72+
73+
public IEnumerable<object> GetValues()
74+
{
75+
yield return 0;
76+
}
77+
78+
[Benchmark]
79+
public void Foo() { }
80+
}
81+
82+
[Fact]
83+
public void PrivateIsNotSupported()
84+
{
85+
var validationErrors = ParameterizationValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(PrivateParameterization))).ToArray();
86+
87+
Assert.NotEmpty(validationErrors);
88+
Assert.StartsWith("Member \"Field\" must be public if it has the [Params]", validationErrors[0].Message);
89+
Assert.StartsWith("Member \"Property\" must be public if it has the [Params]", validationErrors[1].Message);
90+
Assert.StartsWith("Member \"FieldSource\" must be public if it has the [ParamsSource]", validationErrors[2].Message);
91+
Assert.StartsWith("Member \"PropertySource\" must be public if it has the [ParamsSource]", validationErrors[3].Message);
92+
}
93+
94+
public class PrivateParameterization
95+
{
96+
#pragma warning disable CA1823
97+
#pragma warning disable CS0169
98+
[Params(1)]
99+
private int Field;
100+
101+
[Params(1)]
102+
private int Property { get; set; }
103+
104+
[ParamsSource(nameof(GetValues))]
105+
private int FieldSource;
106+
107+
[ParamsSource(nameof(GetValues))]
108+
private int PropertySource { get; set; }
109+
#pragma warning restore CA1823
110+
#pragma warning restore CS0169
111+
112+
public IEnumerable<object> GetValues()
113+
{
114+
yield return 0;
115+
}
116+
117+
[Benchmark]
118+
public void Foo() { }
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)