Skip to content

Commit e704adb

Browse files
Fix ArgumentsSource on external types not working if the argument type is not primitive (#2820)
* Fix generated code breaking when using ArgumentsSource on an external type * IntroArgumentsSource: use static type for external arguments * Add more test cases for ArgumentsSource in external class Catches some issues not previously caught. * Add test cases for ParamsSource with non-primitive param types * Fix casts Co-authored-by: Tim Cassell <[email protected]> --------- Co-authored-by: Tim Cassell <[email protected]>
1 parent e7cf8b4 commit e704adb

File tree

3 files changed

+114
-16
lines changed

3 files changed

+114
-16
lines changed

samples/BenchmarkDotNet.Samples/IntroArgumentsSource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ public class IntroArgumentsSource
2424
public void SingleArgument(TimeSpan time) => Thread.Sleep(time);
2525
}
2626

27-
public class BenchmarkArguments
27+
public static class BenchmarkArguments
2828
{
29-
public IEnumerable<object> TimeSpans() // for single argument it's an IEnumerable of objects (object)
29+
public static IEnumerable<object> TimeSpans() // for single argument it's an IEnumerable of objects (object)
3030
{
3131
yield return TimeSpan.FromMilliseconds(10);
3232
yield return TimeSpan.FromMilliseconds(100);

src/BenchmarkDotNet/Parameters/SmartParamBuilder.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,20 @@ public string ToSourceCode()
9999
? $"[{argumentIndex}]" // IEnumerable<object[]>
100100
: string.Empty; // IEnumerable<object>
101101

102+
string methodCall;
103+
if ((source as MethodInfo)?.IsStatic ?? (source as PropertyInfo)?.GetMethod.IsStatic ?? throw new Exception($"{nameof(source)} was not {nameof(MethodInfo)} nor {nameof(PropertyInfo)}"))
104+
{
105+
// 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.
106+
methodCall = $"{source.DeclaringType.GetCorrectCSharpTypeName()}.{source.Name}";
107+
}
108+
else
109+
{
110+
// 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.
111+
methodCall = source.Name;
112+
}
113+
102114
// we do something like enumerable.ElementAt(sourceIndex)[argumentIndex];
103-
return $"{cast}BenchmarkDotNet.Parameters.ParameterExtractor.GetParameter({source.Name}{callPostfix}, {sourceIndex}){indexPostfix};";
115+
return $"{cast}BenchmarkDotNet.Parameters.ParameterExtractor.GetParameter({methodCall}{callPostfix}, {sourceIndex}){indexPostfix};";
104116
}
105117
}
106118

tests/BenchmarkDotNet.IntegrationTests/ArgumentsTests.cs

Lines changed: 99 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Numerics;
5-
using System.Threading.Tasks;
6-
using BenchmarkDotNet.Attributes;
1+
using BenchmarkDotNet.Attributes;
72
using BenchmarkDotNet.Jobs;
83
using BenchmarkDotNet.Tests.XUnit;
94
using BenchmarkDotNet.Toolchains;
105
using BenchmarkDotNet.Toolchains.InProcess.Emit;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Linq;
9+
using System.Numerics;
10+
using System.Threading.Tasks;
1111
using Xunit;
1212
using Xunit.Abstractions;
1313

