1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Generic;
6 using System.Globalization;
7 using System.Linq;
8 using System.Runtime.InteropServices;
9 using Xunit;
10 
11 namespace System.Tests
12 {
13     public partial class StringTests
14     {
15         [Theory]
16         [InlineData(0, 0)]
17         [InlineData(3, 1)]
Ctor_CharSpan_EmptyString(int length, int offset)18         public static void Ctor_CharSpan_EmptyString(int length, int offset)
19         {
20             Assert.Same(string.Empty, new string(new ReadOnlySpan<char>(new char[length], offset, 0)));
21         }
22 
23         [Theory]
24         [InlineData(new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\0' }, 0, 8, "abcdefgh")]
25         [InlineData(new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\0', 'i', 'j', 'k' }, 0, 12, "abcdefgh\0ijk")]
26         [InlineData(new char[] { 'a', 'b', 'c' }, 0, 0, "")]
27         [InlineData(new char[] { 'a', 'b', 'c' }, 0, 1, "a")]
28         [InlineData(new char[] { 'a', 'b', 'c' }, 2, 1, "c")]
29         [InlineData(new char[] { '\u8001', '\u8002', '\ufffd', '\u1234', '\ud800', '\udfff' }, 0, 6, "\u8001\u8002\ufffd\u1234\ud800\udfff")]
Ctor_CharSpan(char[] valueArray, int startIndex, int length, string expected)30         public static void Ctor_CharSpan(char[] valueArray, int startIndex, int length, string expected)
31         {
32             var span = new ReadOnlySpan<char>(valueArray, startIndex, length);
33             Assert.Equal(expected, new string(span));
34         }
35 
36         [Fact]
Create_InvalidArguments_Throw()37         public static void Create_InvalidArguments_Throw()
38         {
39             AssertExtensions.Throws<ArgumentNullException>("action", () => string.Create(-1, 0, null));
40             AssertExtensions.Throws<ArgumentOutOfRangeException>("length", () => string.Create(-1, 0, (span, state) => { }));
41         }
42 
43         [Fact]
Create_Length0_ReturnsEmptyString()44         public static void Create_Length0_ReturnsEmptyString()
45         {
46             bool actionInvoked = false;
47             Assert.Same(string.Empty, string.Create(0, 0, (span, state) => actionInvoked = true));
48             Assert.False(actionInvoked);
49         }
50 
51         [Fact]
Create_NullState_Allowed()52         public static void Create_NullState_Allowed()
53         {
54             string result = string.Create(1, (object)null, (span, state) =>
55             {
56                 span[0] = 'a';
57                 Assert.Null(state);
58             });
59             Assert.Equal("a", result);
60         }
61 
62         [Fact]
Create_ClearsMemory()63         public static void Create_ClearsMemory()
64         {
65             const int Length = 10;
66             string result = string.Create(Length, (object)null, (span, state) =>
67             {
68                 for (int i = 0; i < span.Length; i++)
69                 {
70                     Assert.Equal('\0', span[i]);
71                 }
72             });
73             Assert.Equal(new string('\0', Length), result);
74         }
75 
76         [Theory]
77         [InlineData("a")]
78         [InlineData("this is a test")]
79         [InlineData("\0\u8001\u8002\ufffd\u1234\ud800\udfff")]
Create_ReturnsExpectedString(string expected)80         public static void Create_ReturnsExpectedString(string expected)
81         {
82             char[] input = expected.ToCharArray();
83             string result = string.Create(input.Length, input, (span, state) =>
84             {
85                 Assert.Same(input, state);
86                 for (int i = 0; i < state.Length; i++)
87                 {
88                     span[i] = state[i];
89                 }
90             });
91             Assert.Equal(expected, result);
92         }
93 
94         [Theory]
95         [InlineData("Hello", 'H', true)]
96         [InlineData("Hello", 'Z', false)]
97         [InlineData("Hello", 'e', true)]
98         [InlineData("Hello", 'E', false)]
99         [InlineData("", 'H', false)]
Contains(string s, char value, bool expected)100         public static void Contains(string s, char value, bool expected)
101         {
102             Assert.Equal(expected, s.Contains(value));
103         }
104 
105         [Theory]
106         // CurrentCulture
107         [InlineData("Hello", 'H', StringComparison.CurrentCulture, true)]
108         [InlineData("Hello", 'Z', StringComparison.CurrentCulture, false)]
109         [InlineData("Hello", 'e', StringComparison.CurrentCulture, true)]
110         [InlineData("Hello", 'E', StringComparison.CurrentCulture, false)]
111         [InlineData("", 'H', StringComparison.CurrentCulture, false)]
112         // CurrentCultureIgnoreCase
113         [InlineData("Hello", 'H', StringComparison.CurrentCultureIgnoreCase, true)]
114         [InlineData("Hello", 'Z', StringComparison.CurrentCultureIgnoreCase, false)]
115         [InlineData("Hello", 'e', StringComparison.CurrentCultureIgnoreCase, true)]
116         [InlineData("Hello", 'E', StringComparison.CurrentCultureIgnoreCase, true)]
117         [InlineData("", 'H', StringComparison.CurrentCultureIgnoreCase, false)]
118         // InvariantCulture
119         [InlineData("Hello", 'H', StringComparison.InvariantCulture, true)]
120         [InlineData("Hello", 'Z', StringComparison.InvariantCulture, false)]
121         [InlineData("Hello", 'e', StringComparison.InvariantCulture, true)]
122         [InlineData("Hello", 'E', StringComparison.InvariantCulture, false)]
123         [InlineData("", 'H', StringComparison.InvariantCulture, false)]
124         // InvariantCultureIgnoreCase
125         [InlineData("Hello", 'H', StringComparison.InvariantCultureIgnoreCase, true)]
126         [InlineData("Hello", 'Z', StringComparison.InvariantCultureIgnoreCase, false)]
127         [InlineData("Hello", 'e', StringComparison.InvariantCultureIgnoreCase, true)]
128         [InlineData("Hello", 'E', StringComparison.InvariantCultureIgnoreCase, true)]
129         [InlineData("", 'H', StringComparison.InvariantCultureIgnoreCase, false)]
130         // Ordinal
131         [InlineData("Hello", 'H', StringComparison.Ordinal, true)]
132         [InlineData("Hello", 'Z', StringComparison.Ordinal, false)]
133         [InlineData("Hello", 'e', StringComparison.Ordinal, true)]
134         [InlineData("Hello", 'E', StringComparison.Ordinal, false)]
135         [InlineData("", 'H', StringComparison.Ordinal, false)]
136         // OrdinalIgnoreCase
137         [InlineData("Hello", 'H', StringComparison.OrdinalIgnoreCase, true)]
138         [InlineData("Hello", 'Z', StringComparison.OrdinalIgnoreCase, false)]
139         [InlineData("Hello", 'e', StringComparison.OrdinalIgnoreCase, true)]
140         [InlineData("Hello", 'E', StringComparison.OrdinalIgnoreCase, true)]
141         [InlineData("", 'H', StringComparison.OrdinalIgnoreCase, false)]
Contains(string s, char value, StringComparison comparisionType, bool expected)142         public static void Contains(string s, char value, StringComparison comparisionType, bool expected)
143         {
144             Assert.Equal(expected, s.Contains(value, comparisionType));
145         }
146 
147         [Theory]
148         // CurrentCulture
149         [InlineData("Hello", "ello", StringComparison.CurrentCulture, true)]
150         [InlineData("Hello", "ELL", StringComparison.CurrentCulture, false)]
151         [InlineData("Hello", "ElLo", StringComparison.CurrentCulture, false)]
152         [InlineData("Hello", "Larger Hello", StringComparison.CurrentCulture, false)]
153         [InlineData("Hello", "Goodbye", StringComparison.CurrentCulture, false)]
154         [InlineData("", "", StringComparison.CurrentCulture, true)]
155         [InlineData("", "hello", StringComparison.CurrentCulture, false)]
156         [InlineData("Hello", "", StringComparison.CurrentCulture, true)]
157         [InlineData("Hello", "ell" + SoftHyphen, StringComparison.CurrentCulture, true)]
158         [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.CurrentCulture, false)]
159         // CurrentCultureIgnoreCase
160         [InlineData("Hello", "ello", StringComparison.CurrentCultureIgnoreCase, true)]
161         [InlineData("Hello", "ELL", StringComparison.CurrentCultureIgnoreCase, true)]
162         [InlineData("Hello", "ElLo", StringComparison.CurrentCultureIgnoreCase, true)]
163         [InlineData("Hello", "Larger Hello", StringComparison.CurrentCultureIgnoreCase, false)]
164         [InlineData("Hello", "Goodbye", StringComparison.CurrentCultureIgnoreCase, false)]
165         [InlineData("", "", StringComparison.CurrentCultureIgnoreCase, true)]
166         [InlineData("", "hello", StringComparison.CurrentCultureIgnoreCase, false)]
167         [InlineData("Hello", "", StringComparison.CurrentCultureIgnoreCase, true)]
168         [InlineData("Hello", "ell" + SoftHyphen, StringComparison.CurrentCultureIgnoreCase, true)]
169         [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.CurrentCultureIgnoreCase, true)]
170         // InvariantCulture
171         [InlineData("Hello", "ello", StringComparison.InvariantCulture, true)]
172         [InlineData("Hello", "ELL", StringComparison.InvariantCulture, false)]
173         [InlineData("Hello", "ElLo", StringComparison.InvariantCulture, false)]
174         [InlineData("Hello", "Larger Hello", StringComparison.InvariantCulture, false)]
175         [InlineData("Hello", "Goodbye", StringComparison.InvariantCulture, false)]
176         [InlineData("", "", StringComparison.InvariantCulture, true)]
177         [InlineData("", "hello", StringComparison.InvariantCulture, false)]
178         [InlineData("Hello", "", StringComparison.InvariantCulture, true)]
179         [InlineData("Hello", "ell" + SoftHyphen, StringComparison.InvariantCulture, true)]
180         [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.InvariantCulture, false)]
181         // InvariantCultureIgnoreCase
182         [InlineData("Hello", "ello", StringComparison.InvariantCultureIgnoreCase, true)]
183         [InlineData("Hello", "ELL", StringComparison.InvariantCultureIgnoreCase, true)]
184         [InlineData("Hello", "ElLo", StringComparison.InvariantCultureIgnoreCase, true)]
185         [InlineData("Hello", "Larger Hello", StringComparison.InvariantCultureIgnoreCase, false)]
186         [InlineData("Hello", "Goodbye", StringComparison.InvariantCultureIgnoreCase, false)]
187         [InlineData("", "", StringComparison.InvariantCultureIgnoreCase, true)]
188         [InlineData("", "hello", StringComparison.InvariantCultureIgnoreCase, false)]
189         [InlineData("Hello", "", StringComparison.InvariantCultureIgnoreCase, true)]
190         [InlineData("Hello", "ell" + SoftHyphen, StringComparison.InvariantCultureIgnoreCase, true)]
191         [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.InvariantCultureIgnoreCase, true)]
192         // Ordinal
193         [InlineData("Hello", "ello", StringComparison.Ordinal, true)]
194         [InlineData("Hello", "ELL", StringComparison.Ordinal, false)]
195         [InlineData("Hello", "ElLo", StringComparison.Ordinal, false)]
196         [InlineData("Hello", "Larger Hello", StringComparison.Ordinal, false)]
197         [InlineData("Hello", "Goodbye", StringComparison.Ordinal, false)]
198         [InlineData("", "", StringComparison.Ordinal, true)]
199         [InlineData("", "hello", StringComparison.Ordinal, false)]
200         [InlineData("Hello", "", StringComparison.Ordinal, true)]
201         [InlineData("Hello", "ell" + SoftHyphen, StringComparison.Ordinal, false)]
202         [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.Ordinal, false)]
203         // OrdinalIgnoreCase
204         [InlineData("Hello", "ello", StringComparison.OrdinalIgnoreCase, true)]
205         [InlineData("Hello", "ELL", StringComparison.OrdinalIgnoreCase, true)]
206         [InlineData("Hello", "ElLo", StringComparison.OrdinalIgnoreCase, true)]
207         [InlineData("Hello", "Larger Hello", StringComparison.OrdinalIgnoreCase, false)]
208         [InlineData("Hello", "Goodbye", StringComparison.OrdinalIgnoreCase, false)]
209         [InlineData("", "", StringComparison.OrdinalIgnoreCase, true)]
210         [InlineData("", "hello", StringComparison.OrdinalIgnoreCase, false)]
211         [InlineData("Hello", "", StringComparison.OrdinalIgnoreCase, true)]
212         [InlineData("Hello", "ell" + SoftHyphen, StringComparison.OrdinalIgnoreCase, false)]
213         [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.OrdinalIgnoreCase, false)]
Contains(string s, string value, StringComparison comparisonType, bool expected)214         public static void Contains(string s, string value, StringComparison comparisonType, bool expected)
215         {
216             Assert.Equal(expected, s.Contains(value, comparisonType));
217         }
218 
219         [Fact]
Contains_StringComparison_TurkishI()220         public static void Contains_StringComparison_TurkishI()
221         {
222             string str = "\u0069\u0130";
223             RemoteInvoke((source) =>
224             {
225                 CultureInfo.CurrentCulture = new CultureInfo("tr-TR");
226 
227                 Assert.True(source.Contains("\u0069\u0069", StringComparison.CurrentCultureIgnoreCase));
228 
229                 return SuccessExitCode;
230             }, str).Dispose();
231 
232             RemoteInvoke((source) =>
233             {
234                 CultureInfo.CurrentCulture = new CultureInfo("en-US");
235 
236                 Assert.False(source.Contains("\u0069\u0069", StringComparison.CurrentCultureIgnoreCase));
237 
238                 return SuccessExitCode;
239             }, str).Dispose();
240         }
241 
242         [Theory]
243         [InlineData(StringComparison.CurrentCulture)]
244         [InlineData(StringComparison.CurrentCultureIgnoreCase)]
245         [InlineData(StringComparison.InvariantCulture)]
246         [InlineData(StringComparison.InvariantCultureIgnoreCase)]
247         [InlineData(StringComparison.Ordinal)]
248         [InlineData(StringComparison.OrdinalIgnoreCase)]
Contains_NullValue_ThrowsArgumentNullException(StringComparison comparisonType)249         public static void Contains_NullValue_ThrowsArgumentNullException(StringComparison comparisonType)
250         {
251             AssertExtensions.Throws<ArgumentNullException>("value", () => "foo".Contains(null, comparisonType));
252         }
253 
254         [Theory]
255         [InlineData(StringComparison.CurrentCulture - 1)]
256         [InlineData(StringComparison.OrdinalIgnoreCase + 1)]
Contains_InvalidComparisonType_ThrowsArgumentOutOfRangeException(StringComparison comparisonType)257         public static void Contains_InvalidComparisonType_ThrowsArgumentOutOfRangeException(StringComparison comparisonType)
258         {
259             AssertExtensions.Throws<ArgumentException>("comparisonType", () => "ab".Contains("a", comparisonType));
260         }
261 
262         [Theory]
263         [InlineData("Hello", 'o', true)]
264         [InlineData("Hello", 'O', false)]
265         [InlineData("o", 'o', true)]
266         [InlineData("o", 'O', false)]
267         [InlineData("Hello", 'e', false)]
268         [InlineData("Hello", '\0', false)]
269         [InlineData("", '\0', false)]
270         [InlineData("\0", '\0', true)]
271         [InlineData("", 'a', false)]
272         [InlineData("abcdefghijklmnopqrstuvwxyz", 'z', true)]
EndsWith(string s, char value, bool expected)273         public static void EndsWith(string s, char value, bool expected)
274         {
275             Assert.Equal(expected, s.EndsWith(value));
276         }
277 
278         [Theory]
279         [InlineData("Hello", 'H', true)]
280         [InlineData("Hello", 'h', false)]
281         [InlineData("H", 'H', true)]
282         [InlineData("H", 'h', false)]
283         [InlineData("Hello", 'e', false)]
284         [InlineData("Hello", '\0', false)]
285         [InlineData("", '\0', false)]
286         [InlineData("\0", '\0', true)]
287         [InlineData("", 'a', false)]
288         [InlineData("abcdefghijklmnopqrstuvwxyz", 'a', true)]
StartsWith(string s, char value, bool expected)289         public static void StartsWith(string s, char value, bool expected)
290         {
291             Assert.Equal(expected, s.StartsWith(value));
292         }
293 
Join_Char_StringArray_TestData()294         public static IEnumerable<object[]> Join_Char_StringArray_TestData()
295         {
296             yield return new object[] { '|', new string[0], 0, 0, "" };
297             yield return new object[] { '|', new string[] { "a" }, 0, 1, "a" };
298             yield return new object[] { '|', new string[] { "a", "b", "c" }, 0, 3, "a|b|c" };
299             yield return new object[] { '|', new string[] { "a", "b", "c" }, 0, 2, "a|b" };
300             yield return new object[] { '|', new string[] { "a", "b", "c" }, 1, 1, "b" };
301             yield return new object[] { '|', new string[] { "a", "b", "c" }, 1, 2, "b|c" };
302             yield return new object[] { '|', new string[] { "a", "b", "c" }, 3, 0, "" };
303             yield return new object[] { '|', new string[] { "a", "b", "c" }, 0, 0, "" };
304             yield return new object[] { '|', new string[] { "", "", "" }, 0, 3, "||" };
305             yield return new object[] { '|', new string[] { null, null, null }, 0, 3, "||" };
306         }
307 
308         [Theory]
309         [MemberData(nameof(Join_Char_StringArray_TestData))]
Join_Char_StringArray(char separator, string[] values, int startIndex, int count, string expected)310         public static void Join_Char_StringArray(char separator, string[] values, int startIndex, int count, string expected)
311         {
312             if (startIndex == 0 && count == values.Length)
313             {
314                 Assert.Equal(expected, string.Join(separator, values));
315                 Assert.Equal(expected, string.Join(separator, (IEnumerable<string>)values));
316                 Assert.Equal(expected, string.Join(separator, (object[])values));
317                 Assert.Equal(expected, string.Join(separator, (IEnumerable<object>)values));
318             }
319 
320             Assert.Equal(expected, string.Join(separator, values, startIndex, count));
321             Assert.Equal(expected, string.Join(separator.ToString(), values, startIndex, count));
322         }
323 
Join_Char_ObjectArray_TestData()324         public static IEnumerable<object[]> Join_Char_ObjectArray_TestData()
325         {
326             yield return new object[] { '|', new object[0], "" };
327             yield return new object[] { '|', new object[] { 1 }, "1" };
328             yield return new object[] { '|', new object[] { 1, 2, 3 }, "1|2|3" };
329             yield return new object[] { '|', new object[] { new ObjectWithNullToString(), 2, new ObjectWithNullToString() }, "|2|" };
330             yield return new object[] { '|', new object[] { "1", null, "3" }, "1||3" };
331             yield return new object[] { '|', new object[] { "", "", "" }, "||" };
332             yield return new object[] { '|', new object[] { "", null, "" }, "||" };
333             yield return new object[] { '|', new object[] { null, null, null }, "||" };
334         }
335 
336         [Theory]
337         [MemberData(nameof(Join_Char_ObjectArray_TestData))]
Join_Char_ObjectArray(char separator, object[] values, string expected)338         public static void Join_Char_ObjectArray(char separator, object[] values, string expected)
339         {
340             Assert.Equal(expected, string.Join(separator, values));
341             Assert.Equal(expected, string.Join(separator, (IEnumerable<object>)values));
342         }
343 
344         [Fact]
Join_Char_NullValues_ThrowsArgumentNullException()345         public static void Join_Char_NullValues_ThrowsArgumentNullException()
346         {
347             AssertExtensions.Throws<ArgumentNullException>("value", () => string.Join('|', (string[])null));
348             AssertExtensions.Throws<ArgumentNullException>("value", () => string.Join('|', (string[])null, 0, 0));
349             AssertExtensions.Throws<ArgumentNullException>("values", () => string.Join('|', (object[])null));
350             AssertExtensions.Throws<ArgumentNullException>("values", () => string.Join('|', (IEnumerable<object>)null));
351         }
352 
353         [Fact]
Join_Char_NegativeStartIndex_ThrowsArgumentOutOfRangeException()354         public static void Join_Char_NegativeStartIndex_ThrowsArgumentOutOfRangeException()
355         {
356             AssertExtensions.Throws<ArgumentOutOfRangeException>("startIndex", () => string.Join('|', new string[] { "Foo" }, -1, 0));
357         }
358 
359         [Fact]
Join_Char_NegativeCount_ThrowsArgumentOutOfRangeException()360         public static void Join_Char_NegativeCount_ThrowsArgumentOutOfRangeException()
361         {
362             AssertExtensions.Throws<ArgumentOutOfRangeException>("count", () => string.Join('|', new string[] { "Foo" }, 0, -1));
363         }
364 
365         [Theory]
366         [InlineData(2, 1)]
367         [InlineData(2, 0)]
368         [InlineData(1, 2)]
369         [InlineData(1, 1)]
370         [InlineData(0, 2)]
371         [InlineData(-1, 0)]
Join_Char_InvalidStartIndexCount_ThrowsArgumentOutOfRangeException(int startIndex, int count)372         public static void Join_Char_InvalidStartIndexCount_ThrowsArgumentOutOfRangeException(int startIndex, int count)
373         {
374             AssertExtensions.Throws<ArgumentOutOfRangeException>("startIndex", () => string.Join('|', new string[] { "Foo" }, startIndex, count));
375         }
376 
Replace_StringComparison_TestData()377         public static IEnumerable<object[]> Replace_StringComparison_TestData()
378         {
379             yield return new object[] { "abc", "abc", "def", StringComparison.CurrentCulture, "def" };
380             yield return new object[] { "abc", "ABC", "def", StringComparison.CurrentCulture, "abc" };
381             yield return new object[] { "abc", "abc", "", StringComparison.CurrentCulture, "" };
382             yield return new object[] { "abc", "b", "LONG", StringComparison.CurrentCulture, "aLONGc" };
383             yield return new object[] { "abc", "b", "d", StringComparison.CurrentCulture, "adc" };
384             yield return new object[] { "abc", "b", null, StringComparison.CurrentCulture, "ac" };
385             yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.CurrentCulture, "def" };
386 
387             yield return new object[] { "abc", "abc", "def", StringComparison.CurrentCultureIgnoreCase, "def" };
388             yield return new object[] { "abc", "ABC", "def", StringComparison.CurrentCultureIgnoreCase, "def" };
389             yield return new object[] { "abc", "abc", "", StringComparison.CurrentCultureIgnoreCase, "" };
390             yield return new object[] { "abc", "b", "LONG", StringComparison.CurrentCultureIgnoreCase, "aLONGc" };
391             yield return new object[] { "abc", "b", "d", StringComparison.CurrentCultureIgnoreCase, "adc" };
392             yield return new object[] { "abc", "b", null, StringComparison.CurrentCultureIgnoreCase, "ac" };
393             yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.CurrentCultureIgnoreCase, "def" };
394 
395             yield return new object[] { "abc", "abc", "def", StringComparison.Ordinal, "def" };
396             yield return new object[] { "abc", "ABC", "def", StringComparison.Ordinal, "abc" };
397             yield return new object[] { "abc", "abc", "", StringComparison.Ordinal, "" };
398             yield return new object[] { "abc", "b", "LONG", StringComparison.Ordinal, "aLONGc" };
399             yield return new object[] { "abc", "b", "d", StringComparison.Ordinal, "adc" };
400             yield return new object[] { "abc", "b", null, StringComparison.Ordinal, "ac" };
401             yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.Ordinal, "abc" };
402 
403             yield return new object[] { "abc", "abc", "def", StringComparison.OrdinalIgnoreCase, "def" };
404             yield return new object[] { "abc", "ABC", "def", StringComparison.OrdinalIgnoreCase, "def" };
405             yield return new object[] { "abc", "abc", "", StringComparison.OrdinalIgnoreCase, "" };
406             yield return new object[] { "abc", "b", "LONG", StringComparison.OrdinalIgnoreCase, "aLONGc" };
407             yield return new object[] { "abc", "b", "d", StringComparison.OrdinalIgnoreCase, "adc" };
408             yield return new object[] { "abc", "b", null, StringComparison.OrdinalIgnoreCase, "ac" };
409             yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.OrdinalIgnoreCase, "abc" };
410 
411             yield return new object[] { "abc", "abc", "def", StringComparison.InvariantCulture, "def" };
412             yield return new object[] { "abc", "ABC", "def", StringComparison.InvariantCulture, "abc" };
413             yield return new object[] { "abc", "abc", "", StringComparison.InvariantCulture, "" };
414             yield return new object[] { "abc", "b", "LONG", StringComparison.InvariantCulture, "aLONGc" };
415             yield return new object[] { "abc", "b", "d", StringComparison.InvariantCulture, "adc" };
416             yield return new object[] { "abc", "b", null, StringComparison.InvariantCulture, "ac" };
417             yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.InvariantCulture, "def" };
418 
419             yield return new object[] { "abc", "abc", "def", StringComparison.InvariantCultureIgnoreCase, "def" };
420             yield return new object[] { "abc", "ABC", "def", StringComparison.InvariantCultureIgnoreCase, "def" };
421             yield return new object[] { "abc", "abc", "", StringComparison.InvariantCultureIgnoreCase, "" };
422             yield return new object[] { "abc", "b", "LONG", StringComparison.InvariantCultureIgnoreCase, "aLONGc" };
423             yield return new object[] { "abc", "b", "d", StringComparison.InvariantCultureIgnoreCase, "adc" };
424             yield return new object[] { "abc", "b", null, StringComparison.InvariantCultureIgnoreCase, "ac" };
425             yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.InvariantCultureIgnoreCase, "def" };
426 
427             string turkishSource = "\u0069\u0130";
428 
429             yield return new object[] { turkishSource, "\u0069", "a", StringComparison.Ordinal, "a\u0130" };
430             yield return new object[] { turkishSource, "\u0069", "a", StringComparison.OrdinalIgnoreCase, "a\u0130" };
431             yield return new object[] { turkishSource, "\u0130", "a", StringComparison.Ordinal, "\u0069a" };
432             yield return new object[] { turkishSource, "\u0130", "a", StringComparison.OrdinalIgnoreCase, "\u0069a" };
433 
434             yield return new object[] { turkishSource, "\u0069", "a", StringComparison.InvariantCulture, "a\u0130" };
435             yield return new object[] { turkishSource, "\u0069", "a", StringComparison.InvariantCultureIgnoreCase, "a\u0130" };
436             yield return new object[] { turkishSource, "\u0130", "a", StringComparison.InvariantCulture, "\u0069a" };
437             yield return new object[] { turkishSource, "\u0130", "a", StringComparison.InvariantCultureIgnoreCase, "\u0069a" };
438         }
439 
440         [Theory]
441         [MemberData(nameof(Replace_StringComparison_TestData))]
Replace_StringComparison_ReturnsExpected(string original, string oldValue, string newValue, StringComparison comparisonType, string expected)442         public void Replace_StringComparison_ReturnsExpected(string original, string oldValue, string newValue, StringComparison comparisonType, string expected)
443         {
444             Assert.Equal(expected, original.Replace(oldValue, newValue, comparisonType));
445         }
446 
447         [Fact]
Replace_StringComparison_TurkishI()448         public void Replace_StringComparison_TurkishI()
449         {
450             string src = "\u0069\u0130";
451 
452             RemoteInvoke((source) =>
453             {
454                 CultureInfo.CurrentCulture = new CultureInfo("tr-TR");
455 
456                 Assert.True("\u0069".Equals("\u0130", StringComparison.CurrentCultureIgnoreCase));
457 
458                 Assert.Equal("a\u0130", source.Replace("\u0069", "a", StringComparison.CurrentCulture));
459                 Assert.Equal("aa", source.Replace("\u0069", "a", StringComparison.CurrentCultureIgnoreCase));
460                 Assert.Equal("\u0069a", source.Replace("\u0130", "a", StringComparison.CurrentCulture));
461                 Assert.Equal("aa", source.Replace("\u0130", "a", StringComparison.CurrentCultureIgnoreCase));
462 
463                 CultureInfo.CurrentCulture = new CultureInfo("en-US");
464 
465                 Assert.False("\u0069".Equals("\u0130", StringComparison.CurrentCultureIgnoreCase));
466 
467                 Assert.Equal("a\u0130", source.Replace("\u0069", "a", StringComparison.CurrentCulture));
468                 Assert.Equal("a\u0130", source.Replace("\u0069", "a", StringComparison.CurrentCultureIgnoreCase));
469                 Assert.Equal("\u0069a", source.Replace("\u0130", "a", StringComparison.CurrentCulture));
470                 Assert.Equal("\u0069a", source.Replace("\u0130", "a", StringComparison.CurrentCultureIgnoreCase));
471 
472                 return SuccessExitCode;
473             }, src).Dispose();
474         }
475 
Replace_StringComparisonCulture_TestData()476         public static IEnumerable<object[]> Replace_StringComparisonCulture_TestData()
477         {
478             yield return new object[] { "abc", "abc", "def", false, null, "def" };
479             yield return new object[] { "abc", "ABC", "def", false, null, "abc" };
480             yield return new object[] { "abc", "abc", "def", false, CultureInfo.InvariantCulture, "def" };
481             yield return new object[] { "abc", "ABC", "def", false, CultureInfo.InvariantCulture, "abc" };
482 
483             yield return new object[] { "abc", "abc", "def", true, null, "def" };
484             yield return new object[] { "abc", "ABC", "def", true, null, "def" };
485             yield return new object[] { "abc", "abc", "def", true, CultureInfo.InvariantCulture, "def" };
486             yield return new object[] { "abc", "ABC", "def", true, CultureInfo.InvariantCulture, "def" };
487 
488             yield return new object[] { "abc", "abc" + SoftHyphen, "def", false, null, "def" };
489             yield return new object[] { "abc", "abc" + SoftHyphen, "def", true, null, "def" };
490             yield return new object[] { "abc", "abc" + SoftHyphen, "def", false, CultureInfo.InvariantCulture, "def" };
491             yield return new object[] { "abc", "abc" + SoftHyphen, "def", true, CultureInfo.InvariantCulture, "def" };
492 
493             yield return new object[] { "\u0069\u0130", "\u0069", "a", false, new CultureInfo("tr-TR"), "a\u0130" };
494             yield return new object[] { "\u0069\u0130", "\u0069", "a", true, new CultureInfo("tr-TR"), "aa" };
495             yield return new object[] { "\u0069\u0130", "\u0069", "a", false, CultureInfo.InvariantCulture, "a\u0130" };
496             yield return new object[] { "\u0069\u0130", "\u0069", "a", true, CultureInfo.InvariantCulture, "a\u0130" };
497         }
498 
499         [Theory]
500         [MemberData(nameof(Replace_StringComparisonCulture_TestData))]
Replace_StringComparisonCulture_ReturnsExpected(string original, string oldValue, string newValue, bool ignoreCase, CultureInfo culture, string expected)501         public void Replace_StringComparisonCulture_ReturnsExpected(string original, string oldValue, string newValue, bool ignoreCase, CultureInfo culture, string expected)
502         {
503             Assert.Equal(expected, original.Replace(oldValue, newValue, ignoreCase, culture));
504             if (culture == null)
505             {
506                 Assert.Equal(expected, original.Replace(oldValue, newValue, ignoreCase, CultureInfo.CurrentCulture));
507             }
508         }
509 
510         [Fact]
Replace_StringComparison_NullOldValue_ThrowsArgumentException()511         public void Replace_StringComparison_NullOldValue_ThrowsArgumentException()
512         {
513             AssertExtensions.Throws<ArgumentNullException>("oldValue", () => "abc".Replace(null, "def", StringComparison.CurrentCulture));
514             AssertExtensions.Throws<ArgumentNullException>("oldValue", () => "abc".Replace(null, "def", true, CultureInfo.CurrentCulture));
515         }
516 
517         [Fact]
Replace_StringComparison_EmptyOldValue_ThrowsArgumentException()518         public void Replace_StringComparison_EmptyOldValue_ThrowsArgumentException()
519         {
520             AssertExtensions.Throws<ArgumentException>("oldValue", () => "abc".Replace("", "def", StringComparison.CurrentCulture));
521             AssertExtensions.Throws<ArgumentException>("oldValue", () => "abc".Replace("", "def", true, CultureInfo.CurrentCulture));
522         }
523 
524         [Theory]
525         [InlineData(StringComparison.CurrentCulture - 1)]
526         [InlineData(StringComparison.OrdinalIgnoreCase + 1)]
Replace_NoSuchStringComparison_ThrowsArgumentException(StringComparison comparisonType)527         public void Replace_NoSuchStringComparison_ThrowsArgumentException(StringComparison comparisonType)
528         {
529             AssertExtensions.Throws<ArgumentException>("comparisonType", () => "abc".Replace("abc", "def", comparisonType));
530         }
531 
532 
533         private static readonly StringComparison[] StringComparisons = (StringComparison[])Enum.GetValues(typeof(StringComparison));
534 
535 
536         public static IEnumerable<object[]> GetHashCode_StringComparison_Data => StringComparisons.Select(value => new object[] { value });
537 
538         [Theory]
539         [MemberData(nameof(GetHashCode_StringComparison_Data))]
GetHashCode_StringComparison(StringComparison comparisonType)540         public static void GetHashCode_StringComparison(StringComparison comparisonType)
541         {
542             Assert.Equal(StringComparer.FromComparison(comparisonType).GetHashCode("abc"), "abc".GetHashCode(comparisonType));
543         }
544 
545 
546         public static IEnumerable<object[]> GetHashCode_NoSuchStringComparison_ThrowsArgumentException_Data => new[]
547         {
548             new object[] { StringComparisons.Min() - 1 },
549             new object[] { StringComparisons.Max() + 1 },
550         };
551 
552         [Theory]
553         [MemberData(nameof(GetHashCode_NoSuchStringComparison_ThrowsArgumentException_Data))]
GetHashCode_NoSuchStringComparison_ThrowsArgumentException(StringComparison comparisonType)554         public static void GetHashCode_NoSuchStringComparison_ThrowsArgumentException(StringComparison comparisonType)
555         {
556             AssertExtensions.Throws<ArgumentException>("comparisonType", () => "abc".GetHashCode(comparisonType));
557         }
558 
559         [Theory]
560         [InlineData("")]
561         [InlineData("a")]
562         [InlineData("\0")]
563         [InlineData("abc")]
ImplicitCast_ResultingSpanMatches(string s)564         public static unsafe void ImplicitCast_ResultingSpanMatches(string s)
565         {
566             ReadOnlySpan<char> span = s;
567             Assert.Equal(s.Length, span.Length);
568             fixed (char* stringPtr = s)
569             fixed (char* spanPtr = &MemoryMarshal.GetReference(span))
570             {
571                 Assert.Equal((IntPtr)stringPtr, (IntPtr)spanPtr);
572             }
573         }
574 
575         [Fact]
ImplicitCast_NullString_ReturnsDefaultSpan()576         public static void ImplicitCast_NullString_ReturnsDefaultSpan()
577         {
578             ReadOnlySpan<char> span = (string)null;
579             Assert.True(span == default);
580         }
581 
582         [Theory]
583         [InlineData("Hello", 'l', StringComparison.Ordinal, 2)]
584         [InlineData("Hello", 'x', StringComparison.Ordinal, -1)]
585         [InlineData("Hello", 'h', StringComparison.Ordinal, -1)]
586         [InlineData("Hello", 'o', StringComparison.Ordinal, 4)]
587         [InlineData("Hello", 'h', StringComparison.OrdinalIgnoreCase, 0)]
588         [InlineData("HelLo", 'L', StringComparison.OrdinalIgnoreCase, 2)]
589         [InlineData("HelLo", 'L', StringComparison.Ordinal, 3)]
590         [InlineData("HelLo", '\0', StringComparison.Ordinal, -1)]
591         [InlineData("!@#$%", '%', StringComparison.Ordinal, 4)]
592         [InlineData("!@#$", '!', StringComparison.Ordinal, 0)]
593         [InlineData("!@#$", '@', StringComparison.Ordinal, 1)]
594         [InlineData("!@#$%", '%', StringComparison.OrdinalIgnoreCase, 4)]
595         [InlineData("!@#$", '!', StringComparison.OrdinalIgnoreCase, 0)]
596         [InlineData("!@#$", '@', StringComparison.OrdinalIgnoreCase, 1)]
597         [InlineData("_____________\u807f", '\u007f', StringComparison.Ordinal, -1)]
598         [InlineData("_____________\u807f__", '\u007f', StringComparison.Ordinal, -1)]
599         [InlineData("_____________\u807f\u007f_", '\u007f', StringComparison.Ordinal, 14)]
600         [InlineData("__\u807f_______________", '\u007f', StringComparison.Ordinal, -1)]
601         [InlineData("__\u807f___\u007f___________", '\u007f', StringComparison.Ordinal, 6)]
602         [InlineData("_____________\u807f", '\u007f', StringComparison.OrdinalIgnoreCase, -1)]
603         [InlineData("_____________\u807f__", '\u007f', StringComparison.OrdinalIgnoreCase, -1)]
604         [InlineData("_____________\u807f\u007f_", '\u007f', StringComparison.OrdinalIgnoreCase, 14)]
605         [InlineData("__\u807f_______________", '\u007f', StringComparison.OrdinalIgnoreCase, -1)]
606         [InlineData("__\u807f___\u007f___________", '\u007f', StringComparison.OrdinalIgnoreCase, 6)]
IndexOf_SingleLetter(string s, char target, StringComparison stringComparison, int expected)607         public static void IndexOf_SingleLetter(string s, char target, StringComparison stringComparison, int expected)
608         {
609             Assert.Equal(expected, s.IndexOf(target, stringComparison));
610         }
611 
612         [Fact]
IndexOf_TurkishI_TurkishCulture_Char()613         public static void IndexOf_TurkishI_TurkishCulture_Char()
614         {
615             RemoteInvoke(() =>
616             {
617                 CultureInfo.CurrentCulture = new CultureInfo("tr-TR");
618 
619                 string s = "Turkish I \u0131s TROUBL\u0130NG!";
620                 char value = '\u0130';
621                 Assert.Equal(19, s.IndexOf(value));
622                 Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture));
623                 Assert.Equal(4, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
624                 Assert.Equal(19, s.IndexOf(value, StringComparison.Ordinal));
625                 Assert.Equal(19, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
626 
627                 value = '\u0131';
628                 Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
629                 Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
630                 Assert.Equal(10, s.IndexOf(value, StringComparison.Ordinal));
631                 Assert.Equal(10, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
632 
633                 return SuccessExitCode;
634             }).Dispose();
635         }
636 
637         [Fact]
IndexOf_TurkishI_InvariantCulture_Char()638         public static void IndexOf_TurkishI_InvariantCulture_Char()
639         {
640             RemoteInvoke(() =>
641             {
642                 CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
643 
644                 string s = "Turkish I \u0131s TROUBL\u0130NG!";
645                 char value = '\u0130';
646 
647                 Assert.Equal(19, s.IndexOf(value));
648                 Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture));
649                 Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
650 
651                 value = '\u0131';
652                 Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
653                 Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
654 
655                 return SuccessExitCode;
656             }).Dispose();
657         }
658 
659         [Fact]
IndexOf_TurkishI_EnglishUSCulture_Char()660         public static void IndexOf_TurkishI_EnglishUSCulture_Char()
661         {
662             RemoteInvoke(() =>
663             {
664                 CultureInfo.CurrentCulture = new CultureInfo("en-US");
665 
666                 string s = "Turkish I \u0131s TROUBL\u0130NG!";
667                 char value = '\u0130';
668 
669                 Assert.Equal(19, s.IndexOf(value));
670                 Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture));
671                 Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
672 
673                 value = '\u0131';
674                 Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
675                 Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
676 
677                 return SuccessExitCode;
678             }).Dispose();
679         }
680 
681         [Fact]
IndexOf_EquivalentDiacritics_EnglishUSCulture_Char()682         public static void IndexOf_EquivalentDiacritics_EnglishUSCulture_Char()
683         {
684             RemoteInvoke(() =>
685             {
686                 string s = "Exhibit a\u0300\u00C0";
687                 char value = '\u00C0';
688 
689                 CultureInfo.CurrentCulture = new CultureInfo("en-US");
690                 Assert.Equal(10, s.IndexOf(value));
691                 Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
692                 Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
693                 Assert.Equal(10, s.IndexOf(value, StringComparison.Ordinal));
694                 Assert.Equal(10, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
695 
696                 return SuccessExitCode;
697             }).Dispose();
698         }
699 
700         [Fact]
IndexOf_EquivalentDiacritics_InvariantCulture_Char()701         public static void IndexOf_EquivalentDiacritics_InvariantCulture_Char()
702         {
703             RemoteInvoke(() =>
704             {
705                 string s = "Exhibit a\u0300\u00C0";
706                 char value = '\u00C0';
707 
708                 CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
709                 Assert.Equal(10, s.IndexOf(value));
710                 Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
711                 Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
712 
713                 return SuccessExitCode;
714             }).Dispose();
715         }
716 
717         [Fact]
IndexOf_CyrillicE_EnglishUSCulture_Char()718         public static void IndexOf_CyrillicE_EnglishUSCulture_Char()
719         {
720             RemoteInvoke(() =>
721             {
722                 string s = "Foo\u0400Bar";
723                 char value = '\u0400';
724 
725                 CultureInfo.CurrentCulture = new CultureInfo("en-US");
726                 Assert.Equal(3, s.IndexOf(value));
727                 Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCulture));
728                 Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
729                 Assert.Equal(3, s.IndexOf(value, StringComparison.Ordinal));
730                 Assert.Equal(3, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
731 
732                 return SuccessExitCode;
733             }).Dispose();
734         }
735 
736         [Fact]
IndexOf_CyrillicE_InvariantCulture_Char()737         public static void IndexOf_CyrillicE_InvariantCulture_Char()
738         {
739             RemoteInvoke(() =>
740             {
741                 string s = "Foo\u0400Bar";
742                 char value = '\u0400';
743 
744                 CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
745                 Assert.Equal(3, s.IndexOf(value));
746                 Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCulture));
747                 Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
748 
749                 return SuccessExitCode;
750             }).Dispose();
751         }
752 
753         [Fact]
IndexOf_Invalid_Char()754         public static void IndexOf_Invalid_Char()
755         {
756             // Invalid comparison type
757             AssertExtensions.Throws<ArgumentException>("comparisonType", () => "foo".IndexOf('o', StringComparison.CurrentCulture - 1));
758             AssertExtensions.Throws<ArgumentException>("comparisonType", () => "foo".IndexOf('o', StringComparison.OrdinalIgnoreCase + 1));
759         }
760     }
761 }
762