Expose/test String.Create span-based method (dotnet/corefx#23872)
authorStephen Toub <stoub@microsoft.com>
Fri, 22 Sep 2017 02:35:33 +0000 (19:35 -0700)
committerGitHub <noreply@github.com>
Fri, 22 Sep 2017 02:35:33 +0000 (19:35 -0700)
* Expose/test String.Create span-based method

* Address PR feedback

Commit migrated from https://github.com/dotnet/corefx/commit/8cc35f2e402b77fa666e1362696df735a9529027

src/libraries/System.Runtime/ref/System.Runtime.cs
src/libraries/System.Runtime/tests/System/StringTests.netcoreapp.cs

index b5821e9..d24e923 100644 (file)
@@ -2159,6 +2159,7 @@ namespace System
         public bool Contains(string value, StringComparison comparisonType) { throw null; }
         public static System.String Copy(System.String str) { throw null; }
         public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) { }
+        public static string Create<TState>(int length, TState state, System.Buffers.SpanAction<char, TState> action) { throw null; }
         public bool EndsWith(char value) { throw null; }
         public bool EndsWith(string value) { throw null; }
         public bool EndsWith(System.String value, bool ignoreCase, System.Globalization.CultureInfo culture) { throw null; }
@@ -3860,6 +3861,11 @@ namespace System.Buffers
         protected internal abstract bool TryGetArray(out ArraySegment<T> arraySegment);
     }
 }
+namespace System.Buffers
+{
+    public delegate void SpanAction<T, in TArg>(Span<T> span, TArg arg);
+    public delegate void ReadOnlySpanAction<T, in TArg>(ReadOnlySpan<T> span, TArg arg);
+}
 namespace System.Collections
 {
     [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
index 9cb5545..f2a5039 100644 (file)
@@ -32,6 +32,64 @@ namespace System.Tests
             Assert.Equal(expected, new string(span));
         }
 
+        [Fact]
+        public static void Create_InvalidArguments_Throw()
+        {
+            AssertExtensions.Throws<ArgumentNullException>("action", () => string.Create(-1, 0, null));
+            AssertExtensions.Throws<ArgumentOutOfRangeException>("length", () => string.Create(-1, 0, (span, state) => { }));
+        }
+
+        [Fact]
+        public static void Create_Length0_ReturnsEmptyString()
+        {
+            bool actionInvoked = false;
+            Assert.Same(string.Empty, string.Create(0, 0, (span, state) => actionInvoked = true));
+            Assert.False(actionInvoked);
+        }
+
+        [Fact]
+        public static void Create_NullState_Allowed()
+        {
+            string result = string.Create(1, (object)null, (span, state) =>
+            {
+                span[0] = 'a';
+                Assert.Null(state);
+            });
+            Assert.Equal("a", result);
+        }
+
+        [Fact]
+        public static void Create_ClearsMemory()
+        {
+            const int Length = 10;
+            string result = string.Create(Length, (object)null, (span, state) =>
+            {
+                for (int i = 0; i < span.Length; i++)
+                {
+                    Assert.Equal('\0', span[i]);
+                }
+            });
+            Assert.Equal(new string('\0', Length), result);
+        }
+
+        [Theory]
+        [InlineData("a")]
+        [InlineData("this is a test")]
+        [InlineData("\0\u8001\u8002\ufffd\u1234\ud800\udfff")]
+        public static void Create_ReturnsExpectedString(string expected)
+        {
+            char[] input = expected.ToCharArray();
+            string result = string.Create(input.Length, input, (span, state) =>
+            {
+                Assert.Same(input, state);
+                for (int i = 0; i < state.Length; i++)
+                {
+                    span[i] = state[i];
+                }
+            });
+            Assert.Equal(expected, result);
+        }
+
         [Theory]
         // CurrentCulture
         [InlineData("Hello", "ello", StringComparison.CurrentCulture, true)]