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