This commit is contained in:
Andrey Akinshin 2023-05-09 19:02:55 +02:00 коммит произвёл GitHub
Родитель bfb192769b
Коммит 492e58eb6f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 129 добавлений и 122 удалений

Просмотреть файл

@ -158,31 +158,26 @@ namespace BenchmarkDotNet.Extensions
.Where(method => method.GetCustomAttributes(true).OfType<BenchmarkAttribute>().Any())
.ToArray();
internal static (string Name, TAttribute Attribute, bool IsPrivate, bool IsStatic, Type ParameterType)[] GetTypeMembersWithGivenAttribute<TAttribute>(this Type type, BindingFlags reflectionFlags)
where TAttribute : Attribute
internal static (string Name, TAttribute Attribute, bool IsStatic, Type ParameterType)[]
GetTypeMembersWithGivenAttribute<TAttribute>(this Type type, BindingFlags reflectionFlags) where TAttribute : Attribute
{
var allFields = type.GetFields(reflectionFlags)
.Select(f => (
Name: f.Name,
Attribute: f.ResolveAttribute<TAttribute>(),
IsPrivate: f.IsPrivate,
IsStatic: f.IsStatic,
ParameterType: f.FieldType));
var allFields = type
.GetFields(reflectionFlags)
.Select(f => (
Name: f.Name,
Attribute: f.ResolveAttribute<TAttribute>(),
IsStatic: f.IsStatic,
ParameterType: f.FieldType));
var allProperties = type.GetProperties(reflectionFlags)
.Select(p => (
Name: p.Name,
Attribute: p.ResolveAttribute<TAttribute>(),
IsPrivate: p.GetSetMethod() == null,
IsStatic: p.GetSetMethod() != null && p.GetSetMethod().IsStatic,
PropertyType: p.PropertyType));
var allProperties = type
.GetProperties(reflectionFlags)
.Select(p => (
Name: p.Name,
Attribute: p.ResolveAttribute<TAttribute>(),
IsStatic: p.GetSetMethod() != null && p.GetSetMethod().IsStatic,
PropertyType: p.PropertyType));
var joined = allFields.Concat(allProperties).Where(member => member.Attribute != null).ToArray();
foreach (var member in joined.Where(m => m.IsPrivate))
throw new InvalidOperationException($"Member \"{member.Name}\" must be public if it has the [{typeof(TAttribute).Name}] attribute applied to it");
return joined;
return allFields.Concat(allProperties).Where(member => member.Attribute != null).ToArray();
}
internal static bool IsStackOnlyWithImplicitCast(this Type argumentType, object? argumentInstance)

Просмотреть файл

