A game about forced loneliness, made by TACStudios
at master 572 lines 28 kB view raw
1using System.Collections; 2using System.Linq; 3using NUnit.Framework; 4using UnityEngine; 5 6public class TextEditorTests 7{ 8 TextEditor m_TextEditor; 9 10 static IEnumerable textWithCodePointBoundaryIndices 11 { 12 get 13 { 14 yield return new TestCaseData(null, new[] { 0 }); 15 yield return new TestCaseData("", new[] { 0 }); 16 yield return new TestCaseData("abc", new[] { 0, 1, 2, 3 }); 17 18 yield return new TestCaseData("\U0001f642", new[] { 0, 2 }).SetName("(U+1F642)"); 19 yield return new TestCaseData("\U0001f642\U0001f643", new[] { 0, 2, 4 }).SetName("(U+1F642)(U+1F643)"); 20 yield return new TestCaseData("a\U0001f642b\U0001f643c", new[] { 0, 1, 3, 4, 6, 7 }).SetName("a(U+1F642)b(U+1F643)c"); 21 22 yield return new TestCaseData("Hello 😁 World", new[] { 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14 }).SetName("Hello (U+1F601) World"); 23 } 24 } 25 26 static IEnumerable textWithWordStartAndEndIndices 27 { 28 get 29 { 30 yield return new TestCaseData(null, new int[0], new int[0]); 31 yield return new TestCaseData("", new int[0], new int[0]); 32 yield return new TestCaseData(" ", new int[0], new int[0]); 33 yield return new TestCaseData("one two three", new[] { 0, 4, 8 }, new[] { 3, 7, 13 }); 34#if !UNITY_PLAYSTATION 35 //yield return new TestCaseData("\U00010000 \U00010001 \U00010002\U00010003", new[] { 0, 3, 6 }, new[] { 2, 5, 10 }).SetName("(U+10000) (U+10001) (U+10002)(U+10003)"); // Disabled for Instability https://jira.unity3d.com/browse/UUM-71065 36 //yield return new TestCaseData("Hello 😁 World", new[] { 0, 6, 9 }, new[] { 5, 8, 14 }).SetName("Hello (U+1F601) World"); // Disabled for Instability https://jira.unity3d.com/browse/UUM-71070 37#endif 38 } 39 } 40 41 // A sequences of punctuation characters is currently considered a word when deleting 42 static IEnumerable textWithWordStartAndEndIndicesWherePunctuationIsAWord 43 { 44 get 45 { 46 yield return new TestCaseData(" ,. abc,. ", new[] { 1, 4, 7 }, new[] { 3, 7, 9 }); 47 } 48 } 49 50 // but not when moving/selecting 51 static IEnumerable textWithWordStartAndEndIndicesWherePunctuationIsNotAWord 52 { 53 get 54 { 55 yield return new TestCaseData(" ,. abc,. ", new[] { 4 }, new[] { 7 }); 56 } 57 } 58 59 static IEnumerable textWithLineStartIndices 60 { 61 get 62 { 63 yield return new TestCaseData("\n\na\nbc\n\nd\n ", new[] { 0, 1, 2, 4, 7, 8, 10 }).SetName("(LF)(LF)a(LF)bc(LF)(LF)d(LF)"); 64 yield return new TestCaseData("\n\na\nbc\n\U0001f642\n\U0001f642\U0001f643\n\n ", new[] { 0, 1, 2, 4, 7, 10, 15, 16 }).SetName("(LF)(LF)a(LF)bc(LF)(U+1F642)(LF)(U+1F642)(U+1F643)(LF)(LF) "); 65 yield return new TestCaseData("\n\na\nbc\n🙂\n🙂🙃\n\n ", new[] { 0, 1, 2, 4, 7, 10, 15, 16 }).SetName("(LF)(LF)a(LF)bc(LF)(U+1F642)(LF)(U+1F642)(U+1F643)(LF)(LF) "); 66 } 67 } 68 69 static IEnumerable textWithLineEndIndices 70 { 71 get 72 { 73 yield return new TestCaseData("\n\na\nbc\n\nd\n ", new[] { 0, 1, 3, 6, 7, 9, 11 }).SetName("(LF)(LF)a(LF)bc(LF)(LF)d(LF) "); 74 yield return new TestCaseData("\n\na\nbc\n\U0001f642\n\U0001f642\U0001f643\n\n ", new[] { 0, 1, 3, 6, 9, 14, 15, 17 }).SetName("(LF)(LF)a(LF)bc(LF)(U+1F642)(LF)(U+1F642)(U+1F643)(LF)(LF) "); 75 yield return new TestCaseData("\n\na\nbc\n🙂\n🙂🙃\n\n ", new[] { 0, 1, 3, 6, 9, 14, 15, 17 }).SetName("(LF)(LF)a(LF)bc(LF)(U+1F642)(LF)(U+1F642)(U+1F643)(LF)(LF) "); 76 } 77 } 78 79 static IEnumerable textWithExpectedCursorAndSelectIndicesWhenSelectingCurrentWordAtIndex 80 { 81 get 82 { 83 yield return new TestCaseData(null, new[] { 0 }, new[] { 0 }); 84 yield return new TestCaseData("", new[] { 0 }, new[] { 0 }); 85 yield return new TestCaseData(" ", new[] { 1, 1 }, new[] { 0, 0 }); 86 yield return new TestCaseData("a", new[] { 1, 1 }, new[] { 0, 0 }); 87 yield return new TestCaseData("ab", new[] { 2, 2, 2 }, new[] { 0, 0, 0 }); 88 yield return new TestCaseData("ab cd", new[] { 2, 2, 4, 4, 6, 6, 6 }, new[] { 0, 0, 2, 2, 4, 4, 4 }); 89 yield return new TestCaseData("a,, ,, ,,b", new[] { 1, 3, 3, 5, 5, 7, 7, 9, 9, 11, 11, 12, 12 }, new[] { 0, 1, 1, 3, 3, 5, 5, 7, 7, 9, 9, 11, 11 }); 90 } 91 } 92 93 [SetUp] 94 public void TestSetup() 95 { 96 m_TextEditor = new TextEditor(); 97 m_TextEditor.DetectFocusChange(); 98 } 99 100 [Test] 101 public void SetText_MovesCursorAndSelectIndicesToNextCodePointIndexIfInvalid() 102 { 103 m_TextEditor.text = "ab"; 104 m_TextEditor.UpdateTextHandle(); 105 106 m_TextEditor.stringCursorIndex = m_TextEditor.stringSelectIndex = 1; 107 108 m_TextEditor.text = "\U0001f642"; 109 m_TextEditor.UpdateTextHandle(); 110 111 Assert.AreEqual(2, m_TextEditor.stringCursorIndex, "cursorIndex at invalid code point index"); 112 Assert.AreEqual(2, m_TextEditor.stringSelectIndex, "selectIndex at invalid code point index"); 113 } 114 115 [Test, TestCaseSource("textWithCodePointBoundaryIndices")] 116 public void SetCursorAndSelectIndices_MovesToNextCodePointIndexIfInvalid(string text, int[] codePointIndices) 117 { 118 m_TextEditor.text = text; 119 m_TextEditor.UpdateTextHandle(); 120 121 for (var index = 0; index <= GetLength(text); index++) 122 { 123 m_TextEditor.stringCursorIndex = index; 124 m_TextEditor.stringSelectIndex = index; 125 126 var nextCodePointIndex = index == GetLength(text) ? index : codePointIndices.First(codePointIndex => codePointIndex > index); 127 if (codePointIndices.Contains(index)) 128 Assert.AreEqual(index, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} should not change if it's already at a valid code point index", index)); 129 else 130 Assert.AreEqual(nextCodePointIndex, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to next code point index", index)); 131 if (codePointIndices.Contains(index)) 132 Assert.AreEqual(index, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} should not change if it's already at a valid code point index", index)); 133 else 134 Assert.AreEqual(nextCodePointIndex, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} did not move to next code point index", index)); 135 } 136 } 137 138 [Test] 139 [TestCaseSource("textWithWordStartAndEndIndices")] 140 [TestCaseSource("textWithWordStartAndEndIndicesWherePunctuationIsAWord")] 141 public void DeleteWordBack_DeletesBackToPreviousWordStart(string text, int[] wordStartIndices, int[] wordEndIndices) 142 { 143 for (var stringIndex = 0; stringIndex <= GetLength(text); stringIndex++) 144 { 145 m_TextEditor.text = text; 146 m_TextEditor.UpdateTextHandle(); 147 m_TextEditor.stringCursorIndex = m_TextEditor.stringSelectIndex = stringIndex; 148 var oldCursorIndex = m_TextEditor.stringCursorIndex; 149 var oldSelectIndex = m_TextEditor.stringSelectIndex; 150 151 m_TextEditor.DeleteWordBack(); 152 153 var previousWordStart = wordStartIndices.Reverse().FirstOrDefault(i => i < oldCursorIndex); 154 Assert.AreEqual(previousWordStart, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to previous word start", oldCursorIndex)); 155 Assert.AreEqual(previousWordStart, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} did not move to previous word start", oldSelectIndex)); 156 if (text != null) 157 Assert.AreEqual(text.Remove(previousWordStart, oldCursorIndex - previousWordStart), m_TextEditor.text, string.Format("wrong resulting text for cursorIndex {0}", oldCursorIndex)); 158 } 159 } 160 161 [Test] 162 [TestCaseSource("textWithWordStartAndEndIndices")] 163 [TestCaseSource("textWithWordStartAndEndIndicesWherePunctuationIsAWord")] 164 public void DeleteWordForward_DeletesForwardToNextWordStart(string text, int[] wordStartIndices, int[] wordEndIndices) 165 { 166 for (var stringIndex = 0; stringIndex <= GetLength(text); stringIndex++) 167 { 168 m_TextEditor.text = text; 169 m_TextEditor.UpdateTextHandle(); 170 m_TextEditor.stringCursorIndex = m_TextEditor.stringSelectIndex = stringIndex; 171 var oldCursorIndex = m_TextEditor.stringCursorIndex; 172 var oldSelectIndex = m_TextEditor.stringSelectIndex; 173 174 m_TextEditor.DeleteWordForward(); 175 176 Assert.AreEqual(oldCursorIndex, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} should not change", oldCursorIndex)); 177 Assert.AreEqual(oldSelectIndex, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} should not change", oldSelectIndex)); 178 if (text != null) 179 { 180 var nextWordStart = oldCursorIndex == text.Length ? text.Length : wordStartIndices.Concat(new[] { text.Length }).First(i => i > oldCursorIndex); 181 Assert.AreEqual(text.Remove(oldCursorIndex, nextWordStart - oldCursorIndex), m_TextEditor.text, string.Format("wrong resulting text for cursorIndex {0}", oldCursorIndex)); 182 } 183 } 184 } 185 186 [Test, TestCaseSource("textWithCodePointBoundaryIndices")] 187 public void Delete_RemovesCodePointRightOfCursor(string text, int[] codePointIndices) 188 { 189 for (var i = 0; i < codePointIndices.Length; i++) 190 { 191 var codePointIndex = codePointIndices[i]; 192 m_TextEditor.text = text; 193 m_TextEditor.UpdateTextHandle(); 194 m_TextEditor.stringCursorIndex = m_TextEditor.stringSelectIndex = codePointIndex; 195 196 m_TextEditor.Delete(); 197 198 var nextCodePointIndex = i < codePointIndices.Length - 1 ? codePointIndices[i + 1] : codePointIndex; 199 Assert.AreEqual(codePointIndex, m_TextEditor.stringCursorIndex, "cursorIndex should not change"); 200 Assert.AreEqual(codePointIndex, m_TextEditor.stringSelectIndex, "selectIndex should not change"); 201 var expectedText = text == null? "" : text.Remove(codePointIndex, nextCodePointIndex - codePointIndex); 202 Assert.AreEqual(expectedText, m_TextEditor.text, string.Format("wrong resulting text for cursorIndex {0}", codePointIndex)); 203 } 204 } 205 206 [Test, TestCaseSource("textWithCodePointBoundaryIndices")] 207 public void Backspace_RemovesCodePointLeftOfCursor(string text, int[] codePointIndices) 208 { 209 for (var i = codePointIndices.Length - 1; i >= 0; i--) 210 { 211 var codePointIndex = codePointIndices[i]; 212 m_TextEditor.text = text; 213 m_TextEditor.UpdateTextHandle(); 214 m_TextEditor.m_TextEditing.stringCursorIndex = m_TextEditor.m_TextEditing.stringSelectIndex = codePointIndex; 215 var oldCursorIndex = m_TextEditor.m_TextEditing.stringCursorIndex; 216 var oldSelectIndex = m_TextEditor.m_TextEditing.stringSelectIndex; 217 218 m_TextEditor.Backspace(); 219 m_TextEditor.UpdateTextHandle(); 220 221 var previousCodePointIndex = i > 0 ? codePointIndices[i - 1] : codePointIndex; 222 var codePointLength = codePointIndex - previousCodePointIndex; 223 Assert.AreEqual(oldCursorIndex - codePointLength, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to before removed code point", oldCursorIndex)); 224 Assert.AreEqual(oldSelectIndex - codePointLength, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} did not move to before removed code point", oldSelectIndex)); 225 var expectedText = text == null ? "" : text.Remove(previousCodePointIndex, codePointLength); 226 Assert.AreEqual(expectedText, m_TextEditor.text); 227 } 228 } 229 230 [Test, TestCaseSource("textWithCodePointBoundaryIndices")] 231 public void MoveRight_SkipsInvalidCodePointIndices(string text, int[] codePointIndices) 232 { 233 m_TextEditor.text = text; 234 m_TextEditor.UpdateTextHandle(); 235 m_TextEditor.cursorIndex = m_TextEditor.selectIndex = 0; 236 237 foreach (var expectedIndex in codePointIndices.Skip(1)) 238 { 239 var oldCursorIndex = m_TextEditor.stringCursorIndex; 240 var oldSelectIndex = m_TextEditor.stringSelectIndex; 241 242 m_TextEditor.MoveRight(); 243 244 Assert.AreEqual(expectedIndex, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to next code point index", oldCursorIndex)); 245 Assert.AreEqual(expectedIndex, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} did not move to next code point index", oldSelectIndex)); 246 } 247 248 var length = GetLength(text); 249 Assert.AreEqual(length, m_TextEditor.stringCursorIndex, "cursorIndex did not reach end"); 250 Assert.AreEqual(length, m_TextEditor.stringSelectIndex, "selectIndex did not reach end"); 251 252 m_TextEditor.MoveRight(); 253 254 Assert.AreEqual(length, m_TextEditor.stringCursorIndex, "cursorIndex at end should not change"); 255 Assert.AreEqual(length, m_TextEditor.stringSelectIndex, "selectIndex at end should not change"); 256 } 257 258 [Test, TestCaseSource("textWithCodePointBoundaryIndices")] 259 public void MoveLeft_SkipsInvalidCodePointIndices(string text, int[] codePointIndices) 260 { 261 m_TextEditor.text = text; 262 m_TextEditor.UpdateTextHandle(); 263 m_TextEditor.cursorIndex = m_TextEditor.selectIndex = GetLength(text); 264 265 foreach (var expectedIndex in codePointIndices.Reverse().Skip(1)) 266 { 267 var oldCursorIndex = m_TextEditor.stringCursorIndex; 268 var oldSelectIndex = m_TextEditor.stringSelectIndex; 269 270 m_TextEditor.MoveLeft(); 271 272 Assert.AreEqual(expectedIndex, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to previous code point index", oldCursorIndex)); 273 Assert.AreEqual(expectedIndex, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} did not move to previous code point index", oldSelectIndex)); 274 } 275 276 Assert.AreEqual(0, m_TextEditor.stringCursorIndex, "cursorIndex did not reach start"); 277 Assert.AreEqual(0, m_TextEditor.stringSelectIndex, "selectIndex did not reach start"); 278 279 m_TextEditor.MoveLeft(); 280 281 Assert.AreEqual(0, m_TextEditor.stringCursorIndex, "cursorIndex at start should not change"); 282 Assert.AreEqual(0, m_TextEditor.stringSelectIndex, "selectIndex at start should not change"); 283 } 284 285 [Test, TestCaseSource("textWithLineStartIndices")] 286 public void MoveLineStart_MovesCursorAfterPreviousLineFeed(string text, int[] lineStartIndices) 287 { 288 m_TextEditor.text = text; 289 m_TextEditor.UpdateTextHandle(); 290 291 for (var index = 0; index <= GetLength(text); index++) 292 { 293 m_TextEditor.stringCursorIndex = m_TextEditor.stringSelectIndex = index; 294 var oldCursorIndex = m_TextEditor.stringCursorIndex; 295 var oldSelectIndex = m_TextEditor.stringSelectIndex; 296 297 m_TextEditor.MoveLineStart(); 298 299 var lineStart = lineStartIndices.Reverse().FirstOrDefault(i => i <= oldCursorIndex); 300 Assert.AreEqual(lineStart, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to line start", oldCursorIndex)); 301 Assert.AreEqual(lineStart, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} did not move to line start", oldSelectIndex)); 302 } 303 } 304 305 [Test, TestCaseSource("textWithLineEndIndices")] 306 public void MoveLineEnd_MovesCursorBeforeNextLineFeed(string text, int[] lineEndIndices) 307 { 308 m_TextEditor.text = text; 309 m_TextEditor.UpdateTextHandle(); 310 311 for (var index = 0; index <= GetLength(text); index++) 312 { 313 m_TextEditor.stringCursorIndex = m_TextEditor.stringSelectIndex = index; 314 var oldCursorIndex = m_TextEditor.stringCursorIndex; 315 var oldSelectIndex = m_TextEditor.stringSelectIndex; 316 317 m_TextEditor.MoveLineEnd(); 318 319 var lineEnd = lineEndIndices.First(i => i >= oldCursorIndex); 320 Assert.AreEqual(lineEnd, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to line end", oldCursorIndex)); 321 Assert.AreEqual(lineEnd, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} did not move to line end", oldSelectIndex)); 322 } 323 } 324 325 [Test] 326 public void MoveTextStart_MovesCursorToStartOfText() 327 { 328 m_TextEditor.text = "Hello World"; 329 m_TextEditor.UpdateTextHandle(); 330 m_TextEditor.cursorIndex = m_TextEditor.selectIndex = 5; 331 332 m_TextEditor.MoveTextStart(); 333 334 Assert.AreEqual(0, m_TextEditor.cursorIndex, "cursorIndex did not move to start of text"); 335 Assert.AreEqual(0, m_TextEditor.selectIndex, "selectIndex did not move to start of text"); 336 } 337 338 [Test] 339 public void MoveTextEnd_MovesCursorToEndOfText() 340 { 341 m_TextEditor.text = "Hello World"; 342 m_TextEditor.UpdateTextHandle(); 343 m_TextEditor.cursorIndex = m_TextEditor.selectIndex = 5; 344 345 m_TextEditor.MoveTextEnd(); 346 347 Assert.AreEqual(m_TextEditor.text.Length, m_TextEditor.cursorIndex, "cursorIndex did not move to end of text"); 348 Assert.AreEqual(m_TextEditor.text.Length, m_TextEditor.selectIndex, "selectIndex did not move to end of text"); 349 } 350 351 [Test, TestCaseSource("textWithCodePointBoundaryIndices")] 352 public void SelectLeft_ExpandSelectionToPreviousCodePoint(string text, int[] codePointIndices) 353 { 354 m_TextEditor.text = text; 355 m_TextEditor.UpdateTextHandle(); 356 m_TextEditor.stringCursorIndex = m_TextEditor.stringSelectIndex = GetLength(text); 357 358 foreach (var expectedCursorIndex in codePointIndices.Reverse().Skip(1)) 359 { 360 var oldCursorIndex = m_TextEditor.stringCursorIndex; 361 var oldSelectIndex = m_TextEditor.stringSelectIndex; 362 363 m_TextEditor.SelectLeft(); 364 365 Assert.AreEqual(expectedCursorIndex, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to previous code point index", oldCursorIndex)); 366 Assert.AreEqual(oldSelectIndex, m_TextEditor.stringSelectIndex, "selectIndex should not change"); 367 } 368 369 Assert.AreEqual(0, m_TextEditor.stringCursorIndex, "cursorIndex did not reach start"); 370 371 m_TextEditor.SelectLeft(); 372 373 Assert.AreEqual(0, m_TextEditor.stringCursorIndex, "cursorIndex at start should not change"); 374 } 375 376 [Test, TestCaseSource("textWithCodePointBoundaryIndices")] 377 public void SelectRight_ExpandSelectionToNextCodePoint(string text, int[] codePointIndices) 378 { 379 m_TextEditor.text = text; 380 m_TextEditor.UpdateTextHandle(); 381 m_TextEditor.stringCursorIndex = m_TextEditor.stringSelectIndex = 0; 382 383 foreach (var expectedCursorIndex in codePointIndices.Skip(1)) 384 { 385 var oldCursorIndex = m_TextEditor.stringCursorIndex; 386 var oldSelectIndex = m_TextEditor.stringSelectIndex; 387 388 m_TextEditor.SelectRight(); 389 390 Assert.AreEqual(expectedCursorIndex, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to next code point index", oldCursorIndex)); 391 Assert.AreEqual(oldSelectIndex, m_TextEditor.stringSelectIndex, "selectIndex should not change"); 392 } 393 394 Assert.AreEqual(GetLength(text), m_TextEditor.stringCursorIndex, "cursorIndex did not reach end"); 395 396 m_TextEditor.SelectRight(); 397 398 Assert.AreEqual(GetLength(text), m_TextEditor.stringCursorIndex, "cursorIndex at end should not change"); 399 } 400 401 [Test] 402 [TestCaseSource("textWithWordStartAndEndIndices")] 403 [TestCaseSource("textWithWordStartAndEndIndicesWherePunctuationIsNotAWord")] 404 public void MoveWordRight_MovesCursorToNextWordEnd(string text, int[] wordStartIndices, int[] wordEndIndices) 405 { 406 if (text != null && text.Any(char.IsSurrogate)) 407 return; // char.IsLetterOrDigit(string, int) does not currently work correctly with surrogates 408 409 m_TextEditor.text = text; 410 m_TextEditor.UpdateTextHandle(); 411 412 for (var index = 0; index <= GetLength(text); index++) 413 { 414 m_TextEditor.cursorIndex = m_TextEditor.selectIndex = index; 415 var oldCursorIndex = m_TextEditor.cursorIndex; 416 var oldSelectIndex = m_TextEditor.selectIndex; 417 418 m_TextEditor.MoveWordRight(); 419 420 var nextWordEnd = wordEndIndices.FirstOrDefault(i => i > oldCursorIndex); 421 if (nextWordEnd == 0) 422 nextWordEnd = GetLength(text); 423 Assert.AreEqual(nextWordEnd, m_TextEditor.cursorIndex, string.Format("cursorIndex {0} did not move to next word start", oldCursorIndex)); 424 Assert.AreEqual(nextWordEnd, m_TextEditor.selectIndex, string.Format("selectIndex {0} did not move to next word start", oldSelectIndex)); 425 } 426 } 427 428 [Test] 429 [TestCaseSource("textWithWordStartAndEndIndices")] 430 [TestCaseSource("textWithWordStartAndEndIndicesWherePunctuationIsAWord")] 431 public void MoveToStartOfNextWord_MovesCursorToNextWordStart(string text, int[] wordStartIndices, int[] wordEndIndices) 432 { 433 m_TextEditor.text = text; 434 m_TextEditor.UpdateTextHandle(); 435 436 for (var index = 0; index <= GetLength(text); index++) 437 { 438 m_TextEditor.stringCursorIndex = m_TextEditor.stringSelectIndex = index; 439 var oldCursorIndex = m_TextEditor.stringCursorIndex; 440 var oldSelectIndex = m_TextEditor.stringSelectIndex; 441 442 m_TextEditor.MoveToStartOfNextWord(); 443 444 var length = GetLength(text); 445 var nextWordStart = oldCursorIndex == length ? length : wordStartIndices.Concat(new[] { length }).First(i => i > oldCursorIndex); 446 Assert.AreEqual(nextWordStart, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to start of next word", oldCursorIndex)); 447 Assert.AreEqual(nextWordStart, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} did not move to start of next word", oldSelectIndex)); 448 } 449 } 450 451 [Test] 452 [TestCaseSource("textWithWordStartAndEndIndices")] 453 [TestCaseSource("textWithWordStartAndEndIndicesWherePunctuationIsAWord")] 454 public void MoveToEndOfPreviousWord_MovesCursorToPreviousWordStart(string text, int[] wordStartIndices, int[] wordEndIndices) 455 { 456 m_TextEditor.text = text; 457 m_TextEditor.UpdateTextHandle(); 458 459 for (var index = 0; index <= GetLength(text); index++) 460 { 461 m_TextEditor.stringCursorIndex = m_TextEditor.stringSelectIndex = index; 462 var oldCursorIndex = m_TextEditor.stringCursorIndex; 463 var oldSelectIndex = m_TextEditor.stringSelectIndex; 464 465 m_TextEditor.MoveToEndOfPreviousWord(); 466 467 var previousWordStart = wordStartIndices.Reverse().FirstOrDefault(i => i < oldCursorIndex); 468 Assert.AreEqual(previousWordStart, m_TextEditor.stringCursorIndex, string.Format("cursorIndex {0} did not move to previous word start", oldCursorIndex)); 469 Assert.AreEqual(previousWordStart, m_TextEditor.stringSelectIndex, string.Format("selectIndex {0} did not move to previous word start", oldSelectIndex)); 470 } 471 } 472 473 [Test] 474 [TestCaseSource("textWithWordStartAndEndIndices")] 475 [TestCaseSource("textWithWordStartAndEndIndicesWherePunctuationIsAWord")] 476 public void FindStartOfNextWord_ReturnsIndexOfNextWordStart(string text, int[] wordStartIndices, int[] wordEndIndices) 477 { 478 if (text != null && text.Any(char.IsSurrogate)) 479 return; // char.IsLetterOrDigit(string, int) does not currently work correctly with surrogates 480 481 m_TextEditor.text = text; 482 m_TextEditor.UpdateTextHandle(); 483 484 for (var index = 0; index <= GetLength(text); index++) 485 { 486 var length = GetLength(text); 487 var nextWordStart = index == length ? length : wordStartIndices.Concat(new[] {length}).First(i => i > index); 488 Assert.AreEqual(nextWordStart, m_TextEditor.FindStartOfNextWord(index)); 489 } 490 } 491 492 [Test] 493 [TestCaseSource("textWithWordStartAndEndIndices")] 494 [TestCaseSource("textWithWordStartAndEndIndicesWherePunctuationIsNotAWord")] 495 public void MoveWordLeft_MovesCursorToPreviousWordStart(string text, int[] wordStartIndices, int[] wordEndIndices) 496 { 497 if (text != null && text.Any(char.IsSurrogate)) 498 return; // char.IsLetterOrDigit(string, int) does not currently work correctly with surrogates 499 500 m_TextEditor.text = text; 501 m_TextEditor.UpdateTextHandle(); 502 503 for (var index = 0; index <= GetLength(text); index++) 504 { 505 m_TextEditor.cursorIndex = m_TextEditor.selectIndex = index; 506 var oldCursorIndex = m_TextEditor.cursorIndex; 507 var oldSelectIndex = m_TextEditor.selectIndex; 508 509 m_TextEditor.MoveWordLeft(); 510 511 var previousWordStart = wordStartIndices.Reverse().FirstOrDefault(i => i < oldCursorIndex); 512 Assert.AreEqual(previousWordStart, m_TextEditor.cursorIndex, string.Format("cursorIndex {0} did not move to previous word start", oldCursorIndex)); 513 Assert.AreEqual(previousWordStart, m_TextEditor.selectIndex, string.Format("selectIndex {0} did not move to previous word start", oldSelectIndex)); 514 } 515 } 516 517 [Test, TestCaseSource("textWithExpectedCursorAndSelectIndicesWhenSelectingCurrentWordAtIndex")] 518 public void SelectCurrentWord(string text, int[] expectedCursorIndices, int[] expectedSelectIndices) 519 { 520 m_TextEditor.text = text; 521 m_TextEditor.UpdateTextHandle(); 522 523 for (var index = 0; index <= GetLength(text); index++) 524 { 525 m_TextEditor.cursorIndex = m_TextEditor.selectIndex = index; 526 var oldCursorIndex = m_TextEditor.cursorIndex; 527 528 m_TextEditor.SelectCurrentWord(); 529 530 Assert.AreEqual(expectedCursorIndices[index], m_TextEditor.cursorIndex, string.Format("wrong cursorIndex for initial cursorIndex {0}", oldCursorIndex)); 531 Assert.AreEqual(expectedSelectIndices[index], m_TextEditor.selectIndex, string.Format("wrong selectIndex for initial cursorIndex {0}", oldCursorIndex)); 532 } 533 } 534 535 [Test] 536 public void HandleKeyEvent_WithControlAKeyDownEvent_MovesCursorToStartOfLineOnMacOS_SelectsAllElsewhere() 537 { 538 const string text = "foo"; 539 m_TextEditor.text = text; 540 m_TextEditor.UpdateTextHandle(); 541 m_TextEditor.MoveLineEnd(); 542 var controlAKeyDownEvent = new Event { type = EventType.KeyDown, keyCode = KeyCode.A, modifiers = EventModifiers.Control }; 543 544 m_TextEditor.HandleKeyEvent(controlAKeyDownEvent); 545 546 if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX) 547 { 548 Assert.That(m_TextEditor.SelectedText, Is.Empty, "Selected text was not empty"); 549 Assert.That(m_TextEditor.cursorIndex, Is.EqualTo(0), "Cursor did not move to start of line"); 550 } 551 else 552 Assert.That(m_TextEditor.SelectedText, Is.EqualTo(text), "Text was not selected"); 553 } 554 555 [Test] 556 public void HandleKeyEvent_WithCommandAKeyDownEvent_SelectsAllOnMacOS() 557 { 558 if (SystemInfo.operatingSystemFamily != OperatingSystemFamily.MacOSX) 559 Assert.Ignore("Test is only applicable on macOS"); 560 561 const string text = "foo"; 562 m_TextEditor.text = text; 563 m_TextEditor.UpdateTextHandle(); 564 var commandAKeyDownEvent = new Event { type = EventType.KeyDown, keyCode = KeyCode.A, modifiers = EventModifiers.Command }; 565 566 m_TextEditor.HandleKeyEvent(commandAKeyDownEvent); 567 568 Assert.That(m_TextEditor.SelectedText, Is.EqualTo(text), "Text was not selected"); 569 } 570 571 int GetLength(string text) => text == null ? 0 : text.Length; 572}