From: Anirudh Agnihotry Date: Wed, 26 Jun 2019 18:54:06 +0000 (-0700) Subject: Tests for nullable reference types in Span apis (dotnet/corefx#38625) X-Git-Tag: submit/tizen/20210909.063632~11031^2~1119 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=59c5617312875257450be177a54d435d4e229c75;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Tests for nullable reference types in Span apis (dotnet/corefx#38625) * Some more null index checks * adding test for ReadonlySpan overload and refactoring all overloads into a single test * FIxing the tests with ben change and rebasing Commit migrated from https://github.com/dotnet/corefx/commit/96a713d3434af8d443f87874e915f5d36c47a564 --- diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/Contains.T.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/Contains.T.cs index e48ca72..0a2b924 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/Contains.T.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/Contains.T.cs @@ -186,5 +186,13 @@ namespace System.SpanTests Assert.True(found); } } + + [Theory] + [MemberData(nameof(TestHelpers.ContainsNullData), MemberType = typeof(TestHelpers))] + public static void ContainsNull_String(string[] spanInput, bool expected) + { + ReadOnlySpan theStrings = spanInput; + Assert.Equal(expected, theStrings.Contains(null)); + } } } diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/IndexOfAny.T.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/IndexOfAny.T.cs index 4451253..d32501c 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/IndexOfAny.T.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/IndexOfAny.T.cs @@ -963,5 +963,27 @@ namespace System.SpanTests Assert.Equal(-1, index); } } + + [Theory] + [MemberData(nameof(TestHelpers.IndexOfAnyNullSequenceData), MemberType = typeof(TestHelpers))] + public static void IndexOfAnyNullSequence_String(string[] spanInput, string[] searchInput, int expected) + { + ReadOnlySpan theStrings = spanInput; + Assert.Equal(expected, theStrings.IndexOfAny(searchInput)); + Assert.Equal(expected, theStrings.IndexOfAny((ReadOnlySpan)searchInput)); + + if (searchInput != null) + { + if (searchInput.Length >= 3) + { + Assert.Equal(expected, theStrings.IndexOfAny(searchInput[0], searchInput[1], searchInput[2])); + } + + if (searchInput.Length >= 2) + { + Assert.Equal(expected, theStrings.IndexOfAny(searchInput[0], searchInput[1])); + } + } + } } } diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/IndexOfSequence.T.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/IndexOfSequence.T.cs index 796a0f6..cb0fd3a 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/IndexOfSequence.T.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/IndexOfSequence.T.cs @@ -238,6 +238,7 @@ namespace System.SpanTests { ReadOnlySpan theStrings = spanInput; Assert.Equal(expected, theStrings.IndexOf(searchInput)); + Assert.Equal(expected, theStrings.IndexOf((ReadOnlySpan)searchInput)); } } } diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/LastIndexOfAny.T.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/LastIndexOfAny.T.cs index 32d5464..861382a 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/LastIndexOfAny.T.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/LastIndexOfAny.T.cs @@ -933,5 +933,27 @@ namespace System.SpanTests Assert.Equal(-1, index); } } + + [Theory] + [MemberData(nameof(TestHelpers.LastIndexOfAnyNullSequenceData), MemberType = typeof(TestHelpers))] + public static void LastIndexOfAnyNullSequence_String(string[] spanInput, string[] searchInput, int expected) + { + ReadOnlySpan theStrings = spanInput; + Assert.Equal(expected, theStrings.LastIndexOfAny(searchInput)); + Assert.Equal(expected, theStrings.LastIndexOfAny((ReadOnlySpan)searchInput)); + + if (searchInput != null) + { + if (searchInput.Length >= 3) + { + Assert.Equal(expected, theStrings.LastIndexOfAny(searchInput[0], searchInput[1], searchInput[2])); + } + + if (searchInput.Length >= 2) + { + Assert.Equal(expected, theStrings.LastIndexOfAny(searchInput[0], searchInput[1])); + } + } + } } } diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.T.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.T.cs index 4885b0b..667d2b7 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.T.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.T.cs @@ -248,6 +248,7 @@ namespace System.SpanTests { ReadOnlySpan theStrings = spanInput; Assert.Equal(expected, theStrings.LastIndexOf(searchInput)); + Assert.Equal(expected, theStrings.LastIndexOf((ReadOnlySpan)searchInput)); } } } diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/SequenceEqual.T.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/SequenceEqual.T.cs index 4557186..4d56723 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/SequenceEqual.T.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/SequenceEqual.T.cs @@ -130,5 +130,14 @@ namespace System.SpanTests Assert.True(b); } } + + [Theory] + [MemberData(nameof(TestHelpers.SequenceEqualsNullData), MemberType = typeof(TestHelpers))] + public static void SequenceEqualsNullData_String(string[] firstInput, string[] secondInput, bool expected) + { + ReadOnlySpan theStrings = firstInput; + Assert.Equal(expected, theStrings.SequenceEqual(secondInput)); + Assert.Equal(expected, theStrings.SequenceEqual((ReadOnlySpan)secondInput)); + } } } diff --git a/src/libraries/System.Memory/tests/Span/Contains.T.cs b/src/libraries/System.Memory/tests/Span/Contains.T.cs new file mode 100644 index 0000000..ba8f5f9 --- /dev/null +++ b/src/libraries/System.Memory/tests/Span/Contains.T.cs @@ -0,0 +1,198 @@ +// 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 +{ + // Adapted from IndexOf.T.cs + public static partial class SpanTests // .Contains + { + [Fact] + public static void ZeroLengthContains() + { + Span span = new Span(Array.Empty()); + + bool found = span.Contains(0); + Assert.False(found); + } + + [Fact] + public static void TestContains() + { + for (int length = 0; length < 32; length++) + { + int[] a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = 10 * (i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target = a[targetIndex]; + bool found = span.Contains(target); + Assert.True(found); + } + } + } + + [Fact] + public static void TestMultipleContains() + { + for (int length = 2; length < 32; length++) + { + int[] a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = 10 * (i + 1); + } + + a[length - 1] = 5555; + a[length - 2] = 5555; + + Span span = new Span(a); + bool found = span.Contains(5555); + Assert.True(found); + } + } + + [Fact] + public static void OnNoMatchForContainsMakeSureEveryElementIsCompared() + { + for (int length = 0; length < 100; length++) + { + TIntLog log = new TIntLog(); + + TInt[] a = new TInt[length]; + for (int i = 0; i < length; i++) + { + a[i] = new TInt(10 * (i + 1), log); + } + Span span = new Span(a); + bool found = span.Contains(new TInt(9999, log)); + Assert.False(found); + + // Since we asked for a non-existent value, make sure each element of the array was compared once. + // (Strictly speaking, it would not be illegal for IndexOf to compare an element more than once but + // that would be a non-optimal implementation and a red flag. So we'll stick with the stricter test.) + Assert.Equal(a.Length, log.Count); + foreach (TInt elem in a) + { + int numCompares = log.CountCompares(elem.Value, 9999); + Assert.True(numCompares == 1, $"Expected {numCompares} == 1 for element {elem.Value}."); + } + } + } + + [Fact] + public static void MakeSureNoChecksForContainsGoOutOfRange() + { + const int GuardValue = 77777; + const int GuardLength = 50; + + void checkForOutOfRangeAccess(int x, int y) + { + if (x == GuardValue || y == GuardValue) + throw new Exception("Detected out of range access in IndexOf()"); + } + + for (int length = 0; length < 100; length++) + { + TInt[] a = new TInt[GuardLength + length + GuardLength]; + for (int i = 0; i < a.Length; i++) + { + a[i] = new TInt(GuardValue, checkForOutOfRangeAccess); + } + + for (int i = 0; i < length; i++) + { + a[GuardLength + i] = new TInt(10 * (i + 1), checkForOutOfRangeAccess); + } + + Span span = new Span(a, GuardLength, length); + bool found = span.Contains(new TInt(9999, checkForOutOfRangeAccess)); + Assert.False(found); + } + } + + [Fact] + public static void ZeroLengthContains_String() + { + Span span = new Span(Array.Empty()); + bool found = span.Contains("a"); + Assert.False(found); + } + + [Fact] + public static void TestMatchContains_String() + { + for (int length = 0; length < 32; length++) + { + string[] a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (10 * (i + 1)).ToString(); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target = a[targetIndex]; + bool found = span.Contains(target); + Assert.True(found); + } + } + } + + [Fact] + public static void TestNoMatchContains_String() + { + var rnd = new Random(42); + for (int length = 0; length <= byte.MaxValue; length++) + { + string[] a = new string[length]; + string target = (rnd.Next(0, 256)).ToString(); + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == target ? (target + 1) : val; + } + Span span = new Span(a); + + bool found = span.Contains(target); + Assert.False(found); + } + } + + [Fact] + public static void TestMultipleMatchContains_String() + { + for (int length = 2; length < 32; length++) + { + string[] a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (10 * (i + 1)).ToString(); + } + + a[length - 1] = "5555"; + a[length - 2] = "5555"; + + Span span = new Span(a); + bool found = span.Contains("5555"); + Assert.True(found); + } + } + + [Theory] + [MemberData(nameof(TestHelpers.ContainsNullData), MemberType = typeof(TestHelpers))] + public static void ContainsNull_String(string[] spanInput, bool expected) + { + Span theStrings = spanInput; + Assert.Equal(expected, theStrings.Contains(null)); + } + } +} diff --git a/src/libraries/System.Memory/tests/Span/IndexOfAny.T.cs b/src/libraries/System.Memory/tests/Span/IndexOfAny.T.cs index cc42791..9e4e5f0 100644 --- a/src/libraries/System.Memory/tests/Span/IndexOfAny.T.cs +++ b/src/libraries/System.Memory/tests/Span/IndexOfAny.T.cs @@ -963,5 +963,27 @@ namespace System.SpanTests Assert.Equal(-1, index); } } + + [Theory] + [MemberData(nameof(TestHelpers.IndexOfAnyNullSequenceData), MemberType = typeof(TestHelpers))] + public static void IndexOfAnyNullSequence_String(string[] spanInput, string[] searchInput, int expected) + { + Span theStrings = spanInput; + Assert.Equal(expected, theStrings.IndexOfAny(searchInput)); + Assert.Equal(expected, theStrings.IndexOfAny((ReadOnlySpan)searchInput)); + + if (searchInput != null) + { + if (searchInput.Length >= 3) + { + Assert.Equal(expected, theStrings.IndexOfAny(searchInput[0], searchInput[1], searchInput[2])); + } + + if (searchInput.Length >= 2) + { + Assert.Equal(expected, theStrings.IndexOfAny(searchInput[0], searchInput[1])); + } + } + } } } diff --git a/src/libraries/System.Memory/tests/Span/IndexOfSequence.T.cs b/src/libraries/System.Memory/tests/Span/IndexOfSequence.T.cs index 697d5ce..5fdb3db 100644 --- a/src/libraries/System.Memory/tests/Span/IndexOfSequence.T.cs +++ b/src/libraries/System.Memory/tests/Span/IndexOfSequence.T.cs @@ -238,6 +238,7 @@ namespace System.SpanTests { Span theStrings = spanInput; Assert.Equal(expected, theStrings.IndexOf(searchInput)); + Assert.Equal(expected, theStrings.IndexOf((ReadOnlySpan)searchInput)); } } } diff --git a/src/libraries/System.Memory/tests/Span/LastIndexOfAny.T.cs b/src/libraries/System.Memory/tests/Span/LastIndexOfAny.T.cs index 5fb04db..590b805 100644 --- a/src/libraries/System.Memory/tests/Span/LastIndexOfAny.T.cs +++ b/src/libraries/System.Memory/tests/Span/LastIndexOfAny.T.cs @@ -930,5 +930,27 @@ namespace System.SpanTests Assert.Equal(-1, index); } } + + [Theory] + [MemberData(nameof(TestHelpers.LastIndexOfAnyNullSequenceData), MemberType = typeof(TestHelpers))] + public static void LastIndexOfAnyNullSequence_String(string[] spanInput, string[] searchInput, int expected) + { + Span theStrings = spanInput; + Assert.Equal(expected, theStrings.LastIndexOfAny(searchInput)); + Assert.Equal(expected, theStrings.LastIndexOfAny((ReadOnlySpan)searchInput)); + + if (searchInput != null) + { + if (searchInput.Length >= 3) + { + Assert.Equal(expected, theStrings.LastIndexOfAny(searchInput[0], searchInput[1], searchInput[2])); + } + + if (searchInput.Length >= 2) + { + Assert.Equal(expected, theStrings.LastIndexOfAny(searchInput[0], searchInput[1])); + } + } + } } } diff --git a/src/libraries/System.Memory/tests/Span/LastIndexOfSequence.T.cs b/src/libraries/System.Memory/tests/Span/LastIndexOfSequence.T.cs index 90134c4..d130e15 100644 --- a/src/libraries/System.Memory/tests/Span/LastIndexOfSequence.T.cs +++ b/src/libraries/System.Memory/tests/Span/LastIndexOfSequence.T.cs @@ -248,6 +248,7 @@ namespace System.SpanTests { Span theStrings = spanInput; Assert.Equal(expected, theStrings.LastIndexOf(searchInput)); + Assert.Equal(expected, theStrings.LastIndexOf((ReadOnlySpan)searchInput)); } } } diff --git a/src/libraries/System.Memory/tests/Span/SequenceEqual.T.cs b/src/libraries/System.Memory/tests/Span/SequenceEqual.T.cs index 9444cac..3df13fe 100644 --- a/src/libraries/System.Memory/tests/Span/SequenceEqual.T.cs +++ b/src/libraries/System.Memory/tests/Span/SequenceEqual.T.cs @@ -130,5 +130,14 @@ namespace System.SpanTests Assert.True(b); } } + + [Theory] + [MemberData(nameof(TestHelpers.SequenceEqualsNullData), MemberType = typeof(TestHelpers))] + public static void SequenceEqualsNullData_String(string[] firstInput, string[] secondInput, bool expected) + { + Span theStrings = firstInput; + Assert.Equal(expected, theStrings.SequenceEqual(secondInput)); + Assert.Equal(expected, theStrings.SequenceEqual((ReadOnlySpan)secondInput)); + } } } diff --git a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj index e2bed79..11eb32d 100644 --- a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj @@ -51,6 +51,7 @@ + diff --git a/src/libraries/System.Memory/tests/TestHelpers.cs b/src/libraries/System.Memory/tests/TestHelpers.cs index f68a235..7e3485c 100644 --- a/src/libraries/System.Memory/tests/TestHelpers.cs +++ b/src/libraries/System.Memory/tests/TestHelpers.cs @@ -411,6 +411,34 @@ namespace System public static ReadOnlyMemory DangerousCreateReadOnlyMemory(object obj, int offset, int length) => DangerousCreateMemory(obj, offset, length); + public static TheoryData ContainsNullData => new TheoryData() + { + { new string[] { "1", null, "2" }, true}, + { new string[] { "1", "3", "2" }, false}, + { null, false}, + { new string[] { "1", null, null }, true}, + { new string[] { null, null, null }, true}, + }; + + public static TheoryData SequenceEqualsNullData => new TheoryData() + { + { new string[] { "1", null, "2" }, new string[] { "1", null, "2" } , true}, + { new string[] { "1", null, "2" }, new string[] { "1", "3", "2" } , false}, + { new string[] { "1", null, "2" }, new string[] { null, "3", "2" } , false}, + { new string[] { "1", null, "2" }, new string[] { null } , false}, + { new string[] { "1", null, "2" }, null , false}, + + { new string[] { null, "2", "1" }, new string[] { null, "2" } , false}, + + { null, new string[] { null }, false}, + { null, null , true}, + { null, new string[] { "1", "3", "2" } , false}, + { null, new string[] { "1", null, "2" } , false}, + + { new string[] { "1", null, null }, new string[] { "1", null, null }, true}, + { new string[] { null, null, null }, new string[] { null, null, null }, true}, + }; + public static TheoryData IndexOfNullData => new TheoryData() { { new string[] { "1", null, "2" }, 1}, @@ -436,6 +464,33 @@ namespace System { new string[] { null, null, null }, new string[] { null, null }, 0}, }; + public static TheoryData IndexOfAnyNullSequenceData => new TheoryData() + { + { new string[] { "1", null, "2" }, new string[] { "1", null, "2" }, 0}, + { new string[] { "1", null, "2" }, new string[] { null, null }, 1}, + + { new string[] { "1", null, "2" }, new string[] { "3", null }, 1}, + { new string[] { "1", null, "2" }, new string[] { "1", "2" }, 0}, + { new string[] { "1", null, "2" }, new string[] { "3", "4" }, -1}, + + { new string[] { null, null, "2" }, new string[] { "3", null }, 0}, + { new string[] { null, null, "2" }, new string[] { null, "1" }, 0}, + { new string[] { null, null, "2" }, new string[] { null, "1" }, 0}, + + { new string[] { "1", "3", "2" }, new string[] { "1", null, "2" }, 0}, + { new string[] { "1", "3", "2" }, new string[] { null, null }, -1}, + + { new string[] { "1", "3", "2" }, new string[] { null, "1" }, 0}, + + { null, new string[] { "1", null, "2" }, -1}, + + { new string[] { "1", null, null }, new string[] { null, null, "2" }, 1}, + { new string[] { null, null, null }, new string[] { null, null }, 0}, + + { new string[] { "1", "3", "2" }, null, -1}, + { new string[] { "1", null, "2" }, null, -1}, + }; + public static TheoryData LastIndexOfNullData => new TheoryData() { { new string[] { "1", null, "2" }, 1}, @@ -462,5 +517,33 @@ namespace System { new string[] { "1", null, null }, new string[] { null, null, "2" }, -1}, { new string[] { null, null, null }, new string[] { null, null }, 1}, }; + + public static TheoryData LastIndexOfAnyNullSequenceData => new TheoryData() + { + { new string[] { "1", null, "2" }, new string[] { "1", null, "3" }, 1}, + { new string[] { "1", null, "2" }, new string[] { null, null }, 1}, + { new string[] { "1", null, "2" }, new string[] { "3", "4" }, -1}, + { new string[] { "1", null, "2" }, new string[] { "3", null }, 1}, + { new string[] { "1", null, "2" }, new string[] { "1", null }, 1}, + { new string[] { "1", null, "2" }, new string[] { null, null }, 1}, + { new string[] { "1", null, "2" }, new string[] { "1", "2" }, 2}, + { null, new string[] { "1", null, "2" }, -1}, + + { new string[] { null, null, "2" }, new string[] { "3", null }, 1}, + { new string[] { null, null, "2" }, new string[] { null, "1" }, 1}, + { new string[] { null, null, "2" }, new string[] { null, "1" }, 1}, + + { new string[] { "1", "3", "2" }, new string[] { null, "1" }, 0}, + { new string[] { "1", "3", "2" }, new string[] { "1", "2", null }, 2}, + { new string[] { "1", "3", "2" }, new string[] { null, null }, -1}, + + { null, new string[] { null, "1" }, -1}, + + { new string[] { "1", null, null }, new string[] { null, null, "2" }, 2}, + { new string[] { null, null, null }, new string[] { null, null }, 2}, + + { new string[] { "1", null, "2" }, null, -1}, + { new string[] { "1", "3", "2" }, null, -1}, + }; } }