@ -20,7 +20,9 @@ namespace BenchmarkDotNet.Validators
private IEnumerable<ValidationError> Validate(Type type)
{
foreach (var memberInfo in type.GetMembers(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
const BindingFlags reflectionFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance |
BindingFlags.FlattenHierarchy;
foreach (var memberInfo in type.GetMembers(reflectionFlags))
{
var attributes = new Attribute[]
{
@ -40,17 +42,33 @@ namespace BenchmarkDotNet.Validators
yield return new ValidationError(TreatsWarningsAsErrors,
$"Unable to use {name} with {attributeString} at the same time. Please, use a single attribute.");
if (memberInfo is FieldInfo fieldInfo && (fieldInfo.IsLiteral || fieldInfo.IsInitOnly))
if (memberInfo is FieldInfo fieldInfo)
{
string modifier = fieldInfo.IsInitOnly ? "readonly" : "constant";
yield return new ValidationError(TreatsWarningsAsErrors,
$"Unable to use {name} with {attributeString} because it's a {modifier} field. Please, remove the {modifier} modifier.");
if (fieldInfo.IsLiteral || fieldInfo.IsInitOnly)
{
string modifier = fieldInfo.IsInitOnly ? "readonly" : "constant";
yield return new ValidationError(TreatsWarningsAsErrors,
$"Unable to use {name} with {attributeString} because it's a {modifier} field. Please, remove the {modifier} modifier.");
}
if (!fieldInfo.IsPublic)
yield return new ValidationError(TreatsWarningsAsErrors,
$"Unable to use {name} with {attributeString} because it's not public. Please, make it public.");
}
if (memberInfo is PropertyInfo propertyInfo && propertyInfo.IsInitOnly())
if (memberInfo is PropertyInfo propertyInfo)
{
yield return new ValidationError(TreatsWarningsAsErrors,
$"Unable to use {name} with {attributeString} because it's init-only. Please, provide a public setter.");
if (propertyInfo.IsInitOnly())
yield return new ValidationError(TreatsWarningsAsErrors,
$"Unable to use {name} with {attributeString} because it's init-only. Please, provide a public setter.");
if (propertyInfo.SetMethod == null)
yield return new ValidationError(TreatsWarningsAsErrors,
$"Unable to use {name} with {attributeString} because it has no setter. Please, provide a public setter.");
if (propertyInfo.SetMethod != null && !propertyInfo.SetMethod.IsPublic)
yield return new ValidationError(TreatsWarningsAsErrors,
$"Unable to use {name} with {attributeString} because its setter is not public. Please, make the setter public.");
}
}
}

Просмотреть файл

@ -30,22 +30,6 @@ namespace BenchmarkDotNet.IntegrationTests
public void Benchmark() => Console.WriteLine($"// ### New Parameter {ParamProperty} ###");
}
[Fact]
public void ParamsDoesNotSupportPropertyWithoutPublicSetter()
{
// System.InvalidOperationException : Property "ParamProperty" must be public and writable if it has the [Params(..)] attribute applied to it
Assert.Throws<InvalidOperationException>(() => CanExecute<ParamsTestPrivatePropertyError>());
}
public class ParamsTestPrivatePropertyError
{
[Params(1, 2)]
public int ParamProperty { get; private set; }
[Benchmark]
public void Benchmark() => Console.WriteLine($"// ### New Parameter {ParamProperty} ###");
}
[Fact]
public void ParamsSupportPublicFields()
{
@ -67,22 +51,6 @@ namespace BenchmarkDotNet.IntegrationTests
public void Benchmark() => Console.WriteLine($"// ### New Parameter {ParamField} ###");
}
[Fact]
public void ParamsDoesNotSupportPrivateFields()
{
// System.InvalidOperationException : Field "ParamField" must be public if it has the [Params(..)] attribute applied to it
Assert.Throws<InvalidOperationException>(() => CanExecute<ParamsTestPrivateFieldError>());
}
public class ParamsTestPrivateFieldError
{
[Params(1, 2)]
private int ParamField = 0;
[Benchmark]
public void Benchmark() => Console.WriteLine($"// ### New Parameter {ParamField} ###");
}
public enum NestedOne
{
SampleValue = 1234
@ -249,31 +217,5 @@ namespace BenchmarkDotNet.IntegrationTests
throw new ArgumentException($"{nameof(StaticParamProperty)} has wrong value: {StaticParamProperty}!");
}
}
[Fact]
public void ParamsPropertiesMustHavePublicSetter()
=> Assert.Throws<InvalidOperationException>(() => CanExecute<WithStaticParamsPropertyWithNoPublicSetter>());
public class WithStaticParamsPropertyWithNoPublicSetter
{
[Params(3)]
public static int StaticParamProperty { get; private set; }
[Benchmark]
public int Benchmark() => StaticParamProperty;
}
[Fact]
public void ParamsFieldsMustBePublic()
=> Assert.Throws<InvalidOperationException>(() => CanExecute<WithStaticPrivateParamsField>());
public class WithStaticPrivateParamsField
{
[Params(4)]
private static int StaticParamField = 0;
[Benchmark]
public int Benchmark() => StaticParamField;
}
}
}

Просмотреть файл

