diff --git a/samples/BenchmarkDotNet.Samples/IntroArgumentsSource.cs b/samples/BenchmarkDotNet.Samples/IntroArgumentsSource.cs index 54dccecc84..493422e51f 100644 --- a/samples/BenchmarkDotNet.Samples/IntroArgumentsSource.cs +++ b/samples/BenchmarkDotNet.Samples/IntroArgumentsSource.cs @@ -24,9 +24,9 @@ public class IntroArgumentsSource public void SingleArgument(TimeSpan time) => Thread.Sleep(time); } - public class BenchmarkArguments + public static class BenchmarkArguments { - public IEnumerable TimeSpans() // for single argument it's an IEnumerable of objects (object) + public static IEnumerable TimeSpans() // for single argument it's an IEnumerable of objects (object) { yield return TimeSpan.FromMilliseconds(10); yield return TimeSpan.FromMilliseconds(100); diff --git a/src/BenchmarkDotNet/Parameters/SmartParamBuilder.cs b/src/BenchmarkDotNet/Parameters/SmartParamBuilder.cs index 29814d8179..49ea1b162f 100644 --- a/src/BenchmarkDotNet/Parameters/SmartParamBuilder.cs +++ b/src/BenchmarkDotNet/Parameters/SmartParamBuilder.cs @@ -99,8 +99,20 @@ public string ToSourceCode() ? $"[{argumentIndex}]" // IEnumerable : string.Empty; // IEnumerable + string methodCall; + if ((source as MethodInfo)?.IsStatic ?? (source as PropertyInfo)?.GetMethod.IsStatic ?? throw new Exception($"{nameof(source)} was not {nameof(MethodInfo)} nor {nameof(PropertyInfo)}")) + { + // If the source member is static, we need to place the fully qualified type name before it, in case the source member is from another type that this generated type does not inherit from. + methodCall = $"{source.DeclaringType.GetCorrectCSharpTypeName()}.{source.Name}"; + } + else + { + // If the source member is non-static, we mustn't include the type name, as this would be a compiler error when accessing a non-static source member in the base class of this generated type. + methodCall = source.Name; + } + // we do something like enumerable.ElementAt(sourceIndex)[argumentIndex]; - return $"{cast}BenchmarkDotNet.Parameters.ParameterExtractor.GetParameter({source.Name}{callPostfix}, {sourceIndex}){indexPostfix};"; + return $"{cast}BenchmarkDotNet.Parameters.ParameterExtractor.GetParameter({methodCall}{callPostfix}, {sourceIndex}){indexPostfix};"; } } diff --git a/tests/BenchmarkDotNet.IntegrationTests/ArgumentsTests.cs b/tests/BenchmarkDotNet.IntegrationTests/ArgumentsTests.cs index 77d8ecb931..021ff5d96d 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/ArgumentsTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/ArgumentsTests.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Threading.Tasks; -using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Tests.XUnit; using BenchmarkDotNet.Toolchains; using BenchmarkDotNet.Toolchains.InProcess.Emit; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -87,20 +87,85 @@ public IEnumerable ArgumentsProvider() public class WithArgumentsSourceInAnotherClass { [Benchmark] - [ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.ArgumentsProvider))] - public void Simple(bool boolean, int number) + [ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.OnePrimitiveType))] + public void OnePrimitiveType(int number) + { + if (number % 2 != 1) + throw new InvalidOperationException("Incorrect values were passed"); + } + + [Benchmark] + [ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.TwoPrimitiveTypes))] + public void TwoPrimitiveTypes(bool boolean, int number) { if (boolean && number != 1 || !boolean && number != 2) throw new InvalidOperationException("Incorrect values were passed"); } + + [Benchmark] + [ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.OneNonPrimitiveType))] + public void OneNonPrimitiveType(Version version) + { + int[] versionNumbers = { version.Major, version.Minor, version.MinorRevision, version.Build }; + if (versionNumbers.Distinct().Count() != 4) + throw new InvalidOperationException("Incorrect values were passed"); + } + + [Benchmark] + [ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.TwoNonPrimitiveTypes))] + public void TwoNonPrimitiveTypes(Version version, DateTime dateTime) + { + int[] versionNumbers = { version.Major, version.Minor, version.MinorRevision, version.Build }; + if (versionNumbers.Distinct().Count() != 4) + throw new InvalidOperationException("Incorrect values were passed"); + + if (dateTime.Month != dateTime.Day) + throw new InvalidOperationException("Incorrect values were passed"); + } + + [Benchmark] + [ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.OnePrimitiveAndOneNonPrimitive))] + public void OnePrimitiveAndOneNonPrimitive(Version version, int number) + { + int[] versionNumbers = { version.Major, version.Minor, version.MinorRevision, version.Build }; + if (versionNumbers.Distinct().Count() != 4) + throw new InvalidOperationException("Incorrect values were passed"); + + if (number != version.Major) + throw new InvalidOperationException("Incorrect values were passed"); + } } public static class ExternalClassWithArgumentsSource { - public static IEnumerable ArgumentsProvider() + public static IEnumerable OnePrimitiveType() + { + yield return 3; + yield return 5; + } + + public static IEnumerable TwoPrimitiveTypes() { yield return new object[] { true, 1 }; yield return new object[] { false, 2 }; } + + public static IEnumerable OneNonPrimitiveType() + { + yield return new Version(1, 2, 3, 4); + yield return new Version(5, 6, 7, 8); + } + + public static IEnumerable TwoNonPrimitiveTypes() + { + yield return new object[] { new Version(1, 2, 3, 4), new DateTime(2011, 11, 11) }; + yield return new object[] { new Version(5, 6, 7, 8), new DateTime(2002, 02, 02) }; + } + + public static IEnumerable OnePrimitiveAndOneNonPrimitive() + { + yield return new object[] { new Version(1, 2, 3, 4), 1 }; + yield return new object[] { new Version(5, 6, 7, 8), 5 }; + } } [Theory, MemberData(nameof(GetToolchains), DisableDiscoveryEnumeration = true)] @@ -775,12 +840,18 @@ public void MethodsAndPropertiesFromAnotherClassCanBeUsedAsSources(IToolchain to public class ParamsSourcePointingToAnotherClass { - [ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.Method))] + [ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.PrimitiveTypeMethod))] public int ParamOne { get; set; } - [ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.Property))] + [ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.PrimitiveTypeProperty))] public int ParamTwo { get; set; } + [ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.NonPrimitiveTypeMethod))] + public Version ParamThree { get; set; } + + [ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.NonPrimitiveTypeProperty))] + public Version ParamFour { get; set; } + [Benchmark] public void Test() { @@ -788,21 +859,36 @@ public void Test() throw new ArgumentException("The ParamOne value is incorrect!"); if (ParamTwo != 456) throw new ArgumentException("The ParamTwo value is incorrect!"); + if (ParamThree != new Version(1, 2, 3, 4)) + throw new ArgumentException("The ParamThree value is incorrect!"); + if (ParamFour != new Version(5, 6, 7, 8)) + throw new ArgumentException("The ParamFour value is incorrect!"); } } public static class ExternalClassWithParamsSource { - public static IEnumerable Method() + public static IEnumerable PrimitiveTypeMethod() { yield return 123; } - public static IEnumerable Property + public static IEnumerable PrimitiveTypeProperty { get { yield return 456; } } + public static IEnumerable NonPrimitiveTypeMethod() + { + yield return new Version(1, 2, 3, 4); + } + public static IEnumerable NonPrimitiveTypeProperty + { + get + { + yield return new Version(5, 6, 7, 8); + } + } } [Theory, MemberData(nameof(GetToolchains), DisableDiscoveryEnumeration = true)]