@@ -87,20 +87,85 @@ public IEnumerable<object[]> ArgumentsProvider()
8787
public class WithArgumentsSourceInAnotherClass
8888
{
8989
[Benchmark]
90-
[ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.ArgumentsProvider))]
91-
public void Simple(bool boolean, int number)
90+
[ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.OnePrimitiveType))]
91+
public void OnePrimitiveType(int number)
92+
{
93+
if (number % 2 != 1)
94+
throw new InvalidOperationException("Incorrect values were passed");
95+
}
96+
97+
[Benchmark]
98+
[ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.TwoPrimitiveTypes))]
99+
public void TwoPrimitiveTypes(bool boolean, int number)
92100
{
93101
if (boolean && number != 1 || !boolean && number != 2)
94102
throw new InvalidOperationException("Incorrect values were passed");
95103
}
104+
105+
[Benchmark]
106+
[ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.OneNonPrimitiveType))]
107+
public void OneNonPrimitiveType(Version version)
108+
{
109+
int[] versionNumbers = { version.Major, version.Minor, version.MinorRevision, version.Build };
110+
if (versionNumbers.Distinct().Count() != 4)
111+
throw new InvalidOperationException("Incorrect values were passed");
112+
}
113+
114+
[Benchmark]
115+
[ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.TwoNonPrimitiveTypes))]
116+
public void TwoNonPrimitiveTypes(Version version, DateTime dateTime)
117+
{
118+
int[] versionNumbers = { version.Major, version.Minor, version.MinorRevision, version.Build };
119+
if (versionNumbers.Distinct().Count() != 4)
120+
throw new InvalidOperationException("Incorrect values were passed");
121+
122+
if (dateTime.Month != dateTime.Day)
123+
throw new InvalidOperationException("Incorrect values were passed");
124+
}
125+
126+
[Benchmark]
127+
[ArgumentsSource(typeof(ExternalClassWithArgumentsSource), nameof(ExternalClassWithArgumentsSource.OnePrimitiveAndOneNonPrimitive))]
128+
public void OnePrimitiveAndOneNonPrimitive(Version version, int number)
129+
{
130+
int[] versionNumbers = { version.Major, version.Minor, version.MinorRevision, version.Build };
131+
if (versionNumbers.Distinct().Count() != 4)
132+
throw new InvalidOperationException("Incorrect values were passed");
133+
134+
if (number != version.Major)
135+
throw new InvalidOperationException("Incorrect values were passed");
136+
}
96137
}
97138
public static class ExternalClassWithArgumentsSource
98139
{
99-
public static IEnumerable<object[]> ArgumentsProvider()
140+
public static IEnumerable<int> OnePrimitiveType()
141+
{
142+
yield return 3;
143+
yield return 5;
144+
}
145+
146+
public static IEnumerable<object[]> TwoPrimitiveTypes()
100147
{
101148
yield return new object[] { true, 1 };
102149
yield return new object[] { false, 2 };
103150
}
151+
152+
public static IEnumerable<Version> OneNonPrimitiveType()
153+
{
154+
yield return new Version(1, 2, 3, 4);
155+
yield return new Version(5, 6, 7, 8);
156+
}
157+
158+
public static IEnumerable<object[]> TwoNonPrimitiveTypes()
159+
{
160+
yield return new object[] { new Version(1, 2, 3, 4), new DateTime(2011, 11, 11) };
161+
yield return new object[] { new Version(5, 6, 7, 8), new DateTime(2002, 02, 02) };
162+
}
163+
164+
public static IEnumerable<object[]> OnePrimitiveAndOneNonPrimitive()
165+
{
166+
yield return new object[] { new Version(1, 2, 3, 4), 1 };
167+
yield return new object[] { new Version(5, 6, 7, 8), 5 };
168+
}
104169
}
105170

106171
[Theory, MemberData(nameof(GetToolchains), DisableDiscoveryEnumeration = true)]
@@ -775,34 +840,55 @@ public void MethodsAndPropertiesFromAnotherClassCanBeUsedAsSources(IToolchain to
775840

776841
public class ParamsSourcePointingToAnotherClass
777842
{
778-
[ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.Method))]
843+
[ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.PrimitiveTypeMethod))]
779844
public int ParamOne { get; set; }
780845

781-
[ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.Property))]
846+
[ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.PrimitiveTypeProperty))]
782847
public int ParamTwo { get; set; }
783848

849+
[ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.NonPrimitiveTypeMethod))]
850+
public Version ParamThree { get; set; }
851+
852+
[ParamsSource(typeof(ExternalClassWithParamsSource), nameof(ExternalClassWithParamsSource.NonPrimitiveTypeProperty))]
853+
public Version ParamFour { get; set; }
854+
784855
[Benchmark]
785856
public void Test()
786857
{
787858
if (ParamOne != 123)
788859
throw new ArgumentException("The ParamOne value is incorrect!");
789860
if (ParamTwo != 456)
790861
throw new ArgumentException("The ParamTwo value is incorrect!");
862+
if (ParamThree != new Version(1, 2, 3, 4))
863+
throw new ArgumentException("The ParamThree value is incorrect!");
864+
if (ParamFour != new Version(5, 6, 7, 8))
865+
throw new ArgumentException("The ParamFour value is incorrect!");
791866
}
792867
}
793868
public static class ExternalClassWithParamsSource
794869
{
795-
public static IEnumerable<int> Method()
870+
public static IEnumerable<int> PrimitiveTypeMethod()
796871
{
797872
yield return 123;
798873
}
799-
public static IEnumerable<int> Property
874+
public static IEnumerable<int> PrimitiveTypeProperty
800875
{
801876
get
802877
{
803878
yield return 456;
804879
}
805880
}
881+
public static IEnumerable<Version> NonPrimitiveTypeMethod()
882+
{
883+
yield return new Version(1, 2, 3, 4);
884+
}
885+
public static IEnumerable<Version> NonPrimitiveTypeProperty
886+
{
887+
get
888+
{
889+
yield return new Version(5, 6, 7, 8);
890+
}
891+
}
806892
}
807893

808894
[Theory, MemberData(nameof(GetToolchains), DisableDiscoveryEnumeration = true)]

0 commit comments

Comments
 (0)