Skip to content

Commit 4172f89

Browse files
authored
fix string comparison structs crash when compared with default values (#432)
1 parent a2d1fb4 commit 4172f89

File tree

6 files changed

+108
-6
lines changed

6 files changed

+108
-6
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.Performance.Testing;
5+
6+
[assembly: UnitTest]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#nullable enable
5+
6+
using Microsoft.VisualStudio.TestTools.UnitTesting;
7+
8+
namespace Microsoft.Performance.SDK.Tests;
9+
10+
[TestClass]
11+
public class StringWithCaseInsensitiveComparisonTests
12+
{
13+
[TestMethod]
14+
[DataRow("Alphabet", "alphabet", 0, DisplayName = "Equal ignoring casing")]
15+
[DataRow("Bravo", "apple", 1, DisplayName = "Comparison ignoring casing")]
16+
public void CompareTo_ProvidesCaseInsensitiveOrdering(string first, string second, int expectedSign)
17+
{
18+
var left = new StringWithCaseInsensitiveComparison(first);
19+
var right = new StringWithCaseInsensitiveComparison(second);
20+
21+
Assert.AreEqual(expectedSign, left.CompareTo(right));
22+
Assert.AreEqual(-expectedSign, right.CompareTo(left));
23+
}
24+
25+
[TestMethod]
26+
public void DefaultValue_CompareToBehavesConsistently()
27+
{
28+
var defaultValue = default(StringWithCaseInsensitiveComparison);
29+
var other = new StringWithCaseInsensitiveComparison("apple");
30+
31+
Assert.AreEqual(0, defaultValue.CompareTo(default(StringWithCaseInsensitiveComparison)));
32+
Assert.IsTrue(defaultValue < other);
33+
Assert.IsTrue(other > defaultValue);
34+
}
35+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#nullable enable
5+
6+
using Microsoft.VisualStudio.TestTools.UnitTesting;
7+
8+
namespace Microsoft.Performance.SDK.Tests;
9+
10+
[TestClass]
11+
public class StringWithLogicalComparisonTests
12+
{
13+
[TestMethod]
14+
[DataRow("item20", "item1", 1, DisplayName = "Numeric segments compared by value")]
15+
[DataRow("apple", "0x10", 1, DisplayName = "Hex less than with non-hex")]
16+
[DataRow("0x1f", "0x0a", 1, DisplayName = "Hex comparison")]
17+
[DataRow("0xB0", "0xaB", 1, DisplayName = "Hex comparison ignoring cases")]
18+
[DataRow("0xa0", "0xA0", 1, DisplayName = "Hex compare cases when numerically equal")]
19+
[DataRow("00000000-0000-0000-ABCD-000000000001", "00000000-0000-0000-0000-ABCDEF000001", 1, DisplayName = "GUID comparison")]
20+
[DataRow("Apple", "F0000000-0000-0000-0000-000000000000", 1, DisplayName = "GUID less than non-GUID")]
21+
[DataRow("-1", "-2", 1, DisplayName = "Negative number comparison")]
22+
[DataRow("0", "-2", 1, DisplayName = "Negative number compared with non-negative")]
23+
[DataRow("-0xa", "-0xf", 1, DisplayName = "Negative hex comparison")]
24+
[DataRow("-0xa", "0xa", 1, DisplayName = "Negative hex compared with non-negative hex")]
25+
public void CompareTo_FollowsLogicalOrdering(string first, string second, int expectedSign)
26+
{
27+
var left = new StringWithLogicalComparison(first);
28+
var right = new StringWithLogicalComparison(second);
29+
30+
Assert.AreEqual(expectedSign, left.CompareTo(right));
31+
Assert.AreEqual(-expectedSign, right.CompareTo(left));
32+
}
33+
34+
[TestMethod]
35+
public void DefaultValue_CompareToBehavesConsistently()
36+
{
37+
var defaultValue = default(StringWithLogicalComparison);
38+
var other = new StringWithLogicalComparison("apple");
39+
40+
Assert.AreEqual(0, defaultValue.CompareTo(default(StringWithLogicalComparison)));
41+
Assert.IsTrue(defaultValue < other);
42+
Assert.IsTrue(other > defaultValue);
43+
}
44+
}

src/Microsoft.Performance.SDK/CompareNumericWithExceptionsMethods.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,19 @@ public int CompareConvertedChar(char c1, char c2)
219219
private static unsafe int _wcstcmpWithNumericExceptions<TCharTraits>(char* string1, char* string2)
220220
where TCharTraits : ICharTraits, new()
221221
{
222+
if (string1 == string2)
223+
{
224+
return firstEqualsSecond;
225+
}
226+
else if (string1 == null)
227+
{
228+
return firstLessThanSecond;
229+
}
230+
else if (string2 == null)
231+
{
232+
return secondLessThanFirst;
233+
}
234+
222235
var charTraits = new TCharTraits();
223236

224237
bool fTreatAsNegative = false;

src/Microsoft.Performance.SDK/StringWithCaseInsensitiveComparison.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
#nullable enable
5+
46
using System;
57
using System.Globalization;
68

@@ -16,7 +18,7 @@ public readonly struct StringWithCaseInsensitiveComparison
1618
IComparable,
1719
IFormattable
1820
{
19-
private readonly string source;
21+
private readonly string? source;
2022

2123
/// <summary>
2224
/// Initializes a new instance of the <see cref="StringWithCaseInsensitiveComparison"/>
@@ -86,13 +88,13 @@ int IComparable.CompareTo(object other)
8688
/// <inheritdoc />
8789
public override string ToString()
8890
{
89-
return this.source;
91+
return this.source ?? string.Empty;
9092
}
9193

9294
/// <inheritdoc />
9395
public string ToString(string format, IFormatProvider formatProvider)
9496
{
95-
return this.source;
97+
return ToString();
9698
}
9799
}
98100
}

src/Microsoft.Performance.SDK/StringWithLogicalComparison.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
#nullable enable
5+
46
using System;
57

68
namespace Microsoft.Performance.SDK
@@ -14,7 +16,7 @@ public readonly struct StringWithLogicalComparison
1416
IComparable,
1517
IFormattable
1618
{
17-
private readonly string source;
19+
private readonly string? source;
1820

1921
/// <summary>
2022
/// Initializes a new instance of the <see cref="StringWithLogicalComparison"/>
@@ -83,13 +85,13 @@ int IComparable.CompareTo(object other)
8385
/// <inheritdoc />
8486
public override string ToString()
8587
{
86-
return this.source;
88+
return this.source ?? string.Empty;
8789
}
8890

8991
/// <inheritdoc />
9092
public string ToString(string format, IFormatProvider formatProvider)
9193
{
92-
return this.source;
94+
return ToString();
9395
}
9496
}
9597
}

0 commit comments

Comments
 (0)