diff --git a/csharp/Platform.Collections.Tests/ArrayStringVsArraySegmentComparisonTests.cs b/csharp/Platform.Collections.Tests/ArrayStringVsArraySegmentComparisonTests.cs new file mode 100644 index 00000000..66bfd693 --- /dev/null +++ b/csharp/Platform.Collections.Tests/ArrayStringVsArraySegmentComparisonTests.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; +using Platform.Collections.Arrays; +using Platform.Collections.Segments; + +namespace Platform.Collections.Tests +{ + /// + /// Tests comparing ArrayString<T> with .NET's ArraySegment<T> to highlight similarities and differences. + /// Тесты, сравнивающие ArrayString<T> с ArraySegment<T> из .NET для выявления сходств и различий. + /// + public class ArrayStringVsArraySegmentComparisonTests + { + [Fact] + public void ConstructorsComparison_ShouldShowDifferences() + { + // ArrayString constructors + var arrayString1 = new ArrayString(5); // Creates new array with length 5 + var arrayString2 = new ArrayString(new int[] { 1, 2, 3, 4, 5 }); // Uses entire existing array + var arrayString3 = new ArrayString(new int[] { 1, 2, 3, 4, 5 }, 3); // Uses first 3 elements + + // ArraySegment constructors + var arraySegment1 = new ArraySegment(new int[] { 1, 2, 3, 4, 5 }); // Uses entire array + var arraySegment2 = new ArraySegment(new int[] { 1, 2, 3, 4, 5 }, 1, 3); // Uses offset + count + + // ArrayString always starts from index 0, ArraySegment can start from any offset + Assert.Equal(0, arrayString2.Offset); + Assert.Equal(5, arrayString2.Count); + + Assert.Equal(0, arraySegment1.Offset); + Assert.Equal(5, arraySegment1.Count); + + Assert.Equal(1, arraySegment2.Offset); + Assert.Equal(3, arraySegment2.Count); + } + + [Fact] + public void TypeComparison_ShouldShowClassVsStruct() + { + var arrayString = new ArrayString(new int[] { 1, 2, 3 }); + var arraySegment = new ArraySegment(new int[] { 1, 2, 3 }); + + // ArrayString is a class (reference type), ArraySegment is a struct (value type) + Assert.True(arrayString.GetType().IsClass); + Assert.False(arraySegment.GetType().IsClass); + Assert.True(arraySegment.GetType().IsValueType); + } + + [Fact] + public void InterfacesComparison_ShouldShowDifferences() + { + var arrayString = new ArrayString(new int[] { 1, 2, 3 }); + var arraySegment = new ArraySegment(new int[] { 1, 2, 3 }); + + // Both implement IList, ICollection, IEnumerable + Assert.IsAssignableFrom>(arrayString); + Assert.IsAssignableFrom>(arrayString); + Assert.IsAssignableFrom>(arrayString); + + Assert.IsAssignableFrom>(arraySegment); + Assert.IsAssignableFrom>(arraySegment); + Assert.IsAssignableFrom>(arraySegment); + + // ArraySegment also implements IReadOnlyList and IReadOnlyCollection + Assert.IsAssignableFrom>(arraySegment); + Assert.IsAssignableFrom>(arraySegment); + } + + [Fact] + public void ReadOnlyBehaviorComparison_ShouldShowSimilarities() + { + var source = new int[] { 1, 2, 3, 4, 5 }; + var arrayString = new ArrayString(source); + var arraySegment = new ArraySegment(source); + + // Both are read-only (cannot insert, add, remove, clear) + Assert.True(arrayString.IsReadOnly); + // Cast to ICollection to access IsReadOnly property + Assert.True(((ICollection)arraySegment).IsReadOnly); + + Assert.Throws(() => arrayString.Add(6)); + Assert.Throws(() => ((ICollection)arraySegment).Add(6)); + + Assert.Throws(() => arrayString.Insert(0, 0)); + Assert.Throws(() => ((IList)arraySegment).Insert(0, 0)); + + Assert.Throws(() => arrayString.Remove(1)); + Assert.Throws(() => ((ICollection)arraySegment).Remove(1)); + + Assert.Throws(() => arrayString.Clear()); + Assert.Throws(() => ((ICollection)arraySegment).Clear()); + + Assert.Throws(() => arrayString.RemoveAt(0)); + Assert.Throws(() => ((IList)arraySegment).RemoveAt(0)); + } + + [Fact] + public void IndexerBehaviorComparison_ShouldShowSimilarities() + { + var source = new int[] { 1, 2, 3, 4, 5 }; + var arrayString = new ArrayString(source); + var arraySegment = new ArraySegment(source); + + // Both allow reading and writing through indexer + Assert.Equal(1, arrayString[0]); + Assert.Equal(1, arraySegment[0]); + + // Both allow modification through indexer (affects original array) + arrayString[0] = 10; + Assert.Equal(10, source[0]); + + arraySegment[1] = 20; + Assert.Equal(20, source[1]); + + // Verify both see the changes + Assert.Equal(10, arrayString[0]); + Assert.Equal(20, arraySegment[1]); + } + + [Fact] + public void SegmentBehaviorComparison_ShouldShowDifferences() + { + var source = new int[] { 1, 2, 3, 4, 5 }; + + // ArrayString always starts from 0 and can only specify length + var arrayString = new ArrayString(source, 3); // First 3 elements + + // ArraySegment can start from any offset with any count + var arraySegment = new ArraySegment(source, 2, 2); // Elements at indices 2 and 3 + + Assert.Equal(3, arrayString.Count); + Assert.Equal(0, arrayString.Offset); + Assert.Equal(1, arrayString[0]); // source[0] + Assert.Equal(2, arrayString[1]); // source[1] + Assert.Equal(3, arrayString[2]); // source[2] + + Assert.Equal(2, arraySegment.Count); + Assert.Equal(2, arraySegment.Offset); + Assert.Equal(3, arraySegment[0]); // source[2] + Assert.Equal(4, arraySegment[1]); // source[3] + } + + [Fact] + public void EnumerationComparison_ShouldShowSimilarities() + { + var source = new int[] { 1, 2, 3, 4, 5 }; + var arrayString = new ArrayString(source, 3); + var arraySegment = new ArraySegment(source, 0, 3); + + // Both support enumeration + var arrayStringList = arrayString.ToList(); + var arraySegmentList = arraySegment.ToList(); + + Assert.Equal(new[] { 1, 2, 3 }, arrayStringList); + Assert.Equal(new[] { 1, 2, 3 }, arraySegmentList); + + // Both support LINQ operations + Assert.Equal(3, arrayString.Count(x => x <= 3)); + Assert.Equal(3, arraySegment.Count(x => x <= 3)); + } + + [Fact] + public void EqualityComparison_ShouldShowDifferences() + { + var source1 = new int[] { 1, 2, 3 }; + var source2 = new int[] { 1, 2, 3 }; + + var arrayString1 = new ArrayString(source1); + var arrayString2 = new ArrayString(source2); + var arrayString3 = new ArrayString(source1); // Same reference + + var arraySegment1 = new ArraySegment(source1); + var arraySegment2 = new ArraySegment(source2); + var arraySegment3 = new ArraySegment(source1); // Same reference + + // ArrayString uses custom equality implementation (content-based via EqualTo extension) + Assert.True(arrayString1.Equals(arrayString2)); // Different arrays, same content + Assert.True(arrayString1.Equals(arrayString3)); // Same array reference + + // ArraySegment uses default struct equality (checks array reference and offset/count) + Assert.False(arraySegment1.Equals(arraySegment2)); // Different array references + Assert.True(arraySegment1.Equals(arraySegment3)); // Same array reference + } + + [Fact] + public void HashCodeComparison_ShouldShowDifferences() + { + var source1 = new int[] { 1, 2, 3 }; + var source2 = new int[] { 1, 2, 3 }; + + var arrayString1 = new ArrayString(source1); + var arrayString2 = new ArrayString(source2); + + var arraySegment1 = new ArraySegment(source1); + var arraySegment2 = new ArraySegment(source2); + + // ArrayString generates hash code based on content + Assert.Equal(arrayString1.GetHashCode(), arrayString2.GetHashCode()); // Same content, same hash + + // ArraySegment generates hash code based on array reference and segment info + Assert.NotEqual(arraySegment1.GetHashCode(), arraySegment2.GetHashCode()); // Different arrays, different hash + } + + [Fact] + public void MemoryAllocationComparison_ShouldShowDifferences() + { + var source = new int[] { 1, 2, 3, 4, 5 }; + + // ArrayString can create new arrays (allocation) or wrap existing ones + var arrayStringNew = new ArrayString(5); // Allocates new array + var arrayStringWrapped = new ArrayString(source); // Wraps existing array + + // ArraySegment always wraps existing arrays (no allocation) + var arraySegment = new ArraySegment(source); + + Assert.NotNull(arrayStringNew.Base); + Assert.Same(source, arrayStringWrapped.Base); + Assert.Same(source, arraySegment.Array); + } + + [Fact] + public void DefaultValueComparison_ShouldShowDifferences() + { + // Default ArrayString is null (reference type) + ArrayString? defaultArrayString = default; + Assert.Null(defaultArrayString); + + // Default ArraySegment represents an empty segment (value type) + ArraySegment defaultArraySegment = default; + // Array property returns null for default ArraySegment, but struct itself is not null + Assert.Null(defaultArraySegment.Array); + Assert.Equal(0, defaultArraySegment.Count); + Assert.Equal(0, defaultArraySegment.Offset); + } + + [Fact] + public void PerformanceCharacteristics_ComparisonNotes() + { + // This test documents performance characteristics rather than testing them + + var source = new int[] { 1, 2, 3, 4, 5 }; + var arrayString = new ArrayString(source); + var arraySegment = new ArraySegment(source); + + // ArrayString (class): + // - Heap allocation overhead + // - Reference equality fast, content equality slower + // - Garbage collection overhead + + // ArraySegment (struct): + // - No heap allocation for the segment itself + // - Value type copying overhead when passed by value + // - No garbage collection overhead for the segment + + // Both: + // - O(1) indexer access + // - Same memory access patterns for the underlying array + // - Similar enumeration performance + + Assert.True(true); // This test always passes, it's for documentation + } + } +} \ No newline at end of file