-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Fix IdSpan equality violation: Create("") and default now correctly unequal #9902
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,123 @@ | ||||||||||||||||||||||||
| using Orleans.Runtime; | ||||||||||||||||||||||||
| using Xunit; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| namespace NonSilo.Tests | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||
| /// Tests for IdSpan, a primitive type for identities representing a sequence of bytes. | ||||||||||||||||||||||||
| /// Validates equality, hash code consistency, and proper handling of null vs empty arrays. | ||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||
| [TestCategory("BVT")] | ||||||||||||||||||||||||
| public class IdSpanTests | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||
| /// Tests that IdSpan.Create(string.Empty) and default(IdSpan) are NOT equal. | ||||||||||||||||||||||||
| /// They should have different internal states (empty array vs null) and should not be considered equal. | ||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||
| [Fact] | ||||||||||||||||||||||||
| public void IdSpan_CreateEmptyString_NotEqualToDefault() | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| IdSpan createdFromEmptyString = IdSpan.Create(string.Empty); | ||||||||||||||||||||||||
| IdSpan defaultIdSpan = default; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Assert.NotEqual(createdFromEmptyString, defaultIdSpan); | ||||||||||||||||||||||||
| Assert.False(createdFromEmptyString.Equals(defaultIdSpan)); | ||||||||||||||||||||||||
| Assert.False(createdFromEmptyString == defaultIdSpan); | ||||||||||||||||||||||||
| Assert.True(createdFromEmptyString != defaultIdSpan); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||
| /// Tests that hash codes are consistent with equality. | ||||||||||||||||||||||||
| /// If two IdSpans are equal, they must have the same hash code. | ||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||
| [Fact] | ||||||||||||||||||||||||
| public void IdSpan_HashCode_ConsistentWithEquality() | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| IdSpan id1 = IdSpan.Create("test"); | ||||||||||||||||||||||||
| IdSpan id2 = IdSpan.Create("test"); | ||||||||||||||||||||||||
| IdSpan id3 = IdSpan.Create("different"); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Equal objects must have equal hash codes | ||||||||||||||||||||||||
| Assert.Equal(id1, id2); | ||||||||||||||||||||||||
| Assert.Equal(id1.GetHashCode(), id2.GetHashCode()); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Not equal objects may have different hash codes (not required but expected) | ||||||||||||||||||||||||
| Assert.NotEqual(id1, id3); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||
| /// Tests that default IdSpan has expected properties. | ||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||
| [Fact] | ||||||||||||||||||||||||
| public void IdSpan_Default_HasExpectedProperties() | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| IdSpan defaultIdSpan = default; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Assert.True(defaultIdSpan.IsDefault); | ||||||||||||||||||||||||
| Assert.Equal(0, defaultIdSpan.GetHashCode()); | ||||||||||||||||||||||||
| Assert.Equal("", defaultIdSpan.ToString()); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||
| /// Tests that IdSpan created from empty string has expected properties. | ||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||
| [Fact] | ||||||||||||||||||||||||
| public void IdSpan_CreateEmptyString_HasExpectedProperties() | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| IdSpan emptyStringIdSpan = IdSpan.Create(string.Empty); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Assert.True(emptyStringIdSpan.IsDefault); | ||||||||||||||||||||||||
| Assert.Equal("", emptyStringIdSpan.ToString()); | ||||||||||||||||||||||||
| // Hash code should be computed from empty byte array, not 0 | ||||||||||||||||||||||||
| Assert.NotEqual(0, emptyStringIdSpan.GetHashCode()); | ||||||||||||||||||||||||
|
Comment on lines
+68
to
+72
|
||||||||||||||||||||||||
| Assert.True(emptyStringIdSpan.IsDefault); | |
| Assert.Equal("", emptyStringIdSpan.ToString()); | |
| // Hash code should be computed from empty byte array, not 0 | |
| Assert.NotEqual(0, emptyStringIdSpan.GetHashCode()); | |
| IdSpan defaultIdSpan = default; | |
| Assert.True(emptyStringIdSpan.IsDefault); | |
| Assert.Equal("", emptyStringIdSpan.ToString()); | |
| // Hash code should differ from default(IdSpan) while still representing an "empty" value | |
| Assert.NotEqual(defaultIdSpan.GetHashCode(), emptyStringIdSpan.GetHashCode()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IdSpanCodeccurrently serializes based onvalue.AsSpan(). SinceAsSpan()returns an empty span for both_value == nulland_value.Length == 0, anIdSpancreated from""(empty byte[]) will be serialized the same asdefaultand will deserialize back todefault. With the new equality semantics (null != empty), that means serialization no longer round-trips the value. Consider updatingIdSpanCodecto encode empty arrays distinctly from null/default (and add a regression test for that).