@ -290,40 +290,6 @@ namespace BenchmarkDotNet.Tests.Validators
public void NonThrowing() { }
}
[Fact]
public void NonPublicPropertiesWithParamsAreDiscovered()
{
Assert.Throws<InvalidOperationException>(
() => ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(NonPublicPropertyWithParams))));
}
public class NonPublicPropertyWithParams
{
[Params(1)]
[UsedImplicitly]
internal int Property { get; set; }
[Benchmark]
public void NonThrowing() { }
}
[Fact]
public void PropertyWithoutPublicSetterParamsAreDiscovered()
{
Assert.Throws<InvalidOperationException>(
() => ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(PropertyWithoutPublicSetterParams))));
}
public class PropertyWithoutPublicSetterParams
{
[Params(1)]
[UsedImplicitly]
internal int Property { get; }
[Benchmark]
public void NonThrowing() { }
}
[Fact]
public void FieldsWithoutParamsValuesAreDiscovered()
{

Просмотреть файл

@ -6,11 +6,13 @@ using BenchmarkDotNet.Running;
using BenchmarkDotNet.Validators;
using Xunit;
using Xunit.Abstractions;
#pragma warning disable CS0414
namespace BenchmarkDotNet.Tests.Validators
{
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
public class ParamsValidatorTests
{
private readonly ITestOutputHelper output;
@ -56,6 +58,18 @@ namespace BenchmarkDotNet.Tests.Validators
[Fact] public void PropMultiple2Test() => Check<PropMultiple2>(nameof(PropMultiple2.Input), "single attribute", P, Ps);
[Fact] public void PropMultiple3Test() => Check<PropMultiple3>(nameof(PropMultiple3.Input), "single attribute", Pa, Ps);
[Fact] public void PropMultiple4Test() => Check<PropMultiple4>(nameof(PropMultiple4.Input), "single attribute", P, Pa, Ps);
[Fact] public void PrivateSetter1Test() => Check<PrivateSetter1>(nameof(PrivateSetter1.Input), "setter is not public", P);
[Fact] public void PrivateSetter2Test() => Check<PrivateSetter2>(nameof(PrivateSetter2.Input), "setter is not public", Pa);
[Fact] public void PrivateSetter3Test() => Check<PrivateSetter3>(nameof(PrivateSetter3.Input), "setter is not public", Ps);
[Fact] public void NoSetter1Test() => Check<NoSetter1>(nameof(NoSetter1.Input), "no setter", P);
[Fact] public void NoSetter2Test() => Check<NoSetter2>(nameof(NoSetter2.Input), "no setter", Pa);
[Fact] public void NoSetter3Test() => Check<NoSetter3>(nameof(NoSetter3.Input), "no setter", Ps);
[Fact] public void InternalField1Test() => Check<InternalField1>(nameof(InternalField1.Input), "it's not public", P);
[Fact] public void InternalField2Test() => Check<InternalField2>(nameof(InternalField2.Input), "it's not public", Pa);
[Fact] public void InternalField3Test() => Check<InternalField3>(nameof(InternalField3.Input), "it's not public", Ps);
[Fact] public void InternalProp1Test() => Check<InternalProp1>(nameof(InternalProp1.Input), "setter is not public", P);
[Fact] public void InternalProp2Test() => Check<InternalProp2>(nameof(InternalProp2.Input), "setter is not public", Pa);
[Fact] public void InternalProp3Test() => Check<InternalProp3>(nameof(InternalProp3.Input), "setter is not public", Ps);
public class Base
{
@ -119,6 +133,78 @@ namespace BenchmarkDotNet.Tests.Validators
public readonly bool Input = false;
}
public class PrivateSetter1 : Base
{
[Params(false, true)]
public bool Input { get; private set; }
}
public class PrivateSetter2 : Base
{
[ParamsAllValues]
public bool Input { get; private set; }
}
public class PrivateSetter3 : Base
{
[ParamsSource(nameof(Source))]
public bool Input { get; private set; }
}
public class NoSetter1 : Base
{
[Params(false, true)]
public bool Input { get; } = false;
}
public class NoSetter2 : Base
{
[ParamsAllValues]
public bool Input { get; } = false;
}
public class NoSetter3 : Base
{
[ParamsSource(nameof(Source))]
public bool Input { get; } = false;
}
public class InternalField1 : Base
{
[Params(false, true)]
internal bool Input = false;
}
public class InternalField2 : Base
{
[ParamsAllValues]
internal bool Input = false;
}
public class InternalField3 : Base
{
[ParamsSource(nameof(Source))]
internal bool Input = false;
}
public class InternalProp1 : Base
{
[Params(false, true)]
internal bool Input { get; set; }
}
public class InternalProp2 : Base
{
[ParamsAllValues]
internal bool Input { get; set; }
}
public class InternalProp3 : Base
{
[ParamsSource(nameof(Source))]
internal bool Input { get; set; }
}
public class FieldMultiple1 : Base
{
[Params(false, true)]