using System; using System.Text; using Glass.Editing; using Glass.Typesetting; using Glass.UnitTests.Editing; using UnitTestSharp; namespace Glass.UnitTests.Typesetting { public class SplicedRowsTests : TestFixture { // Short lines, wrapped lines, empty lines and a CR LF, long enough to take several blocks of 16 chars. public static readonly string[] Bases = { "ab cd\nef gh ij kl mn op qr st uv\n\nwx\r\nyz aaaa bbbb cccc\nd", "aaaa bbbb cccc dddd eeee ffff\r\ngggg\rhhhh\n", "x", "", }; public static readonly string[] Replacements = { "", "x", "\n", "\r", "\r\n", "pq rs\ntu vw xy zz" }; /// /// Describes every question a spliced text's rows answer differently from a full layout of the spliced string. /// public static string Differences(string baseText, int start, int end, string replacement, int fitWidth = 100, int indexStep = 1) { PagedText paged = PagedTextTests.Paged(baseText); VisualLineIndex index = VisualLineIndexTests.NewIndex(fitWidth); var spliced = new SplicedText(); spliced.Set(paged, start, end, replacement); var rows = new SplicedRows(index); string whole = baseText.Remove(start, end - start).Insert(start, replacement); var layout = new VisualLineIndexTests.FullLayout(whole, fitWidth); var returnMe = new StringBuilder(); int total = layout.TotalRows; if (rows.TotalRows(spliced) != total) { returnMe.Append($"TotalRows {rows.TotalRows(spliced)} not {total}; "); } for (int i = 0; i <= whole.Length; i += indexStep) { if (layout.IsInsideCrLf(i)) { continue; } foreach (bool atLineEnd in new[] { false, true }) { if (rows.RowOf(spliced, i, atLineEnd) != layout.RowOf(i, atLineEnd)) { returnMe.Append($"RowOf({i},{atLineEnd}) {rows.RowOf(spliced, i, atLineEnd)} not ") .Append($"{layout.RowOf(i, atLineEnd)}; "); } } } for (int row = 0; row < total; ++row) { if (rows.IndexOfRow(spliced, row) != layout.IndexOfRow(row)) { returnMe.Append($"IndexOfRow({row}) {rows.IndexOfRow(spliced, row)} not ") .Append($"{layout.IndexOfRow(row)}; "); } } return returnMe.Length == 0 ? "" : $"splice({start},{end},{replacement.Length}): {returnMe}"; } public static readonly string Token = VisualLineIndexTests.Repeat("abcdefghij", 40); public static readonly string Prose = VisualLineIndexTests.Repeat("abcd efgh ", 40); // A splice that changes the length by a multiple of this keeps the rows after it where they were. public const int RowChars = 10; /// /// Describes how the rows of a spliced text, with a chunked line the splice is in, differ from a full layout. /// The rows through the end of the replacement must match it. The rows after it must too when exactBelow is /// set, and otherwise are within a row of it. /// public static string ChunkedDifferences(string baseText, int start, int end, string replacement, bool exactBelow, int fitWidth = 100, int indexStep = 1) { PagedText paged = PagedTextTests.Paged(baseText); VisualLineIndex index = VisualLineIndexTests.NewIndex(fitWidth, blockChars: 16, longLineChars: 48); VisualLineIndexTests.CountAll(index, paged); var spliced = new SplicedText(); spliced.Set(paged, start, end, replacement); var rows = new SplicedRows(index); string whole = baseText.Remove(start, end - start).Insert(start, replacement); var layout = new VisualLineIndexTests.FullLayout(whole, fitWidth); int after = start + replacement.Length; var returnMe = new StringBuilder(); int total = rows.TotalRows(spliced); if (total != layout.TotalRows && (exactBelow || Math.Abs(total - layout.TotalRows) > 1)) { returnMe.Append($"TotalRows {total} not {layout.TotalRows}; "); } for (int i = 0; i <= whole.Length; i += indexStep) { if (layout.IsInsideCrLf(i)) { continue; } foreach (bool atLineEnd in new[] { false, true }) { int expected = layout.RowOf(i, atLineEnd); int actual = rows.RowOf(spliced, i, atLineEnd); bool allowed = actual == expected || (i > after && !exactBelow && Math.Abs(actual - expected) <= 1); if (!allowed) { returnMe.Append($"RowOf({i},{atLineEnd}) {actual} not {expected}; "); } } } int exactRows = layout.RowOf(after, atLineEnd: false); for (int row = 0; row < layout.TotalRows && row < total; ++row) { int expected = layout.IndexOfRow(row); int actual = rows.IndexOfRow(spliced, row); bool allowed = actual == expected || (row > exactRows && !exactBelow && Math.Abs(actual - expected) <= 2 * RowChars); if (!allowed) { returnMe.Append($"IndexOfRow({row}) {actual} not {expected}; "); } } return returnMe.Length == 0 ? "" : $"splice({start},{end},{replacement.Length}): {returnMe}"; } /// /// Describes how the distances across the lines of a spliced text, for lines that don't wrap, differ from those /// of an index that counted the text with the splice in it. /// public static string WidthDifferences(string baseText, int start, int end, string replacement, SpriteFontDefinition font = null, TrailingSpaceMode trailingSpaces = TrailingSpaceMode.Hang, bool widestIsExact = true) { PagedText paged = PagedTextTests.Paged(baseText); VisualLineIndex index = VisualLineIndexTests.NewIndex(int.MaxValue, measureWidths: true, blockChars: 16, longLineChars: 48, trailingSpaces: trailingSpaces, font: font); VisualLineIndexTests.CountAll(index, paged); var spliced = new SplicedText(); spliced.Set(paged, start, end, replacement); var rows = new SplicedRows(index); string composed = baseText.Remove(start, end - start).Insert(start, replacement); PagedText reference = PagedTextTests.Paged(composed); VisualLineIndex referenceIndex = VisualLineIndexTests.NewIndex(int.MaxValue, measureWidths: true, blockChars: 16, longLineChars: 48, trailingSpaces: trailingSpaces, font: font); VisualLineIndexTests.CountAll(referenceIndex, reference); var returnMe = new StringBuilder(); if (rows.TotalRows(spliced) != referenceIndex.TotalRows(reference)) { returnMe.Append($"TotalRows {rows.TotalRows(spliced)} not {referenceIndex.TotalRows(reference)}; "); } if (widestIsExact && rows.EstimatedWidestLine(spliced) != referenceIndex.EstimatedWidestLine(reference)) { returnMe.Append($"EstimatedWidestLine {rows.EstimatedWidestLine(spliced)} not ") .Append($"{referenceIndex.EstimatedWidestLine(reference)}; "); } for (int row = 0; row < referenceIndex.TotalRows(reference); ++row) { if (rows.IndexOfRow(spliced, row) != referenceIndex.IndexOfRow(reference, row)) { returnMe.Append($"IndexOfRow({row}) {rows.IndexOfRow(spliced, row)} not ") .Append($"{referenceIndex.IndexOfRow(reference, row)}; "); } } for (int i = 0; i <= composed.Length; ++i) { if (i > 0 && i < composed.Length && composed[i - 1] == '\r' && composed[i] == '\n') { continue; } long expected = referenceIndex.WidthBefore(reference, i); if (rows.WidthBefore(spliced, i) != expected) { returnMe.Append($"WidthBefore({i}) {rows.WidthBefore(spliced, i)} not {expected}; "); } if (rows.RowOf(spliced, i, atLineEnd: false) != referenceIndex.RowOf(reference, i, atLineEnd: false)) { returnMe.Append($"RowOf({i}) {rows.RowOf(spliced, i, atLineEnd: false)} not ") .Append($"{referenceIndex.RowOf(reference, i, atLineEnd: false)}; "); } if (i == 0 || composed[i - 1] == '\n' || composed[i - 1] == '\r') { long lineWidth = referenceIndex.EstimatedLineWidth(reference, i); if (rows.EstimatedLineWidth(spliced, i) != lineWidth) { returnMe.Append($"EstimatedLineWidth({i}) {rows.EstimatedLineWidth(spliced, i)} not ") .Append($"{lineWidth}; "); } } } for (int line = 0; line < reference.HardLineCount; ++line) { int lineStart = reference.StartOfLineNumber(line); long lineWidth = referenceIndex.EstimatedLineWidth(reference, lineStart); for (long x = 0; x <= lineWidth + 20; x += 7) { int expected = referenceIndex.IndexAtWidth(reference, lineStart, x); int actual = rows.IndexAtWidth(spliced, lineStart, x); long expectedAway = Math.Abs(referenceIndex.WidthBefore(reference, expected) - x); long actualAway = Math.Abs(referenceIndex.WidthBefore(reference, actual) - x); if (actual != expected && actualAway != expectedAway) { returnMe.Append($"IndexAtWidth({lineStart}, {x}) {actual} not {expected}; "); } } } return returnMe.Length == 0 ? "" : $"splice({start},{end},{replacement.Length}): {returnMe}"; } public static string DifferencesForEverySplice(string replacement, int startStep, int indexStep) { var returnMe = new StringBuilder(); foreach (string baseText in Bases) { for (int start = 0; start <= baseText.Length; start += startStep) { foreach (int length in new[] { 0, 1, 4, 20, 1000 }) { int end = Math.Min(baseText.Length, start + length); returnMe.Append(Differences(baseText, start, end, replacement, indexStep: indexStep)); } } } return returnMe.ToString(); } public class CtorTests : TestFixture { public void RejectsNoIndex() { CheckThrow(typeof(ArgumentNullException)); new SplicedRows(null); } } public class TotalRowsTests : TestFixture { public void MatchesAFullLayout_WhenNothingIsInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[0], startStep: 3, indexStep: 100)); } public void MatchesAFullLayout_WhenACharIsInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[1], startStep: 3, indexStep: 100)); } public void MatchesAFullLayout_WhenALineFeedIsInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[2], startStep: 3, indexStep: 100)); } public void MatchesAFullLayout_WhenACarriageReturnIsInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[3], startStep: 3, indexStep: 100)); } public void MatchesAFullLayout_WhenACrLfIsInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[4], startStep: 3, indexStep: 100)); } public void MatchesAFullLayout_WhenWrappedLinesAreInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[5], startStep: 3, indexStep: 100)); } public void WhenAnInsertedCarriageReturnJoinsALineFeed_CountsOneBreak() { CheckEqual("", Differences("ab\ncd", 1, 2, "x\r", indexStep: 1)); } public void OnAChunkedLine_WhenARowOfCharsIsInserted_MatchesAFullLayout() { CheckEqual("", ChunkedDifferences(Token, 205, 205, "klmnopqrst", exactBelow: true)); } public void OnAChunkedLine_WhenARowOfCharsIsDeleted_MatchesAFullLayout() { CheckEqual("", ChunkedDifferences(Token, 205, 215, "", exactBelow: true)); } public void OnAChunkedLine_WhenCharsAreReplacedByAsManyChars_MatchesAFullLayout() { CheckEqual("", ChunkedDifferences(Token, 205, 208, "xyz", exactBelow: true)); } [Thorough] public void OnAChunkedLine_WhenTheRowsAfterAreShifted_IsWithinARowOfAFullLayout() { var returnMe = new StringBuilder(); foreach (int start in new[] { 0, 1, 16, 103, 205, 399, 400 }) { returnMe.Append(ChunkedDifferences(Token, start, start, "xyz", exactBelow: false)); returnMe.Append(ChunkedDifferences(Token, start, Math.Min(400, start + 30), "xy", exactBelow: false)); } CheckEqual("", returnMe.ToString()); } public void OnAChunkedLineOfProse_WhenTheRowsAfterAreShifted_IsWithinARowOfAFullLayout() { var returnMe = new StringBuilder(); foreach (int start in new[] { 0, 7, 103, 205, 399 }) { returnMe.Append(ChunkedDifferences(Prose, start, start, "wxyz", exactBelow: false)); returnMe.Append(ChunkedDifferences(Prose, start, Math.Min(400, start + 15), "x", exactBelow: false)); } CheckEqual("", returnMe.ToString()); } public void OnAChunkedLine_WhenALineBreakIsInserted_IsWithinARowOfAFullLayout() { CheckEqual("", ChunkedDifferences(Token, 205, 205, "xy\nz", exactBelow: false)); } [Thorough] public void OnAChunkedLine_AmongShortOnes_IsWithinARowOfAFullLayout() { string text = "ab\ncd\n" + Token + "\nef\n" + Prose + "\ngh"; var returnMe = new StringBuilder(); foreach (int start in new[] { 3, 200, 420 }) { returnMe.Append(ChunkedDifferences(text, start, start + 2, "xyz", exactBelow: false)); } CheckEqual("", returnMe.ToString()); } } public class RowOfTests : TestFixture { [Thorough] public void MatchesAFullLayoutAtEveryIndex_WhenNothingIsInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[0], startStep: 5, indexStep: 1)); } public void MatchesAFullLayoutAtEveryIndex_WhenACharIsInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[1], startStep: 5, indexStep: 1)); } [Thorough] public void MatchesAFullLayoutAtEveryIndex_WhenALineFeedIsInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[2], startStep: 5, indexStep: 1)); } [Thorough] public void MatchesAFullLayoutAtEveryIndex_WhenACarriageReturnIsInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[3], startStep: 5, indexStep: 1)); } [Thorough] public void MatchesAFullLayoutAtEveryIndex_WhenACrLfIsInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[4], startStep: 5, indexStep: 1)); } public void MatchesAFullLayoutAtEveryIndex_WhenWrappedLinesAreInserted() { CheckEqual("", DifferencesForEverySplice(Replacements[5], startStep: 5, indexStep: 1)); } public void MatchesAFullLayoutAtEveryIndex_AtOtherWidths() { var returnMe = new StringBuilder(); foreach (int width in new[] { 15, 45, 250 }) { returnMe.Append(Differences(Bases[0], 8, 30, Replacements[5], width)); returnMe.Append(Differences(Bases[1], 0, 0, Replacements[2], width)); returnMe.Append(Differences(Bases[1], 20, 38, Replacements[0], width)); } CheckEqual("", returnMe.ToString()); } } public class CheckpointAtOrBeforeTests : TestFixture { public static SplicedText Spliced(out PagedText paged, out VisualLineIndex index, out SplicedRows rows) { paged = PagedTextTests.Paged(Token); index = VisualLineIndexTests.NewIndex(blockChars: 16, longLineChars: 48); VisualLineIndexTests.CountAll(index, paged); rows = new SplicedRows(index); var returnMe = new SplicedText(); returnMe.Set(paged, 205, 205, "xyz"); return returnMe; } public void BeforeTheSplice_IsTheChunkStartUnderneath() { SplicedText spliced = Spliced(out PagedText paged, out VisualLineIndex index, out SplicedRows rows); CheckEqual(index.CheckpointAtOrBefore(paged, 100), rows.CheckpointAtOrBefore(spliced, 100)); } public void InTheStretchTheSpliceTouches_IsTheStartOfTheStretch() { SplicedText spliced = Spliced(out PagedText paged, out VisualLineIndex index, out SplicedRows rows); int stretchStart = index.CheckpointAtOrBefore(paged, 205); CheckEqual(stretchStart, rows.CheckpointAtOrBefore(spliced, 206)); CheckEqual(stretchStart, rows.CheckpointAtOrBefore(spliced, 208)); CheckTrue(stretchStart <= 205 && stretchStart > 205 - CheckpointAtOrBeforeTests.ChunkBound); } public const int ChunkBound = VisualLineIndexTests.CheckpointAtOrBeforeTests.ChunkBound; public void AfterTheStretch_IsTheChunkStartUnderneathMovedWithTheText() { SplicedText spliced = Spliced(out PagedText paged, out VisualLineIndex index, out SplicedRows rows); CheckEqual(index.CheckpointAtOrBefore(paged, 300) + 3, rows.CheckpointAtOrBefore(spliced, 303)); } public void ShortAfterTheStretch_IsNotBeforeTheStretchEnds() { SplicedText spliced = Spliced(out _, out _, out SplicedRows rows); CheckTrue(rows.CheckpointAtOrBefore(spliced, 209) <= 209); CheckTrue(rows.CheckpointAtOrBefore(spliced, 209) >= 205 - ChunkBound); } public void OnAShortLine_IsTheStartOfTheLine() { PagedText paged = PagedTextTests.Paged("ab cd\nef gh ij"); var spliced = new SplicedText(); spliced.Set(paged, 8, 9, "xyz"); var rows = new SplicedRows(VisualLineIndexTests.NewIndex(longLineChars: 48)); CheckEqual(6, rows.CheckpointAtOrBefore(spliced, 11)); CheckEqual(0, rows.CheckpointAtOrBefore(spliced, 3)); } } public class IndexOfRowTests : TestFixture { public void ARowAfterTheStretchOfAChunkedLine_KeepsTheStartItHadUnderneathMovedWithTheText() { PagedText paged = PagedTextTests.Paged(Token); VisualLineIndex index = VisualLineIndexTests.NewIndex(blockChars: 16, longLineChars: 48); VisualLineIndexTests.CountAll(index, paged); var spliced = new SplicedText(); spliced.Set(paged, 205, 205, "xyz"); var rows = new SplicedRows(index); CheckEqual(rows.IndexOfRow(spliced, 31), rows.IndexOfRow(spliced, 30) + RowChars); CheckTrue(rows.IndexOfRow(spliced, 30) % RowChars == 3); } public void AFarRow_LeadsToTheLastRowsStart() { PagedText paged = PagedTextTests.Paged("ab\ncd"); var spliced = new SplicedText(); spliced.Set(paged, 1, 2, "x\ny"); var rows = new SplicedRows(VisualLineIndexTests.NewIndex()); CheckEqual(5, rows.IndexOfRow(spliced, 500)); } public void ANegativeRow_LeadsToTheStart() { PagedText paged = PagedTextTests.Paged("ab\ncd"); var spliced = new SplicedText(); spliced.Set(paged, 1, 2, "x\ny"); var rows = new SplicedRows(VisualLineIndexTests.NewIndex()); CheckEqual(0, rows.IndexOfRow(spliced, -3)); } } // A line past the long length with a short one either side, and spaces in it to hang or to break on. public static readonly string WideLines = "ab\n" + VisualLineIndexTests.Repeat("abcd efgh ", 20) + "\ncd"; public static readonly string KernedLines = "ab\n" + VisualLineIndexTests.Repeat("AVAWTa ", 40) + "\ncd"; public static readonly string SpacedLines = "ab\n" + VisualLineIndexTests.Repeat("abcd efgh ", 20) + " \ncd"; public class WidthBeforeTests : TestFixture { public void InALineCountedInChunks_AfterACharIsInserted_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(WideLines, 100, 100, "xy")); } public void InALineCountedInChunks_AfterCharsAreReplaced_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(WideLines, 100, 140, "wxyz!")); } public void InALineCountedInChunks_AfterCharsAreDeleted_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(WideLines, 30, 180, "")); } public void InALineCountedInChunks_AtTheStartOfTheLine_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(WideLines, 3, 3, "xy")); } public void InALineCountedInChunks_AtTheEndOfTheLine_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(WideLines, 203, 203, "xy")); } public void InALineCountedInChunks_AtEveryCharOfTheLine_IsWhereTheSplicedTextPutsIt() { var returnMe = new StringBuilder(); for (int start = 3; start <= 203; start += 7) { returnMe.Append(WidthDifferences(WideLines, start, Math.Min(203, start + 2), "q")); } CheckEqual("", returnMe.ToString()); } public void InALineCountedInChunks_WithKerning_IsWhereTheSplicedTextPutsIt() { SpriteFontDefinition kerned = VisualLineIndexTests.WidthChunkedLinesTests.Kerned; var returnMe = new StringBuilder(); foreach (int start in new[] { 3, 20, 21, 100, 283 }) { returnMe.Append(WidthDifferences(KernedLines, start, start, "AVT", kerned)); returnMe.Append(WidthDifferences(KernedLines, start, Math.Min(283, start + 3), "WA", kerned)); } CheckEqual("", returnMe.ToString()); } public void InALineCountedInChunks_WithSpacesAtItsEnd_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(SpacedLines, 100, 100, "xy")); CheckEqual("", WidthDifferences(SpacedLines, 203, 205, "q")); CheckEqual("", WidthDifferences(SpacedLines, 207, 207, " ")); } public void InALineCountedInChunks_WhenSpacesWrap_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(SpacedLines, 100, 100, "xy", trailingSpaces: TrailingSpaceMode.Wrap)); CheckEqual("", WidthDifferences(SpacedLines, 203, 205, "q", trailingSpaces: TrailingSpaceMode.Wrap)); } public void InAShortLine_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(WideLines, 1, 1, "xy", widestIsExact: false)); CheckEqual("", WidthDifferences(WideLines, 206, 206, "xy")); } public void WhenTheSpliceHasABreak_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(WideLines, 100, 100, "x\ny")); } public void WhenTheSpliceJoinsLines_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(WideLines, 1, 206, "zz", widestIsExact: false)); CheckEqual("", WidthDifferences(WideLines, 200, 206, "zz")); } public void AtTheEndOfTheText_IsWhereTheSplicedTextPutsIt() { CheckEqual("", WidthDifferences(WideLines, 283 - 100, 283 - 100, "xy")); CheckEqual("", WidthDifferences(WideLines, WideLines.Length, WideLines.Length, "xy")); CheckEqual("", WidthDifferences(WideLines, 100, WideLines.Length, "xy")); } } public class IndexAtWidthTests : TestFixture { public void InALineCountedInChunks_IsTheStopNearestInTheSplicedText() { var returnMe = new StringBuilder(); foreach (int start in new[] { 3, 50, 120, 203 }) { returnMe.Append(WidthDifferences(WideLines, start, Math.Min(203, start + 5), "xyz")); } CheckEqual("", returnMe.ToString()); } public void InALineCountedInChunks_WithAWiderReplacement_IsTheStopNearestInTheSplicedText() { CheckEqual("", WidthDifferences(WideLines, 60, 61, VisualLineIndexTests.Repeat("W", 30))); } } public class EstimatedLineWidthTests : TestFixture { public void OfALineCountedInChunks_IsTheWidthOfTheSplicedLine() { CheckEqual("", WidthDifferences(WideLines, 100, 110, "xy")); } public void OfALineCountedInChunks_WhenTheSpliceChangesTheHangingSpaces_IsTheWidthOfTheSplicedLine() { CheckEqual("", WidthDifferences(SpacedLines, 203, 207, "q")); CheckEqual("", WidthDifferences(SpacedLines, 190, 207, "")); } public void OfALineBeforeAndAfterTheSplice_IsTheWidthUnderneath() { CheckEqual("", WidthDifferences(WideLines, 206, 206, "xyz")); CheckEqual("", WidthDifferences("ab\n" + WideLines, 100, 100, "x")); } } public class EstimatedWidestLineTests : TestFixture { public void WhenTheSpliceNarrowsTheWidestLine_IsTheSplicedLinesWidth() { CheckEqual("", WidthDifferences(WideLines, 20, 190, "q")); } public void WhenTheSpliceWidensTheWidestLine_IsTheSplicedLinesWidth() { CheckEqual("", WidthDifferences(WideLines, 100, 100, VisualLineIndexTests.Repeat("W", 40))); } public void WhenAnotherLineIsWider_IsThatLinesWidth() { string text = WideLines + "\n" + VisualLineIndexTests.Repeat("WWWW ", 60); CheckEqual("", WidthDifferences(text, 100, 150, "q")); } } public class WidestLineTests : TestFixture { public void WhenTheSpliceNarrowsALineCountedInChunks_IsTheSplicedLinesWidth() { PagedText paged = PagedTextTests.Paged(WideLines); VisualLineIndex index = VisualLineIndexTests.NewIndex(int.MaxValue, measureWidths: true, blockChars: 16, longLineChars: 48); VisualLineIndexTests.CountAll(index, paged); var spliced = new SplicedText(); spliced.Set(paged, 20, 190, "q"); var rows = new SplicedRows(index); long expected = index.WidthBefore(paged, 20) + 10 + index.WidestLine(paged) - index.WidthBefore(paged, 190); CheckEqual(expected, rows.WidestLine(spliced)); } public static VisualLineIndex NoWrapIndex() { return VisualLineIndexTests.NewIndex(int.MaxValue, measureWidths: true); } public void CountsAWiderLineTheSpliceBrings() { PagedText paged = PagedTextTests.Paged("ab\ncd\nef"); var spliced = new SplicedText(); spliced.Set(paged, 3, 3, "abcdefgh"); var rows = new SplicedRows(NoWrapIndex()); CheckEqual(100, rows.WidestLine(spliced)); } public void StillCountsALineTheSpliceReplaced() { PagedText paged = PagedTextTests.Paged("ab\nabcdefgh\nef"); var spliced = new SplicedText(); spliced.Set(paged, 3, 11, "x"); var rows = new SplicedRows(NoWrapIndex()); CheckEqual(80, rows.WidestLine(spliced)); } public void IsTheTextUnderneathsWidestLine_WhenTheSpliceIsNarrower() { PagedText paged = PagedTextTests.Paged("ab\ncdefg\nef"); var spliced = new SplicedText(); spliced.Set(paged, 0, 0, "x"); var rows = new SplicedRows(NoWrapIndex()); CheckEqual(50, rows.WidestLine(spliced)); } } } }