Skip to content

Commit 7a8135b

Browse files
authored
IComparable fallback for Tuple/ValueTuple (#2368)
* Catch CompareTo argument exceptions and fallback to string comparison * Catch when message contains expected IComparable error
1 parent 8a7caa7 commit 7a8135b

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

src/BenchmarkDotNet/Parameters/ParameterComparer.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,16 @@ private int CompareValues(object x, object y)
2828
if (x != null && y != null && x.GetType() == y.GetType() &&
2929
x is IComparable xComparable)
3030
{
31-
return xComparable.CompareTo(y);
31+
try
32+
{
33+
return xComparable.CompareTo(y);
34+
}
35+
// Some types, such as Tuple and ValueTuple, have a fallible CompareTo implementation which can throw if the inner items don't implement IComparable.
36+
// See: https://github.com/dotnet/BenchmarkDotNet/issues/2346
37+
// For now, catch and ignore the exception, and fallback to string comparison below.
38+
catch (ArgumentException ex) when (ex.Message.Contains("At least one object must implement IComparable."))
39+
{
40+
}
3241
}
3342

3443
// Anything else.

tests/BenchmarkDotNet.Tests/ParameterComparerTests.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,70 @@ public void IComparableComparisionTest()
157157
Assert.Equal(4, ((ComplexParameter)sortedData[3].Items[0].Value).Value);
158158
}
159159

160+
[Fact]
161+
public void ValueTupleWithNonIComparableInnerTypesComparisionTest()
162+
{
163+
var comparer = ParameterComparer.Instance;
164+
var originalData = new[]
165+
{
166+
new ParameterInstances(new[]
167+
{
168+
new ParameterInstance(sharedDefinition, (new ComplexNonIComparableParameter(), 1), null)
169+
}),
170+
new ParameterInstances(new[]
171+
{
172+
new ParameterInstance(sharedDefinition, (new ComplexNonIComparableParameter(), 3), null)
173+
}),
174+
new ParameterInstances(new[]
175+
{
176+
new ParameterInstance(sharedDefinition, (new ComplexNonIComparableParameter(), 2), null)
177+
}),
178+
new ParameterInstances(new[]
179+
{
180+
new ParameterInstance(sharedDefinition, (new ComplexNonIComparableParameter(), 4), null)
181+
})
182+
};
183+
184+
var sortedData = originalData.OrderBy(d => d, comparer).ToArray();
185+
186+
Assert.Equal(1, (((ComplexNonIComparableParameter, int))sortedData[0].Items[0].Value).Item2);
187+
Assert.Equal(2, (((ComplexNonIComparableParameter, int))sortedData[1].Items[0].Value).Item2);
188+
Assert.Equal(3, (((ComplexNonIComparableParameter, int))sortedData[2].Items[0].Value).Item2);
189+
Assert.Equal(4, (((ComplexNonIComparableParameter, int))sortedData[3].Items[0].Value).Item2);
190+
}
191+
192+
[Fact]
193+
public void TupleWithNonIComparableInnerTypesComparisionTest()
194+
{
195+
var comparer = ParameterComparer.Instance;
196+
var originalData = new[]
197+
{
198+
new ParameterInstances(new[]
199+
{
200+
new ParameterInstance(sharedDefinition, Tuple.Create(new ComplexNonIComparableParameter(), 1), null)
201+
}),
202+
new ParameterInstances(new[]
203+
{
204+
new ParameterInstance(sharedDefinition, Tuple.Create(new ComplexNonIComparableParameter(), 3), null)
205+
}),
206+
new ParameterInstances(new[]
207+
{
208+
new ParameterInstance(sharedDefinition, Tuple.Create(new ComplexNonIComparableParameter(), 2), null)
209+
}),
210+
new ParameterInstances(new[]
211+
{
212+
new ParameterInstance(sharedDefinition, Tuple.Create(new ComplexNonIComparableParameter(), 4), null)
213+
})
214+
};
215+
216+
var sortedData = originalData.OrderBy(d => d, comparer).ToArray();
217+
218+
Assert.Equal(1, ((Tuple<ComplexNonIComparableParameter, int>)sortedData[0].Items[0].Value).Item2);
219+
Assert.Equal(2, ((Tuple<ComplexNonIComparableParameter, int>)sortedData[1].Items[0].Value).Item2);
220+
Assert.Equal(3, ((Tuple<ComplexNonIComparableParameter, int>)sortedData[2].Items[0].Value).Item2);
221+
Assert.Equal(4, ((Tuple<ComplexNonIComparableParameter, int>)sortedData[3].Items[0].Value).Item2);
222+
}
223+
160224
private class ComplexParameter : IComparable<ComplexParameter>, IComparable
161225
{
162226
public ComplexParameter(int value, string name)
@@ -199,5 +263,9 @@ public int CompareTo(object obj)
199263
return CompareTo(other);
200264
}
201265
}
266+
267+
private class ComplexNonIComparableParameter
268+
{
269+
}
202270
}
203271
}

0 commit comments

Comments
 (0)