Skip to content

Commit

Permalink
Hash code tests cleanup (#97299)
Browse files Browse the repository at this point in the history
* Moving the tests, cleaning up.

* Removal.

* Rename: fix build.

* Fix order, apply feedback

* Missing changes.

* Fixed by #96354

* This test uses SortKey, should not be enabled.

* Nit

* These methods use `CompareOptions = IgnoreCase, IgnoreKanaType, IgnoreWidth`, should stay blocked.
  • Loading branch information
ilonatommy committed Jan 25, 2024
1 parent 8d87ccc commit 75dc1b3
Show file tree
Hide file tree
Showing 6 changed files with 413 additions and 400 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public void Default_GetHashCodeCompare(object a, object b, bool expected)
CaseInsensitiveHashCodeProvider.DefaultInvariant.GetHashCode(a) == CaseInsensitiveHashCodeProvider.DefaultInvariant.GetHashCode(b));
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))]
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/37069", TestPlatforms.Android | TestPlatforms.LinuxBionic)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))]
public void Default_Compare_TurkishI()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,46 +12,22 @@ namespace System.Globalization.Tests
{
public class CompareInfoHashCodeTests : CompareInfoTestsBase
{
public class CustomComparer : StringComparer
{
private readonly CompareInfo _compareInfo;
private readonly CompareOptions _compareOptions;

public CustomComparer(CompareInfo cmpInfo, CompareOptions cmpOptions)
{
_compareInfo = cmpInfo;
_compareOptions = cmpOptions;
}

public override int Compare(string x, string y) =>
_compareInfo.Compare(x, y, _compareOptions);

public override bool Equals(string x, string y) =>
_compareInfo.Compare(x, y, _compareOptions) == 0;

public override int GetHashCode(string obj)
{
return _compareInfo.GetHashCode(obj, _compareOptions);
}
}

public static IEnumerable<object[]> HashCodeLocalized_TestData()
{
yield return new object[] { s_invariantCompare, "foo", "Foo", CompareOptions.IgnoreCase };
yield return new object[] { s_invariantCompare, "igloo", "\u0130GLOO", CompareOptions.IgnoreCase }; // FAILS
yield return new object[] { s_invariantCompare, "igloo", "\u0130GLOO", CompareOptions.IgnoreCase };
yield return new object[] { s_invariantCompare, "igloo", "\u0130GLOO", CompareOptions.None };
yield return new object[] { s_invariantCompare, "igloo", "IGLOO", CompareOptions.IgnoreCase };
yield return new object[] { new CultureInfo("pl-PL").CompareInfo, "igloo", "\u0130GLOO", CompareOptions.IgnoreCase }; // FAILS
yield return new object[] { new CultureInfo("pl-PL").CompareInfo, "igloo", "\u0130GLOO", CompareOptions.IgnoreCase };
yield return new object[] { new CultureInfo("pl-PL").CompareInfo, "igloo", "IGLOO", CompareOptions.IgnoreCase };
yield return new object[] { new CultureInfo("tr-TR").CompareInfo, "igloo", "\u0130GLOO", CompareOptions.IgnoreCase };
yield return new object[] { new CultureInfo("tr-TR").CompareInfo, "igloo", "IGLOO", CompareOptions.IgnoreCase }; // FAILS
yield return new object[] { new CultureInfo("tr-TR").CompareInfo, "igloo", "IGLOO", CompareOptions.IgnoreCase };

if (!PlatformDetection.IsHybridGlobalizationOnBrowser)
{
// ActiveIssue: https://github.com/dotnet/runtime/issues/96400
yield return new object[] { new CultureInfo("en-GB").CompareInfo, "100", "100!", CompareOptions.IgnoreSymbols }; // HG: equal: True, hashCodesEqual: False
yield return new object[] { new CultureInfo("ja-JP").CompareInfo, "\u30A2", "\u3042", CompareOptions.IgnoreKanaType }; // HG: equal: True, hashCodesEqual: False
yield return new object[] { new CultureInfo("en-GB").CompareInfo, "caf\u00E9", "cafe\u0301", CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreKanaType }; // HG: equal: True, hashCodesEqual: False
yield return new object[] { new CultureInfo("en-GB").CompareInfo, "caf\u00E9", "cafe\u0301", CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreKanaType }; // HG: equal: True, hashCodesEqual: False
}
}

Expand Down Expand Up @@ -135,5 +111,67 @@ public void CheckHashingOfSkippedChars(char character, CompareInfo cmpInfo)
StringComparer stringComparer = new CustomComparer(cmpInfo, options);
TryAddToCustomDictionary(stringComparer, str1, str2, areHashCodesEqual);
}

public static IEnumerable<object[]> GetHashCodeTestData => new[]
{
new object[] { "abc", CompareOptions.OrdinalIgnoreCase, "ABC", CompareOptions.OrdinalIgnoreCase, true },
new object[] { "abc", CompareOptions.Ordinal, "ABC", CompareOptions.Ordinal, false },
new object[] { "abc", CompareOptions.Ordinal, "abc", CompareOptions.Ordinal, true },
new object[] { "abc", CompareOptions.None, "abc", CompareOptions.None, true },
new object[] { "", CompareOptions.None, "\u200c", CompareOptions.None, true }, // see comment at bottom of SortKey_TestData
};

[Theory]
[MemberData(nameof(GetHashCodeTestData))]
public void GetHashCodeTest(string source1, CompareOptions options1, string source2, CompareOptions options2, bool expected)
{
CompareInfo invariantCompare = CultureInfo.InvariantCulture.CompareInfo;
Assert.Equal(expected, invariantCompare.GetHashCode(source1, options1).Equals(invariantCompare.GetHashCode(source2, options2)));
}

[Theory]
[MemberData(nameof(GetHashCodeTestData))]
public void GetHashCode_Span(string source1, CompareOptions options1, string source2, CompareOptions options2, bool expectSameHashCode)
{
CompareInfo invariantCompare = CultureInfo.InvariantCulture.CompareInfo;

int hashOfSource1AsString = invariantCompare.GetHashCode(source1, options1);
int hashOfSource1AsSpan = invariantCompare.GetHashCode(source1.AsSpan(), options1);
Assert.Equal(hashOfSource1AsString, hashOfSource1AsSpan);

int hashOfSource2AsString = invariantCompare.GetHashCode(source2, options2);
int hashOfSource2AsSpan = invariantCompare.GetHashCode(source2.AsSpan(), options2);
Assert.Equal(hashOfSource2AsString, hashOfSource2AsSpan);

Assert.Equal(expectSameHashCode, hashOfSource1AsSpan == hashOfSource2AsSpan);
}

[Fact]
public void GetHashCode_Invalid()
{
AssertExtensions.Throws<ArgumentNullException>("source", () => CultureInfo.InvariantCulture.CompareInfo.GetHashCode(null, CompareOptions.None));

AssertExtensions.Throws<ArgumentException>("options", () => CultureInfo.InvariantCulture.CompareInfo.GetHashCode("Test", CompareOptions.OrdinalIgnoreCase | CompareOptions.IgnoreCase));
AssertExtensions.Throws<ArgumentException>("options", () => CultureInfo.InvariantCulture.CompareInfo.GetHashCode("Test", CompareOptions.Ordinal | CompareOptions.IgnoreSymbols));
AssertExtensions.Throws<ArgumentException>("options", () => CultureInfo.InvariantCulture.CompareInfo.GetHashCode("Test", (CompareOptions)(-1)));
}

[Fact]
public void GetHashCode_NullAndEmptySpan()
{
// Ensure that null spans and non-null empty spans produce the same hash code.

int hashCodeOfNullSpan = CultureInfo.InvariantCulture.CompareInfo.GetHashCode(ReadOnlySpan<char>.Empty, CompareOptions.None);
int hashCodeOfNotNullEmptySpan = CultureInfo.InvariantCulture.CompareInfo.GetHashCode("".AsSpan(), CompareOptions.None);
Assert.Equal(hashCodeOfNullSpan, hashCodeOfNotNullEmptySpan);
}

[Fact]
public void GetHashCode_Span_Invalid()
{
AssertExtensions.Throws<ArgumentException>("options", () => CultureInfo.InvariantCulture.CompareInfo.GetHashCode("Test".AsSpan(), CompareOptions.OrdinalIgnoreCase | CompareOptions.IgnoreCase));
AssertExtensions.Throws<ArgumentException>("options", () => CultureInfo.InvariantCulture.CompareInfo.GetHashCode("Test".AsSpan(), CompareOptions.Ordinal | CompareOptions.IgnoreSymbols));
AssertExtensions.Throws<ArgumentException>("options", () => CultureInfo.InvariantCulture.CompareInfo.GetHashCode("Test".AsSpan(), (CompareOptions)(-1)));
}
}
}
Loading

0 comments on commit 75dc1b3

Please sign in to comment.