diff options
-rw-r--r-- | src/System.Memory/System.Memory.sln | 1 | ||||
-rw-r--r-- | src/System.Memory/ref/System.Memory.cs | 4 | ||||
-rw-r--r-- | src/System.Memory/src/System/MemoryExtensions.Portable.cs | 52 | ||||
-rw-r--r-- | src/System.Memory/tests/ReadOnlySpan/CompareTo.cs | 279 | ||||
-rw-r--r-- | src/System.Memory/tests/ReadOnlySpan/Contains.cs | 181 | ||||
-rw-r--r-- | src/System.Memory/tests/ReadOnlySpan/Equals.cs | 262 | ||||
-rw-r--r-- | src/System.Memory/tests/ReadOnlySpan/IndexOf.charSpan.cs | 409 | ||||
-rw-r--r-- | src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.char.cs | 12 | ||||
-rw-r--r-- | src/System.Memory/tests/System.Memory.Tests.csproj | 4 |
9 files changed, 1204 insertions, 0 deletions
diff --git a/src/System.Memory/System.Memory.sln b/src/System.Memory/System.Memory.sln index 7c68229780..00692c230f 100644 --- a/src/System.Memory/System.Memory.sln +++ b/src/System.Memory/System.Memory.sln @@ -27,6 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2ED EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3F794AE4-319F-4498-A9DA-C49A4EB11D9E}" ProjectSection(SolutionItems) = preProject + ..\Common\src\CoreLib\System\Runtime\InteropServices\MemoryMarshal.Fast.cs = ..\Common\src\CoreLib\System\Runtime\InteropServices\MemoryMarshal.Fast.cs ..\Common\src\CoreLib\System\ReadOnlySpan.Fast.cs = ..\Common\src\CoreLib\System\ReadOnlySpan.Fast.cs ..\Common\src\CoreLib\System\Span.Fast.cs = ..\Common\src\CoreLib\System\Span.Fast.cs ..\Common\src\CoreLib\System\Span.NonGeneric.cs = ..\Common\src\CoreLib\System\Span.NonGeneric.cs diff --git a/src/System.Memory/ref/System.Memory.cs b/src/System.Memory/ref/System.Memory.cs index e8f33c577d..5c630a8e74 100644 --- a/src/System.Memory/ref/System.Memory.cs +++ b/src/System.Memory/ref/System.Memory.cs @@ -29,11 +29,15 @@ namespace System public static int BinarySearch<T, TComparable>(this System.ReadOnlySpan<T> span, TComparable comparable) where TComparable : System.IComparable<T> { throw null; } public static int BinarySearch<T, TComparer>(this System.Span<T> span, T value, TComparer comparer) where TComparer : System.Collections.Generic.IComparer<T> { throw null; } public static int BinarySearch<T, TComparable>(this System.Span<T> span, TComparable comparable) where TComparable : System.IComparable<T> { throw null; } + public static int CompareTo(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; } + public static bool Contains(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; } public static void CopyTo<T>(this T[] array, System.Memory<T> destination) { } public static void CopyTo<T>(this T[] array, System.Span<T> destination) { } public static bool EndsWith(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; } public static bool EndsWith<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; } public static bool EndsWith<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; } + public static bool Equals(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; } + public static int IndexOf(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; } public static int IndexOfAny<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T> { throw null; } public static int IndexOfAny<T>(this System.ReadOnlySpan<T> span, T value0, T value1) where T : System.IEquatable<T> { throw null; } public static int IndexOfAny<T>(this System.ReadOnlySpan<T> span, T value0, T value1, T value2) where T : System.IEquatable<T> { throw null; } diff --git a/src/System.Memory/src/System/MemoryExtensions.Portable.cs b/src/System.Memory/src/System/MemoryExtensions.Portable.cs index 0cfbff8f37..d5c36d3968 100644 --- a/src/System.Memory/src/System/MemoryExtensions.Portable.cs +++ b/src/System.Memory/src/System/MemoryExtensions.Portable.cs @@ -14,6 +14,58 @@ namespace System public static partial class MemoryExtensions { /// <summary> + /// Returns a value indicating whether the specified <paramref name="value"/> occurs within the <paramref name="span"/>. + /// <param name="span">The source span.</param> + /// <param name="value">The value to seek within the source span.</param> + /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param> + /// </summary> + public static bool Contains(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType) + => (IndexOf(span, value, comparisonType) >= 0); + + /// <summary> + /// Determines whether this <paramref name="span"/> and the specified <paramref name="value"/> span have the same characters + /// when compared using the specified <paramref name="comparisonType"/> option. + /// <param name="span">The source span.</param> + /// <param name="value">The value to compare with the source span.</param> + /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param> + /// </summary> + public static bool Equals(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType) + { + if (comparisonType == StringComparison.Ordinal) + { + return span.SequenceEqual<char>(value); + } + + return span.ToString().Equals(value.ToString(), comparisonType); + } + + /// <summary> + /// Compares the specified <paramref name="span"/> and <paramref name="value"/> using the specified <paramref name="comparisonType"/>, + /// and returns an integer that indicates their relative position in the sort order. + /// <param name="span">The source span.</param> + /// <param name="value">The value to compare with the source span.</param> + /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param> + /// </summary> + public static int CompareTo(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType) + => string.Compare(span.ToString(), value.ToString(), comparisonType); + + /// <summary> + /// Reports the zero-based index of the first occurrence of the specified <paramref name="value"/> in the current <paramref name="span"/>. + /// <param name="span">The source span.</param> + /// <param name="value">The value to seek within the source span.</param> + /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param> + /// </summary> + public static int IndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType) + { + if (comparisonType == StringComparison.Ordinal) + { + return span.IndexOf<char>(value); + } + + return span.ToString().IndexOf(value.ToString(), comparisonType); + } + + /// <summary> /// Copies the characters from the source span into the destination, converting each character to lowercase, /// using the casing rules of the specified culture. /// </summary> diff --git a/src/System.Memory/tests/ReadOnlySpan/CompareTo.cs b/src/System.Memory/tests/ReadOnlySpan/CompareTo.cs new file mode 100644 index 0000000000..ab1f294fe1 --- /dev/null +++ b/src/System.Memory/tests/ReadOnlySpan/CompareTo.cs @@ -0,0 +1,279 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Globalization; +using System.Threading; +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void ZeroLengthCompareTo_StringComparison() + { + char[] a = { '4', '5', '6' }; + + var span = new ReadOnlySpan<char>(a); + var slice = new ReadOnlySpan<char>(a, 2, 0); + Assert.True(0 < span.CompareTo(slice, StringComparison.Ordinal)); + + Assert.True(0 < span.CompareTo(slice, StringComparison.CurrentCulture)); + Assert.True(0 < span.CompareTo(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(0 < span.CompareTo(slice, StringComparison.InvariantCulture)); + Assert.True(0 < span.CompareTo(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(0 < span.CompareTo(slice, StringComparison.OrdinalIgnoreCase)); + + span = new ReadOnlySpan<char>(a, 1, 0); + Assert.Equal(0, span.CompareTo(slice, StringComparison.Ordinal)); + + Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCulture)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCulture)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void SameSpanCompareTo_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a); + Assert.Equal(0, span.CompareTo(span, StringComparison.Ordinal)); + + Assert.Equal(0, span.CompareTo(span, StringComparison.CurrentCulture)); + Assert.Equal(0, span.CompareTo(span, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(0, span.CompareTo(span, StringComparison.InvariantCulture)); + Assert.Equal(0, span.CompareTo(span, StringComparison.InvariantCultureIgnoreCase)); + Assert.Equal(0, span.CompareTo(span, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void LengthMismatchCompareTo_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a, 0, 2); + var slice = new ReadOnlySpan<char>(a, 0, 3); + Assert.True(0 > span.CompareTo(slice, StringComparison.Ordinal)); + + Assert.True(0 > span.CompareTo(slice, StringComparison.CurrentCulture)); + Assert.True(0 > span.CompareTo(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(0 > span.CompareTo(slice, StringComparison.InvariantCulture)); + Assert.True(0 > span.CompareTo(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(0 > span.CompareTo(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void CompareToOverlappingMatch_StringComparison() + { + char[] a = { '4', '5', '6', '5', '6', '5' }; + var span = new ReadOnlySpan<char>(a, 1, 3); + var slice = new ReadOnlySpan<char>(a, 3, 3); + Assert.Equal(0, span.CompareTo(slice, StringComparison.Ordinal)); + + Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCulture)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCulture)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void CompareToMatchDifferentSpans_StringComparison() + { + char[] a = { '4', '5', '6', '7' }; + char[] b = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a, 0, 3); + var slice = new ReadOnlySpan<char>(b, 0, 3); + Assert.Equal(0, span.CompareTo(slice, StringComparison.Ordinal)); + + Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCulture)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCulture)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.Equal(0, span.CompareTo(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void CompareToNoMatch_StringComparison() + { + for (int length = 1; length < 150; length++) + { + for (int mismatchIndex = 0; mismatchIndex < length; mismatchIndex++) + { + var first = new char[length]; + var second = new char[length]; + for (int i = 0; i < length; i++) + { + first[i] = second[i] = (char)(i + 1); + } + + second[mismatchIndex] = (char)(second[mismatchIndex] + 1); + + var firstSpan = new ReadOnlySpan<char>(first); + var secondSpan = new ReadOnlySpan<char>(second); + Assert.True(0 > firstSpan.CompareTo(secondSpan, StringComparison.Ordinal)); + + Assert.Equal( + string.Compare(firstSpan.ToString(), secondSpan.ToString(), StringComparison.OrdinalIgnoreCase), + firstSpan.CompareTo(secondSpan, StringComparison.OrdinalIgnoreCase)); + Assert.Equal( + string.Compare(firstSpan.ToString(), secondSpan.ToString(), StringComparison.CurrentCulture), + firstSpan.CompareTo(secondSpan, StringComparison.CurrentCulture)); + Assert.Equal( + string.Compare(firstSpan.ToString(), secondSpan.ToString(), StringComparison.CurrentCultureIgnoreCase), + firstSpan.CompareTo(secondSpan, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal( + string.Compare(firstSpan.ToString(), secondSpan.ToString(), StringComparison.InvariantCulture), + firstSpan.CompareTo(secondSpan, StringComparison.InvariantCulture)); + Assert.Equal( + string.Compare(firstSpan.ToString(), secondSpan.ToString(), StringComparison.InvariantCultureIgnoreCase), + firstSpan.CompareTo(secondSpan, StringComparison.InvariantCultureIgnoreCase)); + } + } + } + + [Fact] + public static void MakeSureNoCompareToChecksGoOutOfRange_StringComparison() + { + for (int length = 0; length < 100; length++) + { + var first = new char[length + 2]; + first[0] = (char)99; + first[length + 1] = (char)99; + var second = new char[length + 2]; + second[0] = (char)100; + second[length + 1] = (char)100; + var span1 = new ReadOnlySpan<char>(first, 1, length); + var span2 = new ReadOnlySpan<char>(second, 1, length); + Assert.Equal(0, span1.CompareTo(span2, StringComparison.Ordinal)); + + Assert.Equal(0, span1.CompareTo(span2, StringComparison.CurrentCulture)); + Assert.Equal(0, span1.CompareTo(span2, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(0, span1.CompareTo(span2, StringComparison.InvariantCulture)); + Assert.Equal(0, span1.CompareTo(span2, StringComparison.InvariantCultureIgnoreCase)); + Assert.Equal(0, span1.CompareTo(span2, StringComparison.OrdinalIgnoreCase)); + } + } + + [Fact] + public static void CompareToUnknownComparisonType_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a); + TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.CompareTo(_span, StringComparison.CurrentCulture - 1)); + TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.CompareTo(_span, StringComparison.OrdinalIgnoreCase + 1)); + TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.CompareTo(_span, (StringComparison)6)); + } + + [Theory] + // CurrentCulture + [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.CurrentCulture, 0)] + [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.CurrentCulture, 1)] + [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.CurrentCulture, -1)] + [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.CurrentCulture, 1)] + [InlineData("hello", 2, "HELLO", 2, 3, StringComparison.CurrentCulture, -1)] + [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.CurrentCulture, 0)] + [InlineData("Hello", 2, "Goodbye", 2, 3, StringComparison.CurrentCulture, -1)] + [InlineData("A", 0, "B", 0, 1, StringComparison.CurrentCulture, -1)] + [InlineData("B", 0, "A", 0, 1, StringComparison.CurrentCulture, 1)] + // CurrentCultureIgnoreCase + [InlineData("HELLO", 0, "hello", 0, 5, StringComparison.CurrentCultureIgnoreCase, 0)] + [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.CurrentCultureIgnoreCase, 0)] + [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.CurrentCultureIgnoreCase, 0)] + [InlineData("Hello", 2, "Yellow", 2, 3, StringComparison.CurrentCultureIgnoreCase, 0)] + [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.CurrentCultureIgnoreCase, 1)] + [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.CurrentCultureIgnoreCase, -1)] + [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.CurrentCultureIgnoreCase, 0)] + [InlineData("Hello", 2, "Goodbye", 2, 3, StringComparison.CurrentCultureIgnoreCase, -1)] + // InvariantCulture + [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.InvariantCulture, 0)] + [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.InvariantCulture, 1)] + [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.InvariantCulture, -1)] + [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.InvariantCulture, 1)] + [InlineData("hello", 2, "HELLO", 2, 3, StringComparison.InvariantCulture, -1)] + // InvariantCultureIgnoreCase + [InlineData("HELLO", 0, "hello", 0, 5, StringComparison.InvariantCultureIgnoreCase, 0)] + [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.InvariantCultureIgnoreCase, 0)] + [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.InvariantCultureIgnoreCase, 0)] + [InlineData("Hello", 2, "Yellow", 2, 3, StringComparison.InvariantCultureIgnoreCase, 0)] + [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.InvariantCultureIgnoreCase, 1)] + [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.InvariantCultureIgnoreCase, -1)] + [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.InvariantCultureIgnoreCase, 0)] + [InlineData("Hello", 2, "Goodbye", 2, 3, StringComparison.InvariantCultureIgnoreCase, -1)] + // Ordinal + [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.Ordinal, 0)] + [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.Ordinal, 1)] + [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.Ordinal, -1)] + [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.Ordinal, 0)] + [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.Ordinal, -1)] + [InlineData("Hello", 2, "Goodbye", 2, 3, StringComparison.Ordinal, -1)] + [InlineData("Hello", 0, "Hello", 0, 0, StringComparison.Ordinal, 0)] + [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.Ordinal, 0)] + [InlineData("Hello", 0, "Hello", 0, 3, StringComparison.Ordinal, 0)] + [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.Ordinal, 0)] + [InlineData("Hello", 0, "He" + SoftHyphen + "llo", 0, 5, StringComparison.Ordinal, -1)] + [InlineData("Hello", 0, "-=<Hello>=-", 3, 5, StringComparison.Ordinal, 0)] + [InlineData("\uD83D\uDD53Hello\uD83D\uDD50", 1, "\uD83D\uDD53Hello\uD83D\uDD54", 1, 7, StringComparison.Ordinal, 0)] // Surrogate split + [InlineData("Hello", 0, "Hello123", 0, int.MaxValue, StringComparison.Ordinal, -1)] // Recalculated length, second string longer + [InlineData("Hello123", 0, "Hello", 0, int.MaxValue, StringComparison.Ordinal, 1)] // Recalculated length, first string longer + [InlineData("---aaaaaaaaaaa", 3, "+++aaaaaaaaaaa", 3, 100, StringComparison.Ordinal, 0)] // Equal long alignment 2, equal compare + [InlineData("aaaaaaaaaaaaaa", 3, "aaaxaaaaaaaaaa", 3, 100, StringComparison.Ordinal, -1)] // Equal long alignment 2, different compare at n=1 + [InlineData("-aaaaaaaaaaaaa", 1, "+aaaaaaaaaaaaa", 1, 100, StringComparison.Ordinal, 0)] // Equal long alignment 6, equal compare + [InlineData("aaaaaaaaaaaaaa", 1, "axaaaaaaaaaaaa", 1, 100, StringComparison.Ordinal, -1)] // Equal long alignment 6, different compare at n=1 + [InlineData("aaaaaaaaaaaaaa", 0, "aaaaaaaaaaaaaa", 0, 100, StringComparison.Ordinal, 0)] // Equal long alignment 4, equal compare + [InlineData("aaaaaaaaaaaaaa", 0, "xaaaaaaaaaaaaa", 0, 100, StringComparison.Ordinal, -1)] // Equal long alignment 4, different compare at n=1 + [InlineData("aaaaaaaaaaaaaa", 0, "axaaaaaaaaaaaa", 0, 100, StringComparison.Ordinal, -1)] // Equal long alignment 4, different compare at n=2 + [InlineData("--aaaaaaaaaaaa", 2, "++aaaaaaaaaaaa", 2, 100, StringComparison.Ordinal, 0)] // Equal long alignment 0, equal compare + [InlineData("aaaaaaaaaaaaaa", 2, "aaxaaaaaaaaaaa", 2, 100, StringComparison.Ordinal, -1)] // Equal long alignment 0, different compare at n=1 + [InlineData("aaaaaaaaaaaaaa", 2, "aaaxaaaaaaaaaa", 2, 100, StringComparison.Ordinal, -1)] // Equal long alignment 0, different compare at n=2 + [InlineData("aaaaaaaaaaaaaa", 2, "aaaaxaaaaaaaaa", 2, 100, StringComparison.Ordinal, -1)] // Equal long alignment 0, different compare at n=3 + [InlineData("aaaaaaaaaaaaaa", 2, "aaaaaxaaaaaaaa", 2, 100, StringComparison.Ordinal, -1)] // Equal long alignment 0, different compare at n=4 + [InlineData("aaaaaaaaaaaaaa", 2, "aaaaaaxaaaaaaa", 2, 100, StringComparison.Ordinal, -1)] // Equal long alignment 0, different compare at n=5 + [InlineData("aaaaaaaaaaaaaa", 0, "+aaaaaaaaaaaaa", 1, 13, StringComparison.Ordinal, 0)] // Different int alignment, equal compare + [InlineData("aaaaaaaaaaaaaa", 0, "aaaaaaaaaaaaax", 1, 100, StringComparison.Ordinal, -1)] // Different int alignment + [InlineData("aaaaaaaaaaaaaa", 1, "aaaxaaaaaaaaaa", 3, 100, StringComparison.Ordinal, -1)] // Different long alignment, abs of 4, one of them is 2, different at n=1 + [InlineData("-aaaaaaaaaaaaa", 1, "++++aaaaaaaaaa", 4, 10, StringComparison.Ordinal, 0)] // Different long alignment, equal compare + [InlineData("aaaaaaaaaaaaaa", 1, "aaaaaaaaaaaaax", 4, 100, StringComparison.Ordinal, -1)] // Different long alignment + [InlineData("\0", 0, "", 0, 1, StringComparison.Ordinal, 1)] // Same memory layout, except for m_stringLength (m_firstChars are both 0) + [InlineData("\0\0", 0, "", 0, 2, StringComparison.Ordinal, 1)] // Same as above, except m_stringLength for one is 2 + [InlineData("", 0, "\0b", 0, 2, StringComparison.Ordinal, -1)] // strA's second char != strB's second char codepath + [InlineData("", 0, "b", 0, 1, StringComparison.Ordinal, -1)] // Should hit strA.m_firstChar != strB.m_firstChar codepath + [InlineData("abcxxxxxxxxxxxxxxxxxxxxxx", 0, "abdxxxxxxxxxxxxxxx", 0, int.MaxValue, StringComparison.Ordinal, -1)] // 64-bit: first long compare is different + [InlineData("abcdefgxxxxxxxxxxxxxxxxxx", 0, "abcdefhxxxxxxxxxxx", 0, int.MaxValue, StringComparison.Ordinal, -1)] // 64-bit: second long compare is different + [InlineData("abcdefghijkxxxxxxxxxxxxxx", 0, "abcdefghijlxxxxxxx", 0, int.MaxValue, StringComparison.Ordinal, -1)] // 64-bit: third long compare is different + [InlineData("abcdexxxxxxxxxxxxxxxxxxxx", 0, "abcdfxxxxxxxxxxxxx", 0, int.MaxValue, StringComparison.Ordinal, -1)] // 32-bit: second int compare is different + [InlineData("abcdefghixxxxxxxxxxxxxxxx", 0, "abcdefghjxxxxxxxxx", 0, int.MaxValue, StringComparison.Ordinal, -1)] // 32-bit: fourth int compare is different + // OrdinalIgnoreCase + [InlineData("HELLO", 0, "hello", 0, 5, StringComparison.OrdinalIgnoreCase, 0)] + [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.OrdinalIgnoreCase, 0)] + [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.OrdinalIgnoreCase, 0)] + [InlineData("Hello", 2, "Yellow", 2, 3, StringComparison.OrdinalIgnoreCase, 0)] + [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.OrdinalIgnoreCase, -1)] + [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.OrdinalIgnoreCase, 0)] + [InlineData("Hello", 2, "Goodbye", 2, 3, StringComparison.OrdinalIgnoreCase, -1)] + [InlineData("A", 0, "x", 0, 1, StringComparison.OrdinalIgnoreCase, -1)] + [InlineData("a", 0, "X", 0, 1, StringComparison.OrdinalIgnoreCase, -1)] + [InlineData("[", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("[", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("\\", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("\\", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("]", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("]", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("^", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("^", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("_", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("_", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("`", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("`", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)] + public static void Compare(string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType, int expected) + { + ReadOnlySpan<char> span = length <= (strA.Length - indexA) ? strA.AsSpan(indexA, length) : strA.AsSpan(indexA); + ReadOnlySpan<char> value = length <= (strB.Length - indexB) ? strB.AsSpan(indexB, length) : strB.AsSpan(indexB); + //Assert.True(expected == Math.Sign(span.CompareTo(value, comparisonType)), span.ToString() + "|" + value.ToString() + "|" + length); + Assert.Equal(expected, Math.Sign(span.CompareTo(value, comparisonType))); + } + } +} diff --git a/src/System.Memory/tests/ReadOnlySpan/Contains.cs b/src/System.Memory/tests/ReadOnlySpan/Contains.cs new file mode 100644 index 0000000000..a38f904966 --- /dev/null +++ b/src/System.Memory/tests/ReadOnlySpan/Contains.cs @@ -0,0 +1,181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void ZeroLengthContains_StringComparison() + { + var a = new char[3]; + + var span = new ReadOnlySpan<char>(a); + var slice = new ReadOnlySpan<char>(a, 2, 0); + Assert.True(span.Contains(slice, StringComparison.Ordinal)); + + Assert.True(span.Contains(slice, StringComparison.CurrentCulture)); + Assert.True(span.Contains(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span.Contains(slice, StringComparison.InvariantCulture)); + Assert.True(span.Contains(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span.Contains(slice, StringComparison.OrdinalIgnoreCase)); + + span = ReadOnlySpan<char>.Empty; + Assert.True(span.Contains(slice, StringComparison.Ordinal)); + + Assert.True(span.Contains(slice, StringComparison.CurrentCulture)); + Assert.True(span.Contains(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span.Contains(slice, StringComparison.InvariantCulture)); + Assert.True(span.Contains(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span.Contains(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void SameSpanContains_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a); + Assert.True(span.Contains(span, StringComparison.Ordinal)); + + Assert.True(span.Contains(span, StringComparison.CurrentCulture)); + Assert.True(span.Contains(span, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span.Contains(span, StringComparison.InvariantCulture)); + Assert.True(span.Contains(span, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span.Contains(span, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void LengthMismatchContains_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a, 0, 2); + var slice = new ReadOnlySpan<char>(a, 0, 3); + Assert.False(span.Contains(slice, StringComparison.Ordinal)); + + Assert.False(span.Contains(slice, StringComparison.CurrentCulture)); + Assert.False(span.Contains(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.False(span.Contains(slice, StringComparison.InvariantCulture)); + Assert.False(span.Contains(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.False(span.Contains(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void ContainsMatch_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a, 0, 3); + var slice = new ReadOnlySpan<char>(a, 0, 2); + Assert.True(span.Contains(slice, StringComparison.Ordinal)); + + Assert.True(span.Contains(slice, StringComparison.CurrentCulture)); + Assert.True(span.Contains(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span.Contains(slice, StringComparison.InvariantCulture)); + Assert.True(span.Contains(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span.Contains(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void ContainsMatchDifferentSpans_StringComparison() + { + char[] a = { '4', '5', '6', '7' }; + char[] b = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a, 0, 3); + var slice = new ReadOnlySpan<char>(b, 0, 3); + Assert.True(span.Contains(slice, StringComparison.Ordinal)); + + Assert.True(span.Contains(slice, StringComparison.CurrentCulture)); + Assert.True(span.Contains(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span.Contains(slice, StringComparison.InvariantCulture)); + Assert.True(span.Contains(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span.Contains(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void ContainsNoMatch_StringComparison() + { + for (int length = 1; length < 150; length++) + { + for (int mismatchIndex = 0; mismatchIndex < length; mismatchIndex++) + { + var first = new char[length]; + var second = new char[length]; + for (int i = 0; i < length; i++) + { + first[i] = second[i] = (char)(i + 1); + } + + second[mismatchIndex] = (char)(second[mismatchIndex] + 1); + + var firstSpan = new ReadOnlySpan<char>(first); + var secondSpan = new ReadOnlySpan<char>(second); + Assert.False(firstSpan.Contains(secondSpan, StringComparison.Ordinal)); + + Assert.False(firstSpan.Contains(secondSpan, StringComparison.OrdinalIgnoreCase)); + + // Different behavior depending on OS + Assert.Equal( + firstSpan.ToString().StartsWith(secondSpan.ToString(), StringComparison.CurrentCulture), + firstSpan.Contains(secondSpan, StringComparison.CurrentCulture)); + Assert.Equal( + firstSpan.ToString().StartsWith(secondSpan.ToString(), StringComparison.CurrentCulture), + firstSpan.Contains(secondSpan, StringComparison.CurrentCulture)); + Assert.Equal( + firstSpan.ToString().StartsWith(secondSpan.ToString(), StringComparison.InvariantCulture), + firstSpan.Contains(secondSpan, StringComparison.InvariantCulture)); + Assert.Equal( + firstSpan.ToString().StartsWith(secondSpan.ToString(), StringComparison.InvariantCultureIgnoreCase), + firstSpan.Contains(secondSpan, StringComparison.InvariantCultureIgnoreCase)); + } + } + } + + [Fact] + public static void MakeSureNoContainsChecksGoOutOfRange_StringComparison() + { + for (int length = 0; length < 100; length++) + { + var first = new char[length + 2]; + first[0] = (char)99; + first[length + 1] = (char)99; + var second = new char[length + 2]; + second[0] = (char)100; + second[length + 1] = (char)100; + var span1 = new ReadOnlySpan<char>(first, 1, length); + var span2 = new ReadOnlySpan<char>(second, 1, length); + Assert.True(span1.Contains(span2, StringComparison.Ordinal)); + + Assert.True(span1.Contains(span2, StringComparison.CurrentCulture)); + Assert.True(span1.Contains(span2, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span1.Contains(span2, StringComparison.InvariantCulture)); + Assert.True(span1.Contains(span2, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span1.Contains(span2, StringComparison.OrdinalIgnoreCase)); + } + } + + [Fact] + public static void ContainsUnknownComparisonType_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a); + TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Contains(_span, StringComparison.CurrentCulture - 1)); + TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Contains(_span, StringComparison.OrdinalIgnoreCase + 1)); + TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Contains(_span, (StringComparison)6)); + } + + [Theory] + [InlineData("Hello", "ello", true)] + [InlineData("Hello", "ELL", false)] + [InlineData("Hello", "Larger Hello", false)] + [InlineData("Hello", "Goodbye", false)] + [InlineData("", "", true)] + [InlineData("", "hello", false)] + [InlineData("Hello", "", true)] + public static void Contains(string s, string value, bool expected) + { + Assert.Equal(expected, s.AsSpan().Contains(value.AsSpan(), StringComparison.Ordinal)); + } + } +} diff --git a/src/System.Memory/tests/ReadOnlySpan/Equals.cs b/src/System.Memory/tests/ReadOnlySpan/Equals.cs new file mode 100644 index 0000000000..b2462d5a00 --- /dev/null +++ b/src/System.Memory/tests/ReadOnlySpan/Equals.cs @@ -0,0 +1,262 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Threading; +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void ZeroLengthEquals_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a); + var slice = new ReadOnlySpan<char>(a, 2, 0); + Assert.False(span.Equals(slice, StringComparison.Ordinal)); + + Assert.False(span.Equals(slice, StringComparison.CurrentCulture)); + Assert.False(span.Equals(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.False(span.Equals(slice, StringComparison.InvariantCulture)); + Assert.False(span.Equals(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.False(span.Equals(slice, StringComparison.OrdinalIgnoreCase)); + + span = new ReadOnlySpan<char>(a, 1, 0); + Assert.True(span.Equals(slice, StringComparison.Ordinal)); + + Assert.True(span.Equals(slice, StringComparison.CurrentCulture)); + Assert.True(span.Equals(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span.Equals(slice, StringComparison.InvariantCulture)); + Assert.True(span.Equals(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span.Equals(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void SameSpanEquals_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a); + Assert.True(span.Equals(span, StringComparison.Ordinal)); + + Assert.True(span.Equals(span, StringComparison.CurrentCulture)); + Assert.True(span.Equals(span, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span.Equals(span, StringComparison.InvariantCulture)); + Assert.True(span.Equals(span, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span.Equals(span, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void LengthMismatchEquals_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a, 0, 2); + var slice = new ReadOnlySpan<char>(a, 0, 3); + Assert.False(span.Equals(slice, StringComparison.Ordinal)); + + Assert.False(span.Equals(slice, StringComparison.CurrentCulture)); + Assert.False(span.Equals(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.False(span.Equals(slice, StringComparison.InvariantCulture)); + Assert.False(span.Equals(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.False(span.Equals(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void EqualsOverlappingMatch_StringComparison() + { + char[] a = { '4', '5', '6', '5', '6', '5' }; + var span = new ReadOnlySpan<char>(a, 1, 3); + var slice = new ReadOnlySpan<char>(a, 3, 3); + Assert.True(span.Equals(slice, StringComparison.Ordinal)); + + Assert.True(span.Equals(slice, StringComparison.CurrentCulture)); + Assert.True(span.Equals(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span.Equals(slice, StringComparison.InvariantCulture)); + Assert.True(span.Equals(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span.Equals(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void EqualsMatchDifferentSpans_StringComparison() + { + char[] a = { '4', '5', '6', '7' }; + char[] b = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a, 0, 3); + var slice = new ReadOnlySpan<char>(b, 0, 3); + Assert.True(span.Equals(slice, StringComparison.Ordinal)); + + Assert.True(span.Equals(slice, StringComparison.CurrentCulture)); + Assert.True(span.Equals(slice, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span.Equals(slice, StringComparison.InvariantCulture)); + Assert.True(span.Equals(slice, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span.Equals(slice, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public static void EqualsNoMatch_StringComparison() + { + for (int length = 1; length < 150; length++) + { + for (int mismatchIndex = 0; mismatchIndex < length; mismatchIndex++) + { + var first = new char[length]; + var second = new char[length]; + for (int i = 0; i < length; i++) + { + first[i] = second[i] = (char)(i + 1); + } + + second[mismatchIndex] = (char)(second[mismatchIndex] + 1); + + var firstSpan = new ReadOnlySpan<char>(first); + var secondSpan = new ReadOnlySpan<char>(second); + Assert.False(firstSpan.Equals(secondSpan, StringComparison.Ordinal)); + + Assert.False(firstSpan.Equals(secondSpan, StringComparison.OrdinalIgnoreCase)); + + // Different behavior depending on OS + Assert.Equal( + firstSpan.ToString().Equals(secondSpan.ToString(), StringComparison.CurrentCulture), + firstSpan.Equals(secondSpan, StringComparison.CurrentCulture)); + Assert.Equal( + firstSpan.ToString().Equals(secondSpan.ToString(), StringComparison.CurrentCultureIgnoreCase), + firstSpan.Equals(secondSpan, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal( + firstSpan.ToString().Equals(secondSpan.ToString(), StringComparison.InvariantCulture), + firstSpan.Equals(secondSpan, StringComparison.InvariantCulture)); + Assert.Equal( + firstSpan.ToString().Equals(secondSpan.ToString(), StringComparison.InvariantCultureIgnoreCase), + firstSpan.Equals(secondSpan, StringComparison.InvariantCultureIgnoreCase)); + } + } + } + + [Fact] + public static void MakeSureNoEqualsChecksGoOutOfRange_StringComparison() + { + for (int length = 0; length < 100; length++) + { + var first = new char[length + 2]; + first[0] = (char)99; + first[length + 1] = (char)99; + var second = new char[length + 2]; + second[0] = (char)100; + second[length + 1] = (char)100; + var span1 = new ReadOnlySpan<char>(first, 1, length); + var span2 = new ReadOnlySpan<char>(second, 1, length); + Assert.True(span1.Equals(span2, StringComparison.Ordinal)); + + Assert.True(span1.Equals(span2, StringComparison.CurrentCulture)); + Assert.True(span1.Equals(span2, StringComparison.CurrentCultureIgnoreCase)); + Assert.True(span1.Equals(span2, StringComparison.InvariantCulture)); + Assert.True(span1.Equals(span2, StringComparison.InvariantCultureIgnoreCase)); + Assert.True(span1.Equals(span2, StringComparison.OrdinalIgnoreCase)); + } + } + + [Fact] + public static void EqualsUnknownComparisonType_StringComparison() + { + char[] a = { '4', '5', '6' }; + var span = new ReadOnlySpan<char>(a); + TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Equals(_span, StringComparison.CurrentCulture - 1)); + TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Equals(_span, StringComparison.OrdinalIgnoreCase + 1)); + TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Equals(_span, (StringComparison)6)); + } + + [Theory] + // CurrentCulture + [InlineData("Hello", "Hello", StringComparison.CurrentCulture, true)] + [InlineData("Hello", "hello", StringComparison.CurrentCulture, false)] + [InlineData("Hello", "Helloo", StringComparison.CurrentCulture, false)] + [InlineData("Hello", "Hell", StringComparison.CurrentCulture, false)] + [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.CurrentCulture, true)] + [InlineData("Hello", "", StringComparison.CurrentCulture, false)] + [InlineData("", "Hello", StringComparison.CurrentCulture, false)] + [InlineData("", "", StringComparison.CurrentCulture, true)] + // CurrentCultureIgnoreCase + [InlineData("Hello", "Hello", StringComparison.CurrentCultureIgnoreCase, true)] + [InlineData("Hello", "hello", StringComparison.CurrentCultureIgnoreCase, true)] + [InlineData("Hello", "helloo", StringComparison.CurrentCultureIgnoreCase, false)] + [InlineData("Hello", "hell", StringComparison.CurrentCultureIgnoreCase, false)] + [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.CurrentCulture, true)] + [InlineData("Hello", "", StringComparison.CurrentCultureIgnoreCase, false)] + [InlineData("", "Hello", StringComparison.CurrentCultureIgnoreCase, false)] + [InlineData("", "", StringComparison.CurrentCultureIgnoreCase, true)] + // InvariantCulture + [InlineData("Hello", "Hello", StringComparison.InvariantCulture, true)] + [InlineData("Hello", "hello", StringComparison.InvariantCulture, false)] + [InlineData("Hello", "Helloo", StringComparison.InvariantCulture, false)] + [InlineData("Hello", "Hell", StringComparison.InvariantCulture, false)] + [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.CurrentCulture, true)] + [InlineData("Hello", "", StringComparison.InvariantCulture, false)] + [InlineData("", "Hello", StringComparison.InvariantCulture, false)] + [InlineData("", "", StringComparison.InvariantCulture, true)] + // InvariantCultureIgnoreCase + [InlineData("Hello", "Hello", StringComparison.InvariantCultureIgnoreCase, true)] + [InlineData("Hello", "hello", StringComparison.InvariantCultureIgnoreCase, true)] + [InlineData("Hello", "Helloo", StringComparison.InvariantCultureIgnoreCase, false)] + [InlineData("Hello", "Hell", StringComparison.InvariantCultureIgnoreCase, false)] + [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.CurrentCulture, true)] + [InlineData("Hello", "", StringComparison.InvariantCultureIgnoreCase, false)] + [InlineData("", "Hello", StringComparison.InvariantCultureIgnoreCase, false)] + [InlineData("", "", StringComparison.InvariantCultureIgnoreCase, true)] + // Ordinal + [InlineData("Hello", "Hello", StringComparison.Ordinal, true)] + [InlineData("Hello", "hello", StringComparison.Ordinal, false)] + [InlineData("Hello", "Helloo", StringComparison.Ordinal, false)] + [InlineData("Hello", "Hell", StringComparison.Ordinal, false)] + [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.Ordinal, false)] + [InlineData("Hello", "", StringComparison.Ordinal, false)] + [InlineData("", "Hello", StringComparison.Ordinal, false)] + [InlineData("", "", StringComparison.Ordinal, true)] + // OridinalIgnoreCase + [InlineData("Hello", "Hello", StringComparison.OrdinalIgnoreCase, true)] + [InlineData("HELLO", "hello", StringComparison.OrdinalIgnoreCase, true)] + [InlineData("Hello", "Helloo", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("Hello", "Hell", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.OrdinalIgnoreCase, false)] + [InlineData("\u1234\u5678", "\u1234\u5678", StringComparison.OrdinalIgnoreCase, true)] + [InlineData("\u1234\u5678", "\u1234\u5679", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("\u1234\u5678", "\u1235\u5678", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("\u1234\u5678", "\u1234", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("\u1234\u5678", "\u1234\u56789\u1234", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("Hello", "", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("", "Hello", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("", "", StringComparison.OrdinalIgnoreCase, true)] + public static void Equals(string s1, string s2, StringComparison comparisonType, bool expected) + { + Assert.Equal(expected, s1.AsSpan().Equals(s2.AsSpan(), comparisonType)); + } + + public static IEnumerable<object[]> Equals_EncyclopaediaData() + { + yield return new object[] { StringComparison.CurrentCulture, false }; + yield return new object[] { StringComparison.CurrentCultureIgnoreCase, false }; + yield return new object[] { StringComparison.Ordinal, false }; + yield return new object[] { StringComparison.OrdinalIgnoreCase, false }; + + // Windows and ICU disagree about how these strings compare in the default locale. + yield return new object[] { StringComparison.InvariantCulture, PlatformDetection.IsWindows }; + yield return new object[] { StringComparison.InvariantCultureIgnoreCase, PlatformDetection.IsWindows }; + } + + [Theory] + [MemberData(nameof(Equals_EncyclopaediaData))] + public static void Equals_Encyclopaedia_ReturnsExpected(StringComparison comparison, bool expected) + { + string source = "encyclop\u00e6dia"; + string target = "encyclopaedia"; + CultureInfo backupCulture = CultureInfo.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = new CultureInfo("se-SE"); + Assert.Equal(expected, source.AsSpan().Equals(target.AsSpan(), comparison)); + + Thread.CurrentThread.CurrentCulture = backupCulture; + } + } +} diff --git a/src/System.Memory/tests/ReadOnlySpan/IndexOf.charSpan.cs b/src/System.Memory/tests/ReadOnlySpan/IndexOf.charSpan.cs new file mode 100644 index 0000000000..b505deaa87 --- /dev/null +++ b/src/System.Memory/tests/ReadOnlySpan/IndexOf.charSpan.cs @@ -0,0 +1,409 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Globalization; +using System.Threading; +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Theory] + [InlineData("Hello", 'l', 0, 5, 2)] + [InlineData("Hello", 'x', 0, 5, -1)] + [InlineData("Hello", 'l', 1, 4, 2)] + [InlineData("Hello", 'l', 3, 2, 3)] + [InlineData("Hello", 'l', 4, 1, -1)] + [InlineData("Hello", 'x', 1, 4, -1)] + [InlineData("Hello", 'l', 3, 0, -1)] + [InlineData("Hello", 'l', 0, 2, -1)] + [InlineData("Hello", 'l', 0, 3, 2)] + [InlineData("Hello", 'l', 4, 1, -1)] + [InlineData("Hello", 'x', 1, 4, -1)] + [InlineData("Hello", 'o', 5, 0, -1)] + [InlineData("H" + SoftHyphen + "ello", 'e', 0, 3, 2)] + [InlineData("\ud800\udfff", '\ud800', 0, 1, 0)] // Surrogate characters + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'A', 0, 26, 0)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'B', 1, 25, 1)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'C', 2, 24, 2)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'D', 3, 23, 3)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'G', 2, 24, 6)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'K', 2, 24, 10)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'O', 2, 24, 14)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'P', 2, 24, 15)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'Q', 2, 24, 16)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'R', 2, 24, 17)] + [InlineData("________\u8080\u8080\u8080________", '\u0080', 0, 19, -1)] + [InlineData("________\u8000\u8000\u8000________", '\u0080', 0, 19, -1)] + [InlineData("__\u8080\u8000\u0080______________", '\u0080', 0, 19, 4)] + [InlineData("__\u8080\u8000__\u0080____________", '\u0080', 0, 19, 6)] + [InlineData("__________________________________", '\ufffd', 0, 34, -1)] + [InlineData("____________________________\ufffd", '\ufffd', 0, 29, 28)] + [InlineData("ABCDEFGHIJKLM", 'M', 0, 13, 12)] + [InlineData("ABCDEFGHIJKLMN", 'N', 0, 14, 13)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", '@', 0, 26, -1)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXY", '@', 0, 25, -1)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ#", '@', 0, 27, -1)] + [InlineData("_____________\u807f", '\u007f', 0, 14, -1)] + [InlineData("_____________\u807f__", '\u007f', 0, 16, -1)] + [InlineData("_____________\u807f\u007f_", '\u007f', 0, 16, 14)] + [InlineData("__\u807f_______________", '\u007f', 0, 18, -1)] + [InlineData("__\u807f___\u007f___________", '\u007f', 0, 18, 6)] + [InlineData("ABCDEFGHIJKLMN", 'N', 2, 11, -1)] + [InlineData("!@#$%^&", '%', 0, 7, 4)] + [InlineData("!@#$", '!', 0, 4, 0)] + [InlineData("!@#$", '@', 0, 4, 1)] + [InlineData("!@#$", '#', 0, 4, 2)] + [InlineData("!@#$", '$', 0, 4, 3)] + [InlineData("!@#$%^&*", '%', 0, 8, 4)] + public static void IndexOf_SingleLetter(string s, char target, int startIndex, int count, int expected) + { + bool safeForCurrentCulture = + IsSafeForCurrentCultureComparisons(s) + && IsSafeForCurrentCultureComparisons(target.ToString()); + + ReadOnlySpan<char> span = s.AsSpan(); + var charArray = new char[1]; + charArray[0] = target; + ReadOnlySpan<char> targetSpan = charArray; + + int expectedFromSpan = expected == -1 ? expected : expected - startIndex; + + if (count + startIndex == s.Length) + { + if (startIndex == 0) + { + Assert.Equal(expectedFromSpan, span.IndexOf(targetSpan, StringComparison.Ordinal)); + Assert.Equal(expectedFromSpan, span.IndexOf(targetSpan, StringComparison.OrdinalIgnoreCase)); + + // To be safe we only want to run CurrentCulture comparisons if + // we know the results will not vary depending on location + if (safeForCurrentCulture) + { + Assert.Equal(expectedFromSpan, span.IndexOf(targetSpan, StringComparison.CurrentCulture)); + } + } + + Assert.Equal(expectedFromSpan, span.Slice(startIndex).IndexOf(targetSpan, StringComparison.Ordinal)); + Assert.Equal(expectedFromSpan, span.Slice(startIndex).IndexOf(targetSpan, StringComparison.OrdinalIgnoreCase)); + + if (safeForCurrentCulture) + { + Assert.Equal(expectedFromSpan, span.Slice(startIndex).IndexOf(targetSpan, StringComparison.CurrentCulture)); + } + } + Assert.Equal(expectedFromSpan, span.Slice(startIndex, count).IndexOf(targetSpan, StringComparison.Ordinal)); + Assert.Equal(expectedFromSpan, span.Slice(startIndex, count).IndexOf(targetSpan, StringComparison.OrdinalIgnoreCase)); + + if (safeForCurrentCulture) + { + Assert.Equal(expectedFromSpan, span.Slice(startIndex, count).IndexOf(targetSpan, StringComparison.CurrentCulture)); + } + } + + private static bool IsSafeForCurrentCultureComparisons(string str) + { + for (int i = 0; i < str.Length; i++) + { + char c = str[i]; + // We only want ASCII chars that you can see + // No controls, no delete, nothing >= 0x80 + if (c < 0x20 || c == 0x7f || c >= 0x80) + { + return false; + } + } + return true; + } + + + // NOTE: This is by design. Unix ignores the null characters (i.e. null characters have no weights for the string comparison). + // For desired behavior, use ordinal comparison instead of linguistic comparison. + // This is a known difference between Windows and Unix (https://github.com/dotnet/coreclr/issues/2051). + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] + [InlineData("He\0lo", "He\0lo", 0)] + [InlineData("He\0lo", "He\0", 0)] + [InlineData("He\0lo", "\0", 2)] + [InlineData("He\0lo", "\0lo", 2)] + [InlineData("He\0lo", "lo", 3)] + [InlineData("Hello", "lo\0", -1)] + [InlineData("Hello", "\0lo", -1)] + [InlineData("Hello", "l\0o", -1)] + public static void IndexOf_NullInStrings(string s, string value, int expected) + { + Assert.Equal(expected, s.AsSpan().IndexOf(value.AsSpan(), StringComparison.Ordinal)); + } + + [Theory] + [MemberData(nameof(AllSubstringsAndComparisons), new object[] { "abcde" })] + public static void IndexOf_AllSubstrings(string s, string value, int startIndex, StringComparison comparison) + { + bool ignoringCase = comparison == StringComparison.OrdinalIgnoreCase || comparison == StringComparison.CurrentCultureIgnoreCase; + + // First find the substring. We should be able to with all comparison types. + Assert.Equal(startIndex, s.AsSpan().IndexOf(value.AsSpan(), comparison)); // in the whole string + Assert.Equal(0, s.AsSpan(startIndex).IndexOf(value.AsSpan(), comparison)); // starting at substring + if (startIndex > 0) + { + Assert.Equal(1, s.AsSpan(startIndex - 1).IndexOf(value.AsSpan(), comparison)); // starting just before substring + } + Assert.Equal(-1, s.AsSpan(startIndex + 1).IndexOf(value.AsSpan(), comparison)); // starting just after start of substring + + // Shouldn't be able to find the substring if the count is less than substring's length + Assert.Equal(-1, s.AsSpan(0, value.Length - 1).IndexOf(value.AsSpan(), comparison)); + + // Now double the source. Make sure we find the first copy of the substring. + int halfLen = s.Length; + s += s; + Assert.Equal(startIndex, s.AsSpan().IndexOf(value.AsSpan(), comparison)); + + // Now change the case of a letter. + s = s.ToUpperInvariant(); + Assert.Equal(ignoringCase ? startIndex : -1, s.AsSpan().IndexOf(value.AsSpan(), comparison)); + } + + public static IEnumerable<object[]> AllSubstringsAndComparisons(string source) + { + var comparisons = new StringComparison[] + { + StringComparison.CurrentCulture, + StringComparison.CurrentCultureIgnoreCase, + StringComparison.Ordinal, + StringComparison.OrdinalIgnoreCase + }; + + foreach (StringComparison comparison in comparisons) + { + for (int i = 0; i <= source.Length; i++) + { + for (int subLen = source.Length - i; subLen > 0; subLen--) + { + yield return new object[] { source, source.Substring(i, subLen), i, comparison }; + } + } + } + } + + [Fact] + public static void IndexOf_TurkishI_TurkishCulture() + { + CultureInfo backupCulture = CultureInfo.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR"); + + string str = "Turkish I \u0131s TROUBL\u0130NG!"; + string valueString = "\u0130"; + ReadOnlySpan<char> s = str.AsSpan(); + ReadOnlySpan<char> value = valueString.AsSpan(); + Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(4, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(19, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(19, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + valueString = "\u0131"; + value = valueString.AsSpan(); + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(10, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(10, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + Thread.CurrentThread.CurrentCulture = backupCulture; + } + + [Fact] + public static void IndexOf_TurkishI_InvariantCulture() + { + CultureInfo backupCulture = CultureInfo.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; + + string str = "Turkish I \u0131s TROUBL\u0130NG!"; + string valueString = "\u0130"; + ReadOnlySpan<char> s = str.AsSpan(); + ReadOnlySpan<char> value = valueString.AsSpan(); + + Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + valueString = "\u0131"; + value = valueString.AsSpan(); + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + Thread.CurrentThread.CurrentCulture = backupCulture; + } + + [Fact] + public static void IndexOf_TurkishI_EnglishUSCulture() + { + CultureInfo backupCulture = CultureInfo.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); + + string str = "Turkish I \u0131s TROUBL\u0130NG!"; + string valueString = "\u0130"; + ReadOnlySpan<char> s = str.AsSpan(); + ReadOnlySpan<char> value = valueString.AsSpan(); + + Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + valueString = "\u0131"; + value = valueString.AsSpan(); + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + Thread.CurrentThread.CurrentCulture = backupCulture; + } + + [Fact] + public static void IndexOf_HungarianDoubleCompression_HungarianCulture() + { + string str = "dzsdzs"; + string valueString = "ddzs"; + + ReadOnlySpan<char> s = str.AsSpan(); + ReadOnlySpan<char> value = valueString.AsSpan(); + + CultureInfo backupCulture = CultureInfo.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = new CultureInfo("hu-HU"); + /* + There are differences between Windows and ICU regarding contractions. + Windows has equal contraction collation weights, including case (target="Ddzs" same behavior as "ddzs"). + ICU has different contraction collation weights, depending on locale collation rules. + If CurrentCultureIgnoreCase is specified, ICU will use 'secondary' collation rules + which ignore the contraction collation weights (defined as 'tertiary' rules) + */ + Assert.Equal(PlatformDetection.IsWindows ? 0 : -1, s.IndexOf(value, StringComparison.CurrentCulture)); + + Assert.Equal(0, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(-1, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(-1, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + Thread.CurrentThread.CurrentCulture = backupCulture; + } + + [Fact] + public static void IndexOf_HungarianDoubleCompression_InvariantCulture() + { + string str = "dzsdzs"; + string valueString = "ddzs"; + + ReadOnlySpan<char> s = str.AsSpan(); + ReadOnlySpan<char> value = valueString.AsSpan(); + + CultureInfo backupCulture = CultureInfo.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; + Assert.Equal(-1, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(-1, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + Thread.CurrentThread.CurrentCulture = backupCulture; + } + + [Fact] + public static void IndexOf_EquivalentDiacritics_EnglishUSCulture() + { + string str = "Exhibit a\u0300\u00C0"; + string valueString = "\u00C0"; + + ReadOnlySpan<char> s = str.AsSpan(); + ReadOnlySpan<char> value = valueString.AsSpan(); + + CultureInfo backupCulture = CultureInfo.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(10, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(10, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + valueString = "a\u0300"; // this diacritic combines with preceding character + value = valueString.AsSpan(); + Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(8, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(8, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + Thread.CurrentThread.CurrentCulture = backupCulture; + } + + [Fact] + public static void IndexOf_EquivalentDiacritics_InvariantCulture() + { + string str = "Exhibit a\u0300\u00C0"; + string valueString = "\u00C0"; + + ReadOnlySpan<char> s = str.AsSpan(); + ReadOnlySpan<char> value = valueString.AsSpan(); + + CultureInfo backupCulture = CultureInfo.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + valueString = "a\u0300"; // this diacritic combines with preceding character + value = valueString.AsSpan(); + Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + Thread.CurrentThread.CurrentCulture = backupCulture; + } + + [Fact] + public static void IndexOf_CyrillicE_EnglishUSCulture() + { + string str = "Foo\u0400Bar"; + string valueString = "\u0400"; + + ReadOnlySpan<char> s = str.AsSpan(); + ReadOnlySpan<char> value = valueString.AsSpan(); + + CultureInfo backupCulture = CultureInfo.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); + Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(3, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(3, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + valueString = "bar"; + value = valueString.AsSpan(); + Assert.Equal(-1, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(4, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(-1, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(4, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + Thread.CurrentThread.CurrentCulture = backupCulture; + } + + [Fact] + public static void IndexOf_CyrillicE_InvariantCulture() + { + string str = "Foo\u0400Bar"; + string valueString = "\u0400"; + + ReadOnlySpan<char> s = str.AsSpan(); + ReadOnlySpan<char> value = valueString.AsSpan(); + + CultureInfo backupCulture = CultureInfo.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; + Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + valueString = "bar"; + value = valueString.AsSpan(); + Assert.Equal(-1, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(4, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + Thread.CurrentThread.CurrentCulture = backupCulture; + } + } +} diff --git a/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.char.cs b/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.char.cs index eedc14ac0c..9841b76fa1 100644 --- a/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.char.cs +++ b/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.char.cs @@ -15,6 +15,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '5', '1', '7' }); int index = span.IndexOf(value); Assert.Equal(0, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -24,6 +25,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '2', '3' }); int index = span.IndexOf(value); Assert.Equal(1, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -33,6 +35,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '7', '7', '8' }); int index = span.IndexOf(value); Assert.Equal(10, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -42,6 +45,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '7', '7', '8', 'X' }); int index = span.IndexOf(value); Assert.Equal(-1, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -51,6 +55,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { 'X', '7', '8', '9' }); int index = span.IndexOf(value); Assert.Equal(-1, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -60,6 +65,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '3', '4', '5' }); int index = span.IndexOf(value); Assert.Equal(3, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -69,6 +75,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '3', '4', '5' }); int index = span.IndexOf(value); Assert.Equal(-1, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -79,6 +86,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(Array.Empty<char>()); int index = span.IndexOf(value); Assert.Equal(0, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -88,6 +96,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '1', '2', '3' }); int index = span.IndexOf(value); Assert.Equal(-1, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -98,6 +107,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '2' }); int index = span.IndexOf(value); Assert.Equal(2, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -108,6 +118,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '5' }); int index = span.IndexOf(value); Assert.Equal(5, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } [Fact] @@ -118,6 +129,7 @@ namespace System.SpanTests ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '5' }); int index = span.IndexOf(value); Assert.Equal(-1, index); + Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal)); } } } diff --git a/src/System.Memory/tests/System.Memory.Tests.csproj b/src/System.Memory/tests/System.Memory.Tests.csproj index 7035a2af6e..c777b5bd86 100644 --- a/src/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/System.Memory/tests/System.Memory.Tests.csproj @@ -82,6 +82,8 @@ <Compile Include="ReadOnlySpan\AsBytes.cs" /> <Compile Include="ReadOnlySpan\AsReadOnlySpan.cs" /> <Compile Include="ReadOnlySpan\BinarySearch.cs" /> + <Compile Include="ReadOnlySpan\CompareTo.cs" /> + <Compile Include="ReadOnlySpan\Contains.cs" /> <Compile Include="ReadOnlySpan\CopyTo.cs" /> <Compile Include="ReadOnlySpan\CtorArray.cs" /> <Compile Include="ReadOnlySpan\CtorArrayIntInt.cs" /> @@ -91,12 +93,14 @@ <Compile Include="ReadOnlySpan\EndsWith.char.cs" /> <Compile Include="ReadOnlySpan\EndsWith.T.cs" /> <Compile Include="ReadOnlySpan\Equality.cs" /> + <Compile Include="ReadOnlySpan\Equals.cs" /> <Compile Include="ReadOnlySpan\GetEnumerator.cs" /> <Compile Include="ReadOnlySpan\GetHashCode.cs" /> <Compile Include="ReadOnlySpan\ImplicitConversion.cs" /> <Compile Include="ReadOnlySpan\IndexOf.T.cs" /> <Compile Include="ReadOnlySpan\IndexOf.byte.cs" /> <Compile Include="ReadOnlySpan\IndexOf.char.cs" /> + <Compile Include="ReadOnlySpan\IndexOf.charSpan.cs" /> <Compile Include="ReadOnlySpan\IndexOfAny.T.cs" /> <Compile Include="ReadOnlySpan\IndexOfAny.byte.cs" /> <Compile Include="ReadOnlySpan\IndexOfSequence.T.cs" /> |