Skip to content

Commit dc9ce64

Browse files
Add tests verifying null is passed through to keySelector
- KeySelectorPassesNullToSelector: verifies null flows to keySelector in both Equals and GetHashCode (not short-circuited), with call counting. - KeySelectorReturnsNullKey: verifies correct behavior when keySelector maps an input to a null key (GetHashCode returns 0, Equals uses key-level null comparison). - KeySelectorNotHandlingNull_Throws: verifies NullReferenceException propagates when keySelector doesn't guard against null. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent ef62796 commit dc9ce64

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

src/libraries/System.Collections/tests/Generic/Comparers/EqualityComparer.Tests.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,67 @@ public void EqualityComparerCreate_KeySelectorUsed()
540540
Assert.Equal(comparer.GetHashCode(original), comparer.GetHashCode(otherEqualLen));
541541
}
542542

543+
[Fact]
544+
public void EqualityComparerCreate_KeySelectorPassesNullToSelector()
545+
{
546+
int selectorCalls = 0;
547+
var comparer = EqualityComparer<string>.Create<string>(str =>
548+
{
549+
selectorCalls++;
550+
return str ?? "default";
551+
});
552+
553+
// Null is passed through to keySelector in Equals, mapping to "default"
554+
Assert.True(comparer.Equals(null, null));
555+
Assert.Equal(2, selectorCalls);
556+
557+
selectorCalls = 0;
558+
Assert.True(comparer.Equals(null, "default"));
559+
Assert.Equal(2, selectorCalls);
560+
561+
selectorCalls = 0;
562+
Assert.True(comparer.Equals("default", null));
563+
Assert.Equal(2, selectorCalls);
564+
565+
selectorCalls = 0;
566+
Assert.False(comparer.Equals(null, "other"));
567+
Assert.Equal(2, selectorCalls);
568+
569+
// Null is passed through to keySelector in GetHashCode
570+
selectorCalls = 0;
571+
int hashCode = comparer.GetHashCode(null);
572+
Assert.Equal(1, selectorCalls);
573+
Assert.Equal(comparer.GetHashCode("default"), hashCode);
574+
}
575+
576+
[Fact]
577+
public void EqualityComparerCreate_KeySelectorReturnsNullKey()
578+
{
579+
var comparer = EqualityComparer<string>.Create<string>(str => str == "nil" ? null : str);
580+
581+
// When keySelector returns null for both, they are equal
582+
Assert.True(comparer.Equals("nil", "nil"));
583+
584+
// When keySelector returns null for one side only, they are not equal
585+
Assert.False(comparer.Equals("nil", "foo"));
586+
Assert.False(comparer.Equals("foo", "nil"));
587+
588+
// GetHashCode returns 0 for a null key
589+
Assert.Equal(0, comparer.GetHashCode("nil"));
590+
Assert.Equal(comparer.GetHashCode("nil"), comparer.GetHashCode("nil"));
591+
}
592+
593+
[Fact]
594+
public void EqualityComparerCreate_KeySelectorNotHandlingNull_Throws()
595+
{
596+
var comparer = EqualityComparer<string>.Create<int>(str => str.Length);
597+
598+
// keySelector doesn't guard against null, so NullReferenceException propagates
599+
Assert.Throws<NullReferenceException>(() => comparer.Equals(null, "foo"));
600+
Assert.Throws<NullReferenceException>(() => comparer.Equals("foo", null));
601+
Assert.Throws<NullReferenceException>(() => comparer.GetHashCode(null));
602+
}
603+
543604
[Fact]
544605
public void EqualityComparerCreate_KeySelectorComparerUsed()
545606
{

0 commit comments

Comments
 (0)