using Amphora; using Glass.Editing; using System; using System.Collections.Generic; using UnitTestSharp; namespace Glass.UnitTests.Editing { public class ReplacementReaderTests : TestFixture { public const string Grin = "\U0001F600"; /// /// Hands out a text in chunks of a fixed size and remembers where each was asked for. /// public class ScriptedChunks : IChunkedText { private readonly string text; private readonly int chunkSize; public List Asked { get; } = new List(); public ScriptedChunks(string text, int chunkSize) { this.text = text; this.chunkSize = chunkSize; } public int Length => text.Length; public StringView GetChunk(int start) { Asked.Add(start); return new StringView(text, start, Math.Min(text.Length, start + chunkSize)); } } public static ReplacementReader ReaderWithRoom(int room, IEditLimit limit = null) { return new ReplacementReader(new StringSource(""), start: 0, length: 0, room, limit, pageSize: 8); } public static string Digits(int count) { return SourceChunksTests.Digits(count); } public class CtorTests : TestFixture { public void NullText_Throws() { CheckThrow(typeof(ArgumentNullException)); new ReplacementReader(null, start: 0, length: 0, room: 5, limit: null, pageSize: 8); } public void StartsWithNothingCut() { CheckFalse(ReaderWithRoom(5).WasTruncated); } } public class ReadTests : TestFixture { public void NullSource_Throws() { CheckThrow(typeof(ArgumentNullException)); ReaderWithRoom(5).Read(null); } public void AnEmptyChunk_Throws() { var chunks = new EditBufferTests.BrokenChunks(4, ""); CheckThrow(typeof(InvalidOperationException)); ReaderWithRoom(100).Read(chunks); } public void AChunkPastTheLength_Throws() { var chunks = new EditBufferTests.BrokenChunks(4, "toolong"); CheckThrow(typeof(InvalidOperationException)); ReaderWithRoom(100).Read(chunks); } public void EmptySource_GivesNoText() { var chunks = new ScriptedChunks("", 3); PagedText pages = ReaderWithRoom(5).Read(chunks); CheckEqual(0, pages.Length); CheckEqual(0, chunks.Asked.Count); } public void JoinsTheChunks() { PagedText pages = ReaderWithRoom(100).Read(new ScriptedChunks("hello, world", 5)); CheckEqual("hello, world", pages.ToString()); } public void AsksForEachChunkWhereTheLastEnded() { var chunks = new ScriptedChunks("hello, world", 5); ReaderWithRoom(100).Read(chunks); CheckEqual(new[] { 0, 5, 10 }, chunks.Asked.ToArray()); } public void BuildsPagesOfTheGivenSize() { PagedText pages = ReaderWithRoom(100).Read(new ScriptedChunks(Digits(40), 40)); CheckEqual(8, pages.PageSize); } public void DropsControlCharsInEveryChunk() { PagedText pages = ReaderWithRoom(100).Read(new ScriptedChunks("a\u0001b\u0002c\u0003d", 2)); CheckEqual("abcd", pages.ToString()); } public void KeepsHardBreaks() { PagedText pages = ReaderWithRoom(100).Read(new ScriptedChunks("a\nb\r\nc", 2)); CheckEqual("a\nb\r\nc", pages.ToString()); } public void ACrLfSplitAcrossChunks_StaysTogether() { PagedText pages = ReaderWithRoom(100).Read(new ScriptedChunks("ab\r\ncd", 3)); CheckEqual("ab\r\ncd", pages.ToString()); } public void APairSplitAcrossChunks_IsKept() { PagedText pages = ReaderWithRoom(100).Read(new ScriptedChunks("ab" + Grin + "cd", 3)); CheckEqual("ab" + Grin + "cd", pages.ToString()); } public void APairSplitAcrossChunks_WhoseChunkIsOneChar_IsKept() { PagedText pages = ReaderWithRoom(100).Read(new ScriptedChunks("a" + Grin + "b", 1)); CheckEqual("a" + Grin + "b", pages.ToString()); } public void AHighSurrogateAtAChunkEndWithNoLowAfter_IsDropped() { PagedText pages = ReaderWithRoom(100).Read(new ScriptedChunks("ab\uD83Dcd", 3)); CheckEqual("abcd", pages.ToString()); } public void AHighSurrogateAtTheEndOfTheText_IsDropped() { PagedText pages = ReaderWithRoom(100).Read(new ScriptedChunks("ab\uD83D", 3)); CheckEqual("ab", pages.ToString()); } public void ALoneLowSurrogate_IsDropped() { PagedText pages = ReaderWithRoom(100).Read(new ScriptedChunks("ab\uDE00cd", 3)); CheckEqual("abcd", pages.ToString()); } public void TextThatFitsExactly_IsNotCut() { var reader = ReaderWithRoom(5); PagedText pages = reader.Read(new ScriptedChunks("hello", 2)); CheckEqual("hello", pages.ToString()); CheckFalse(reader.WasTruncated); } public void TextPastTheRoom_IsCutThere() { var reader = ReaderWithRoom(7); PagedText pages = reader.Read(new ScriptedChunks("hello, world", 5)); CheckEqual("hello, ", pages.ToString()); CheckTrue(reader.WasTruncated); } public void StopsAskingOnceTheRoomIsFull() { var chunks = new ScriptedChunks(Digits(60), 3); ReaderWithRoom(5).Read(chunks); CheckEqual(new[] { 0, 3 }, chunks.Asked.ToArray()); } public void ARoomOfNone_ReadsOneChunkAndTakesNothing() { var reader = ReaderWithRoom(0); var chunks = new ScriptedChunks("hello", 3); PagedText pages = reader.Read(chunks); CheckEqual(0, pages.Length); CheckTrue(reader.WasTruncated); CheckEqual(new[] { 0 }, chunks.Asked.ToArray()); } public void ARoomOfNone_WithALineFeedFirst_TakesNothing() { PagedText pages = ReaderWithRoom(0).Read(new ScriptedChunks("\nabc", 4)); CheckEqual(0, pages.Length); } public void ACutThroughAPair_DropsTheWholePair() { var reader = ReaderWithRoom(3); PagedText pages = reader.Read(new ScriptedChunks("ab" + Grin + "cd", 10)); CheckEqual("ab", pages.ToString()); CheckTrue(reader.WasTruncated); } public void ACutThroughAPairSplitAcrossChunks_DropsTheWholePair() { var reader = ReaderWithRoom(3); PagedText pages = reader.Read(new ScriptedChunks("ab" + Grin + "cd", 3)); CheckEqual("ab", pages.ToString()); CheckTrue(reader.WasTruncated); } public void ACutThroughACrLf_DropsTheWholeBreak() { var reader = ReaderWithRoom(2); PagedText pages = reader.Read(new ScriptedChunks("a\r\nb", 10)); CheckEqual("a", pages.ToString()); } public void ACutBetweenTheCrAndLfOfTwoChunks_DropsTheWholeBreak() { var reader = ReaderWithRoom(2); PagedText pages = reader.Read(new ScriptedChunks("a\r\nb", 2)); CheckEqual("a", pages.ToString()); CheckTrue(reader.WasTruncated); } public void ACutAfterALoneCr_KeepsIt() { var reader = ReaderWithRoom(2); PagedText pages = reader.Read(new ScriptedChunks("a\rb", 2)); CheckEqual("a\r", pages.ToString()); } public void TheRoomCountsCleanedText() { var reader = ReaderWithRoom(3); PagedText pages = reader.Read(new ScriptedChunks("a\u0001b\u0001c\u0001d", 2)); CheckEqual("abc", pages.ToString()); CheckTrue(reader.WasTruncated); } public void ATextEndingInDroppedCharsAfterAFullRoom_IsNotCut() { var reader = ReaderWithRoom(3); PagedText pages = reader.Read(new ScriptedChunks("abc\u0001\u0001", 3)); CheckEqual("abc", pages.ToString()); CheckFalse(reader.WasTruncated); } } public class WithALimitTests : TestFixture { public void ALimitThatAllowsAll_KeepsTheText() { var limit = new EditBufferTests.FixedLimit(int.MaxValue); var reader = ReaderWithRoom(100, limit); PagedText pages = reader.Read(new ScriptedChunks("hello, world", 5)); CheckEqual("hello, world", pages.ToString()); CheckFalse(reader.WasTruncated); } public void ALimitIsAskedAboutShortTextOnce_WithAllOfIt() { var limit = new EditBufferTests.FixedLimit(int.MaxValue); ReaderWithRoom(100, limit).Read(new ScriptedChunks("hello, world", 5)); CheckEqual(1, limit.Asked); CheckEqual("hello, world", limit.ReplacementSeen); } public void ALimitIsAskedWhereTheReplacementGoes() { var limit = new EditBufferTests.FixedLimit(int.MaxValue); var reader = new ReplacementReader(new StringSource("0123456789"), start: 2, length: 3, room: 100, limit, pageSize: 8); reader.Read(new ScriptedChunks("ab", 5)); CheckEqual("0123456789", limit.TextSeen); CheckEqual(2, limit.StartSeen); CheckEqual(3, limit.LengthSeen); } public void AnEmptyReplacement_DoesNotAskTheLimit() { var limit = new EditBufferTests.FixedLimit(int.MaxValue); ReaderWithRoom(100, limit).Read(new ScriptedChunks("\u0001\u0002", 1)); CheckEqual(0, limit.Asked); } public void ALimitThatCuts_KeepsWhatFits() { var limit = new EditBufferTests.FixedLimit(4); var reader = ReaderWithRoom(100, limit); PagedText pages = reader.Read(new ScriptedChunks("hello, world", 5)); CheckEqual("hell", pages.ToString()); CheckTrue(reader.WasTruncated); } public void ALimitThatAllowsNone_GivesNoText() { var limit = new EditBufferTests.FixedLimit(0); var reader = ReaderWithRoom(100, limit); PagedText pages = reader.Read(new ScriptedChunks("hello", 5)); CheckEqual(0, pages.Length); CheckTrue(reader.WasTruncated); } public void ALimitThatCutsThroughAPair_DropsTheWholePair() { var limit = new EditBufferTests.FixedLimit(3); PagedText pages = ReaderWithRoom(100, limit).Read(new ScriptedChunks("ab" + Grin + "cd", 10)); CheckEqual("ab", pages.ToString()); } public void AskedAtDoublingSizes_OverALongText() { var limit = new EditBufferTests.FixedLimit(int.MaxValue); int chunk = 1 << 14; ReaderWithRoom(int.MaxValue, limit).Read(new ScriptedChunks(Digits(chunk * 3), chunk)); CheckEqual(3, limit.Asked); } public void ALimitThatCutsEarly_StopsAskingForChunks() { var limit = new EditBufferTests.FixedLimit(100); var chunks = new ScriptedChunks(Digits((1 << 14) * 5), 1 << 14); PagedText pages = ReaderWithRoom(int.MaxValue, limit).Read(chunks); CheckEqual(100, pages.Length); CheckEqual(new[] { 0 }, chunks.Asked.ToArray()); } public void ARoomOfNone_WithALineFeedFirst_TakesNothing() { var limit = new EditBufferTests.FixedLimit(int.MaxValue); PagedText pages = ReaderWithRoom(0, limit).Read(new ScriptedChunks("\nabc", 4)); CheckEqual(0, pages.Length); } public void ALimitTighterThanTheRoom_CutsWhatTheRoomLetsIn() { var limit = new EditBufferTests.FixedLimit(2); var reader = ReaderWithRoom(5, limit); PagedText pages = reader.Read(new ScriptedChunks("hello, world", 10)); CheckEqual("he", pages.ToString()); CheckTrue(reader.WasTruncated); } public void TheRoomAndALimit_BothApply() { var limit = new EditBufferTests.FixedLimit(4); var reader = ReaderWithRoom(3, limit); PagedText pages = reader.Read(new ScriptedChunks("hello", 5)); CheckEqual("hel", pages.ToString()); CheckTrue(reader.WasTruncated); } public void ACrCutOffByTheRoomBeforeItsLf_IsDropped() { var limit = new EditBufferTests.FixedLimit(int.MaxValue); var reader = ReaderWithRoom(2, limit); PagedText pages = reader.Read(new ScriptedChunks("a\r\nb", 2)); CheckEqual("a", pages.ToString()); } } } }