using System; using Amphora; using Glass.Editing; using UnitTestSharp; namespace Glass.UnitTests.Editing { public class TextBoundariesTests : TestFixture { public static readonly string Pair = char.ConvertFromUtf32(0x20BB7); // Combining acute accent, a mark in the Basic Multilingual Plane. public static readonly string Acute = ((char)0x301).ToString(); // Musical symbol combining stem, a mark that needs a surrogate pair. public static readonly string AstralMark = char.ConvertFromUtf32(0x1D165); public class SnapToCharacterTests : TestFixture { public void IndexBetweenCharacters_IsUnchanged() { CheckEqual(1, TextBoundaries.SnapToCharacter("a" + Pair + "b", 1)); CheckEqual(3, TextBoundaries.SnapToCharacter("a" + Pair + "b", 3)); } public void IndexInsideAPair_MovesBackToItsStart() { CheckEqual(1, TextBoundaries.SnapToCharacter("a" + Pair + "b", 2)); } public void IndexBeforeACombiningMark_MovesBackToTheCharacterItAttachesTo() { CheckEqual(0, TextBoundaries.SnapToCharacter("e" + Acute + "x", 1)); } public void IndexBetweenTwoMarks_MovesBackToTheCharacter() { CheckEqual(1, TextBoundaries.SnapToCharacter("xe" + Acute + Acute, 3)); } public void IndexInsideAMarkThatNeedsAPair_MovesBackToTheCharacter() { CheckEqual(0, TextBoundaries.SnapToCharacter("a" + AstralMark, 2)); } public void IndexAfterTheMarks_IsUnchanged() { CheckEqual(2, TextBoundaries.SnapToCharacter("e" + Acute + "x", 2)); } public void MarksAtTheStartOfTheText_SnapToTheStart() { CheckEqual(0, TextBoundaries.SnapToCharacter(Acute + Acute + "x", 1)); } public void StartAndEndOfText_AreUnchanged() { CheckEqual(0, TextBoundaries.SnapToCharacter(Pair, 0)); CheckEqual(2, TextBoundaries.SnapToCharacter(Pair, 2)); } public void IndexInsideACarriageReturnLineFeed_MovesBackToItsStart() { CheckEqual(1, TextBoundaries.SnapToCharacter("a\r\nb", 2)); } public void IndexBetweenTwoLineBreaks_IsUnchanged() { CheckEqual(2, TextBoundaries.SnapToCharacter("a\n\nb", 2)); CheckEqual(2, TextBoundaries.SnapToCharacter("a\r\rb", 2)); } public void IndexBeforeAMarkAfterALineBreak_IsUnchanged() { CheckEqual(2, TextBoundaries.SnapToCharacter("a\n" + Acute, 2)); } public void NullText_Throws() { CheckThrow(typeof(ArgumentNullException)); TextBoundaries.SnapToCharacter(null, 0); } public void NegativeIndex_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); TextBoundaries.SnapToCharacter("abc", -1); } public void IndexPastTheEnd_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); TextBoundaries.SnapToCharacter("abc", 4); } } public class SnapForwardToCharacterTests : TestFixture { public void IndexBetweenCharacters_IsUnchanged() { CheckEqual(1, TextBoundaries.SnapForwardToCharacter("abc", 1)); } public void IndexInsideAPair_MovesOnToItsEnd() { CheckEqual(3, TextBoundaries.SnapForwardToCharacter("a" + Pair + "b", 2)); } public void IndexBeforeACombiningMark_MovesOnPastTheMarks() { CheckEqual(3, TextBoundaries.SnapForwardToCharacter("e" + Acute + Acute + "x", 1)); } public void IndexInsideACarriageReturnLineFeed_MovesOnToItsEnd() { CheckEqual(3, TextBoundaries.SnapForwardToCharacter("a\r\nb", 2)); } public void IndexAfterTheMarks_IsUnchanged() { CheckEqual(2, TextBoundaries.SnapForwardToCharacter("e" + Acute + "x", 2)); } public void StartAndEndOfText_AreUnchanged() { CheckEqual(0, TextBoundaries.SnapForwardToCharacter(Pair, 0)); CheckEqual(2, TextBoundaries.SnapForwardToCharacter(Pair, 2)); } public void NullText_Throws() { CheckThrow(typeof(ArgumentNullException)); TextBoundaries.SnapForwardToCharacter(null, 0); } public void IndexPastTheEnd_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); TextBoundaries.SnapForwardToCharacter("abc", 4); } } public class NextTests : TestFixture { public void Character_AdvancesOne() { CheckEqual(1, TextBoundaries.Next("abc", 0, TextUnit.Character)); } public void Character_AtTheEnd_StaysThere() { CheckEqual(3, TextBoundaries.Next("abc", 3, TextUnit.Character)); } public void Character_JumpsAWholePair() { CheckEqual(3, TextBoundaries.Next("a" + Pair + "b", 1, TextUnit.Character)); } public void Character_FromInsideAPair_LeavesIt() { CheckEqual(3, TextBoundaries.Next("a" + Pair + "b", 2, TextUnit.Character)); } public void Character_JumpsTheCombiningMarksWithIt() { CheckEqual(3, TextBoundaries.Next("e" + Acute + Acute + "x", 0, TextUnit.Character)); } public void Character_FromBetweenAMarkAndItsCharacter_LeavesBoth() { CheckEqual(2, TextBoundaries.Next("e" + Acute + "x", 1, TextUnit.Character)); } public void Word_SkipsTheRestOfTheWordAndTheSpacesAfterIt() { CheckEqual(6, TextBoundaries.Next("hello world", 0, TextUnit.Word)); CheckEqual(6, TextBoundaries.Next("hello world", 2, TextUnit.Word)); } public void Word_FromTheLastWord_GoesToTheEnd() { CheckEqual(11, TextBoundaries.Next("hello world", 6, TextUnit.Word)); CheckEqual(11, TextBoundaries.Next("hello world", 11, TextUnit.Word)); } public void Word_FromSpaces_GoesToTheNextWord() { CheckEqual(8, TextBoundaries.Next("hello world", 5, TextUnit.Word)); } public void Word_FromIndentation_GoesToTheFirstWord() { CheckEqual(2, TextBoundaries.Next(" foo", 0, TextUnit.Word)); } public void Word_KeepsPunctuationWithTheWordBeforeIt() { CheckEqual(4, TextBoundaries.Next("hi, you", 0, TextUnit.Word)); CheckEqual(4, TextBoundaries.Next("hi, you", 2, TextUnit.Word)); } public void Word_StopsAfterAHyphen() { CheckEqual(5, TextBoundaries.Next("well-known", 0, TextUnit.Word)); CheckEqual(10, TextBoundaries.Next("well-known", 5, TextUnit.Word)); } public void Word_KeepsAnUnderscoreInsideTheWord() { CheckEqual(11, TextBoundaries.Next("snake_case x", 0, TextUnit.Word)); } public void Word_CountsDigitsAsLetters() { CheckEqual(7, TextBoundaries.Next("abc123 x", 0, TextUnit.Word)); } public void Word_StopsBetweenALetterAndAnIdeograph() { CheckEqual(2, TextBoundaries.Next("ab" + Pair + " c", 0, TextUnit.Word)); CheckEqual(5, TextBoundaries.Next("ab" + Pair + " c", 2, TextUnit.Word)); } public void Character_JumpsAWholeCarriageReturnLineFeed() { CheckEqual(3, TextBoundaries.Next("a\r\nb", 1, TextUnit.Character)); } public void Character_FromInsideACarriageReturnLineFeed_LeavesIt() { CheckEqual(3, TextBoundaries.Next("a\r\nb", 2, TextUnit.Character)); } public void Word_AtTheEndOfALine_GoesToTheStartOfTheNextLine() { CheckEqual(3, TextBoundaries.Next("ab\ncd", 2, TextUnit.Word)); } public void Word_BeforeAMarkOnASpace_GoesPastTheMark() { CheckEqual(3, TextBoundaries.Next("a " + Acute + "b", 0, TextUnit.Word)); CheckEqual(4, TextBoundaries.Next("a " + Acute + "b", 3, TextUnit.Word)); } public void Word_BeforeAMarkOnAZeroWidthSpace_GoesPastTheMark() { string text = "a" + (char)0x200B + Acute + "b"; CheckEqual(3, TextBoundaries.Next(text, 0, TextUnit.Word)); } public void Word_AtTheEndOfALineEndingInCarriageReturnLineFeed_GoesPastBoth() { CheckEqual(4, TextBoundaries.Next("ab\r\ncd", 2, TextUnit.Word)); } public void Line_StopsAtAnyKindOfBreak() { CheckEqual(2, TextBoundaries.Next("ab\r\ncd", 0, TextUnit.Line)); CheckEqual(2, TextBoundaries.Next("ab" + (char)0x2028 + "cd", 0, TextUnit.Line)); CheckEqual(2, TextBoundaries.Next("ab" + (char)0x85 + "cd", 0, TextUnit.Line)); } public void Word_NeverLeavesItsLine() { CheckEqual(2, TextBoundaries.Next("ab\ncd", 0, TextUnit.Word)); } public void Line_GoesToTheEndOfTheLine() { CheckEqual(2, TextBoundaries.Next("ab\ncd", 0, TextUnit.Line)); } public void Line_AtTheEndOfALine_StaysThere() { CheckEqual(2, TextBoundaries.Next("ab\ncd", 2, TextUnit.Line)); } public void Line_OnTheLastLine_GoesToTheEndOfTheText() { CheckEqual(5, TextBoundaries.Next("ab\ncd", 3, TextUnit.Line)); } public void Document_GoesToTheEndOfTheText() { CheckEqual(5, TextBoundaries.Next("ab\ncd", 1, TextUnit.Document)); } public void EmptyText_StaysAtZero() { CheckEqual(0, TextBoundaries.Next("", 0, TextUnit.Character)); CheckEqual(0, TextBoundaries.Next("", 0, TextUnit.Word)); CheckEqual(0, TextBoundaries.Next("", 0, TextUnit.Line)); } public void UndefinedUnit_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); TextBoundaries.Next("abc", 0, (TextUnit)99); } public void IndexPastTheEnd_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); TextBoundaries.Next("abc", 4, TextUnit.Character); } } public class PreviousTests : TestFixture { public void Character_StepsBackOne() { CheckEqual(2, TextBoundaries.Previous("abc", 3, TextUnit.Character)); } public void Character_AtTheStart_StaysThere() { CheckEqual(0, TextBoundaries.Previous("abc", 0, TextUnit.Character)); } public void Character_JumpsAWholePair() { CheckEqual(1, TextBoundaries.Previous("a" + Pair + "b", 3, TextUnit.Character)); } public void Character_FromInsideAPair_LeavesIt() { CheckEqual(0, TextBoundaries.Previous("a" + Pair + "b", 2, TextUnit.Character)); } public void Character_JumpsTheCombiningMarksWithIt() { CheckEqual(1, TextBoundaries.Previous("xe" + Acute + Acute + "y", 4, TextUnit.Character)); } public void Character_FromBetweenAMarkAndItsCharacter_LeavesBoth() { CheckEqual(0, TextBoundaries.Previous("e" + Acute + "x", 1, TextUnit.Character)); } public void Word_GoesToTheStartOfTheWordBeforeTheCaret() { CheckEqual(6, TextBoundaries.Previous("hello world", 11, TextUnit.Word)); CheckEqual(6, TextBoundaries.Previous("hello world", 8, TextUnit.Word)); CheckEqual(0, TextBoundaries.Previous("hello world", 6, TextUnit.Word)); } public void Word_AtTheStart_StaysThere() { CheckEqual(0, TextBoundaries.Previous("hello world", 0, TextUnit.Word)); } public void Word_SkipsTrailingSpaces() { CheckEqual(0, TextBoundaries.Previous("hi ", 4, TextUnit.Word)); } public void Word_KeepsPunctuationWithTheWordBeforeIt() { CheckEqual(4, TextBoundaries.Previous("hi, you", 7, TextUnit.Word)); CheckEqual(0, TextBoundaries.Previous("hi, you", 4, TextUnit.Word)); CheckEqual(0, TextBoundaries.Previous("hi, you", 2, TextUnit.Word)); } public void Word_StopsBetweenALetterAndAnIdeograph() { CheckEqual(5, TextBoundaries.Previous("ab" + Pair + " c", 6, TextUnit.Word)); CheckEqual(2, TextBoundaries.Previous("ab" + Pair + " c", 5, TextUnit.Word)); CheckEqual(0, TextBoundaries.Previous("ab" + Pair + " c", 2, TextUnit.Word)); } public void Character_JumpsAWholeCarriageReturnLineFeed() { CheckEqual(1, TextBoundaries.Previous("a\r\nb", 3, TextUnit.Character)); } public void Word_AtTheStartOfALine_GoesToTheEndOfTheLineBefore() { CheckEqual(2, TextBoundaries.Previous("ab\ncd", 3, TextUnit.Word)); } public void Word_AfterAMarkOnASpace_GoesBackBeforeTheSpace() { CheckEqual(1, TextBoundaries.Previous("a " + Acute + "b", 4, TextUnit.Word)); CheckEqual(0, TextBoundaries.Previous("a " + Acute + "b", 1, TextUnit.Word)); } public void Word_AtTheStartOfALineAfterCarriageReturnLineFeed_GoesToTheEndOfTheLineBefore() { CheckEqual(2, TextBoundaries.Previous("ab\r\ncd", 4, TextUnit.Word)); } public void Line_StopsAtAnyKindOfBreak() { CheckEqual(4, TextBoundaries.Previous("ab\r\ncd", 6, TextUnit.Line)); CheckEqual(3, TextBoundaries.Previous("ab" + (char)0x2029 + "cd", 5, TextUnit.Line)); } public void Word_NeverLeavesItsLine() { CheckEqual(3, TextBoundaries.Previous("ab\ncd", 5, TextUnit.Word)); } public void Line_GoesToTheStartOfTheLine() { CheckEqual(3, TextBoundaries.Previous("ab\ncd", 4, TextUnit.Line)); CheckEqual(0, TextBoundaries.Previous("ab\ncd", 2, TextUnit.Line)); } public void Line_AtTheStartOfALine_StaysThere() { CheckEqual(3, TextBoundaries.Previous("ab\ncd", 3, TextUnit.Line)); CheckEqual(0, TextBoundaries.Previous("ab\ncd", 0, TextUnit.Line)); } public void Document_GoesToTheStartOfTheText() { CheckEqual(0, TextBoundaries.Previous("ab\ncd", 4, TextUnit.Document)); } public void UndefinedUnit_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); TextBoundaries.Previous("abc", 1, (TextUnit)99); } public void NullText_Throws() { CheckThrow(typeof(ArgumentNullException)); TextBoundaries.Previous(null, 0, TextUnit.Character); } } public class FindTests : TestFixture { public static (int, int) Found(string text, int index, TextUnit unit) { StringView found = TextBoundaries.Find(text, index, unit); return (found.StartIndex, found.EndIndex); } public void Word_InTheMiddle_IsTheWholeWord() { CheckEqual((0, 5), Found("hello world", 2, TextUnit.Word)); } public void Word_AtItsFirstChar_IsTheWholeWord() { CheckEqual((6, 11), Found("hello world", 6, TextUnit.Word)); CheckEqual((0, 5), Found("hello world", 0, TextUnit.Word)); } public void Word_AtTheEndOfTheText_IsTheLastWord() { CheckEqual((6, 11), Found("hello world", 11, TextUnit.Word)); } public void Word_OnSpaces_IsTheRunOfSpaces() { CheckEqual((5, 8), Found("hello world", 6, TextUnit.Word)); CheckEqual((5, 6), Found("hello world", 5, TextUnit.Word)); } public void Word_OnIndentation_IsTheIndentation() { CheckEqual((0, 2), Found(" foo", 1, TextUnit.Word)); } public void Word_OnPunctuation_IsTheWordItIsAttachedTo() { CheckEqual((0, 4), Found("hi!? you", 3, TextUnit.Word)); } public void Word_WithAPairInIt_KeepsThePairWhole() { CheckEqual((2, 4), Found("ab" + Pair + " c", 3, TextUnit.Word)); } public void Word_OnALaterLine_IsFoundWithinThatLine() { CheckEqual((9, 11), Found("ab cd\nef gh", 9, TextUnit.Word)); } public void Word_InEmptyText_IsEmpty() { CheckEqual((0, 0), Found("", 0, TextUnit.Word)); } public void Word_OnABlankLine_IsEmpty() { CheckEqual((2, 2), Found("a\n\nb", 2, TextUnit.Word)); } public void Line_ExcludesTheNewline() { CheckEqual((3, 5), Found("ab\ncd\nef", 4, TextUnit.Line)); CheckEqual((0, 2), Found("ab\ncd\nef", 2, TextUnit.Line)); } public void Word_AfterAMarkOnASpace_StartsAfterTheMark() { CheckEqual((3, 4), Found("a " + Acute + "b", 3, TextUnit.Word)); } public void Word_OnASpaceWithAMark_IncludesTheMark() { CheckEqual((1, 3), Found("a " + Acute + "b", 1, TextUnit.Word)); } public void Line_ExcludesACarriageReturnLineFeed() { CheckEqual((4, 6), Found("ab\r\ncd", 5, TextUnit.Line)); CheckEqual((0, 2), Found("ab\r\ncd", 3, TextUnit.Line)); } public void Word_OnALineAfterACarriageReturnLineFeed_IsFoundWithinThatLine() { CheckEqual((10, 12), Found("ab cd\r\nef gh", 11, TextUnit.Word)); } public void Line_OnTheLastLine_RunsToTheEnd() { CheckEqual((6, 8), Found("ab\ncd\nef", 8, TextUnit.Line)); } public void Line_OnABlankLine_IsEmpty() { CheckEqual((2, 2), Found("a\n\nb", 2, TextUnit.Line)); } public void Document_IsEverything() { CheckEqual((0, 5), Found("ab\ncd", 3, TextUnit.Document)); } public void IsAViewOfTheTextItWasGiven() { string text = "hello world"; StringView found = TextBoundaries.Find(text, 8, TextUnit.Word); Check(ReferenceEquals(text, found.Text)); CheckEqual("world", found.ToString()); } public void Character_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); TextBoundaries.Find("abc", 1, TextUnit.Character); } public void IndexPastTheEnd_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); TextBoundaries.Find("abc", 4, TextUnit.Word); } } } }