using System;
using System.Collections.Generic;
using Amphora;
using Glass.Editing;
using UnitTestSharp;
namespace Glass.UnitTests.Editing
{
public class SplicedTextTests : TestFixture
{
public static readonly string LongLines = new string('a', 700) + "\n" + new string('b', 300);
public static readonly string[] Insertions =
{
"",
"x",
"ab\ncd",
"\n",
"\r",
"\r\n",
PagedTextTests.Pair,
"q" + new string('q', 599) + "\r\n" + new string('r', 300),
};
///
/// A splice and the string it should read as.
///
public class Case
{
public SplicedText Spliced;
public StringSource Expected;
public string ExpectedText;
public string Description;
}
public static Case Splice(ITextSource text, string whole, int start, int end, string inserted)
{
var spliced = new SplicedText();
spliced.Set(text, start, end, inserted);
string expected = whole.Substring(0, start) + inserted + whole.Substring(end);
return new Case
{
Spliced = spliced,
Expected = new StringSource(expected),
ExpectedText = expected,
Description = $"\"{whole.Length} chars, {start}..{end} for {inserted.Length} chars\"",
};
}
///
/// Splices at the start, the end, and inside, over a paged text and a string source.
///
public static List Cases(bool includeLongLines = false, bool pagedOnly = false,
int longestInsertion = int.MaxValue)
{
var returnMe = new List();
var wholes = new List(StringSourceTests.Samples);
if (includeLongLines)
{
wholes = new List { LongLines };
}
foreach (string whole in wholes)
{
var ranges = new List
{
new[] { 0, 0 },
new[] { 0, whole.Length },
new[] { whole.Length, whole.Length },
new[] { whole.Length / 2, whole.Length / 2 },
new[] { whole.Length / 3, whole.Length * 2 / 3 },
};
foreach (int[] range in ranges)
{
foreach (string inserted in Insertions)
{
if (includeLongLines && inserted.Length > 0 && inserted.Length < 6)
{
continue;
}
if (inserted.Length > longestInsertion)
{
continue;
}
returnMe.Add(Splice(PagedTextTests.Paged(whole), whole, range[0], range[1], inserted));
if (!pagedOnly)
{
returnMe.Add(Splice(new StringSource(whole), whole, range[0], range[1], inserted));
}
}
}
}
return returnMe;
}
public class SetTests : TestFixture
{
public void NullText_Throws()
{
CheckThrow(typeof(ArgumentNullException));
new SplicedText().Set(null, 0, 0, "");
}
public void NullReplacement_Throws()
{
CheckThrow(typeof(ArgumentNullException));
new SplicedText().Set(new StringSource("abc"), 0, 0, null);
}
public void StartPastEnd_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
new SplicedText().Set(new StringSource("abc"), 2, 1, "");
}
public void NegativeStart_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
new SplicedText().Set(new StringSource("abc"), -1, 1, "");
}
public void EndPastTheText_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
new SplicedText().Set(new StringSource("abc"), 1, 4, "");
}
}
public class UnderlyingTests : TestFixture
{
public void IsTheTextTheRangeWasSwappedIn()
{
var text = new StringSource("abcdef");
var spliced = new SplicedText();
spliced.Set(text, 1, 3, "x");
CheckTrue(ReferenceEquals(text, spliced.Underlying));
}
}
public class ReplacedStartTests : TestFixture
{
public void IsWhereTheSwappedRangeStarts()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abcdef"), 1, 3, "x");
CheckEqual(1, spliced.ReplacedStart);
}
}
public class ReplacedEndTests : TestFixture
{
public void IsWhereTheSwappedRangeEndsInTheTextUnderneath()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abcdef"), 1, 3, "x");
CheckEqual(3, spliced.ReplacedEnd);
}
}
public class ReplacementTests : TestFixture
{
public void IsTheStringTheRangeWasSwappedFor()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abcdef"), 1, 3, "xyz");
CheckEqual("xyz", spliced.Replacement);
}
}
public class VersionTests : TestFixture
{
public void SettingTheSameSplice_KeepsIt()
{
var text = new StringSource("abc");
var spliced = new SplicedText();
spliced.Set(text, 1, 2, "xy");
int version = spliced.Version;
spliced.Set(text, 1, 2, "xy");
CheckEqual(version, spliced.Version);
}
public void SettingTheSameCharsInANewString_KeepsIt()
{
var text = new StringSource("abc");
var spliced = new SplicedText();
spliced.Set(text, 1, 2, "xy");
int version = spliced.Version;
spliced.Set(text, 1, 2, new string(new[] { 'x', 'y' }));
CheckEqual(version, spliced.Version);
}
public void ADifferentReplacement_ChangesIt()
{
var text = new StringSource("abc");
var spliced = new SplicedText();
spliced.Set(text, 1, 2, "xy");
int version = spliced.Version;
spliced.Set(text, 1, 2, "xz");
Check(version != spliced.Version);
}
public void ADifferentRange_ChangesIt()
{
var text = new StringSource("abc");
var spliced = new SplicedText();
spliced.Set(text, 1, 2, "xy");
int version = spliced.Version;
spliced.Set(text, 0, 2, "xy");
Check(version != spliced.Version);
}
public void ADifferentText_ChangesIt()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abc"), 1, 2, "xy");
int version = spliced.Version;
spliced.Set(new StringSource("abc"), 1, 2, "xy");
Check(version != spliced.Version);
}
public void TheTextChangingUnderneath_ChangesIt()
{
var paged = PagedTextTests.Paged("abcdef");
var spliced = new SplicedText();
spliced.Set(paged, 1, 2, "xy");
int version = spliced.Version;
paged.Replace(5, 0, "!");
spliced.Set(paged, 1, 2, "xy");
Check(version != spliced.Version);
}
}
public class LengthTests : TestFixture
{
public void IsTheLengthOfTheSplicedString()
{
foreach (Case splice in Cases())
{
CheckEqual(splice.ExpectedText.Length, splice.Spliced.Length);
}
}
public void OfANewSplice_IsEmpty()
{
CheckEqual(0, new SplicedText().Length);
}
}
public class IndexerTests : TestFixture
{
public void ReadsTheSplicedChars()
{
foreach (Case splice in Cases())
{
for (int i = 0; i < splice.ExpectedText.Length; ++i)
{
CheckEqual(splice.ExpectedText[i], splice.Spliced[i]);
}
}
}
public void NegativeIndex_Throws()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abc"), 0, 0, "");
CheckThrow(typeof(ArgumentOutOfRangeException));
char ignored = spliced[-1];
}
public void IndexAtTheEnd_Throws()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abc"), 1, 2, "xy");
CheckThrow(typeof(ArgumentOutOfRangeException));
char ignored = spliced[4];
}
}
public class CopyToTests : TestFixture
{
public void CopiesEveryRange()
{
foreach (Case splice in Cases())
{
int length = splice.ExpectedText.Length;
int step = Math.Max(1, length / 7);
for (int start = 0; start <= length; start += step)
{
for (int count = 0; start + count <= length; count += step)
{
var copy = new char[count + 2];
splice.Spliced.CopyTo(start, count, copy, 1);
CheckEqual(splice.ExpectedText.Substring(start, count), new string(copy, 1, count));
}
}
}
}
public void ARangePastTheEnd_Throws()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abc"), 1, 2, "xy");
CheckThrow(typeof(ArgumentOutOfRangeException));
spliced.CopyTo(3, 2, new char[4], 0);
}
}
public class SliceTests : TestFixture
{
public void ViewsEveryRange()
{
foreach (Case splice in Cases())
{
int length = splice.ExpectedText.Length;
int step = Math.Max(1, length / 7);
char[] scratch = null;
for (int start = 0; start <= length; start += step)
{
for (int count = 0; start + count <= length; count += step)
{
StringView view = splice.Spliced.Slice(start, count, ref scratch);
CheckEqual(splice.ExpectedText.Substring(start, count), view.ToString());
}
}
}
}
public void ARangeInTheChangedPart_CopiesNothing()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abcdef"), 2, 4, "XYZ");
char[] scratch = null;
StringView view = spliced.Slice(3, 2, ref scratch);
CheckEqual("YZ", view.ToString());
CheckNull(scratch);
}
public void ARangeBeforeOrAfterTheChangedPart_CopiesNothing()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abcdef"), 2, 4, "XYZ");
char[] scratch = null;
CheckEqual("ab", spliced.Slice(0, 2, ref scratch).ToString());
CheckEqual("ef", spliced.Slice(5, 2, ref scratch).ToString());
CheckNull(scratch);
}
public void ARangeAcrossTheChangedPart_FillsTheScratch()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abcdef"), 2, 4, "XYZ");
char[] scratch = null;
CheckEqual("bXYZe", spliced.Slice(1, 5, ref scratch).ToString());
CheckEqual(5, scratch.Length);
}
public void ARangeAcrossTheChangedPart_KeepsAScratchThatIsBigEnough()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abcdef"), 2, 4, "XYZ");
var scratch = new char[20];
spliced.Slice(1, 5, ref scratch);
CheckEqual(20, scratch.Length);
}
public void ARangePastTheEnd_Throws()
{
var spliced = new SplicedText();
spliced.Set(new StringSource("abc"), 1, 2, "xy");
char[] scratch = null;
CheckThrow(typeof(ArgumentOutOfRangeException));
spliced.Slice(3, 2, ref scratch);
}
}
// The string source is the reference, since its own tests check it against the paged text.
public class AgreesWithAStringSourceTests : TestFixture
{
public void OnTheLineQueriesAtEveryIndex()
{
foreach (Case splice in Cases(pagedOnly: true, longestInsertion: 100))
{
int step = 1;
if (splice.ExpectedText.Length > 120)
{
step = 31;
}
for (int index = 0; index <= splice.ExpectedText.Length; index += step)
{
CheckEqual(splice.Expected.StartOfLine(index), splice.Spliced.StartOfLine(index));
CheckEqual(splice.Expected.EndOfLine(index), splice.Spliced.EndOfLine(index));
CheckEqual(splice.Expected.LengthAt(index), splice.Spliced.LengthAt(index));
CheckEqual(splice.Expected.LengthBefore(index), splice.Spliced.LengthBefore(index));
CheckEqual(splice.Expected.LineNumberOf(index), splice.Spliced.LineNumberOf(index));
}
}
}
public void OnTheLineQueriesInLongLines()
{
foreach (Case splice in Cases(includeLongLines: true))
{
int step = 37;
for (int index = 0; index <= splice.ExpectedText.Length; index += step)
{
CheckEqual(splice.Expected.StartOfLine(index), splice.Spliced.StartOfLine(index));
CheckEqual(splice.Expected.EndOfLine(index), splice.Spliced.EndOfLine(index));
CheckEqual(splice.Expected.LengthAt(index), splice.Spliced.LengthAt(index));
CheckEqual(splice.Expected.LengthBefore(index), splice.Spliced.LengthBefore(index));
}
}
}
public void OnTheLineCountAndTheStartOfEachLine()
{
foreach (Case splice in Cases())
{
CheckEqual(splice.Expected.HardLineCount, splice.Spliced.HardLineCount);
for (int line = 0; line < splice.Expected.HardLineCount; ++line)
{
CheckEqual(splice.Expected.StartOfLineNumber(line), splice.Spliced.StartOfLineNumber(line));
}
}
}
public static readonly string[] BreakyTexts =
{
"a\r\nb\r\n\nc\r",
"\r\n",
"\n\r",
"\r\r\n\n",
"ab",
"",
};
public static readonly string[] BreakyInsertions = { "", "x", "\r", "\n", "\r\n", "\n\r", "a\nb" };
public void OnTheLineCountAndNumbersAtEverySpliceAmongBreaks()
{
foreach (string whole in BreakyTexts)
{
for (int start = 0; start <= whole.Length; ++start)
{
for (int end = start; end <= whole.Length; ++end)
{
foreach (string inserted in BreakyInsertions)
{
Case splice = Splice(PagedTextTests.Paged(whole), whole, start, end, inserted);
CheckEqual(splice.Expected.HardLineCount, splice.Spliced.HardLineCount);
for (int index = 0; index <= splice.ExpectedText.Length; ++index)
{
CheckEqual(splice.Expected.LineNumberOf(index), splice.Spliced.LineNumberOf(index));
}
for (int line = 0; line < splice.Expected.HardLineCount; ++line)
{
CheckEqual(splice.Expected.StartOfLineNumber(line),
splice.Spliced.StartOfLineNumber(line));
}
}
}
}
}
}
[Thorough]
public void OnTheLineCountAndNumbersInATextOfManyLines()
{
string whole = string.Concat(System.Linq.Enumerable.Repeat("ab\r\ncd\nef\r", 40));
foreach (int start in new[] { 0, 7, 100, 199, 200, 201, 399, 400 })
{
foreach (string inserted in BreakyInsertions)
{
Case splice = Splice(PagedTextTests.Paged(whole), whole, start, Math.Min(400, start + 9),
inserted);
CheckEqual(splice.Expected.HardLineCount, splice.Spliced.HardLineCount);
for (int index = 0; index <= splice.ExpectedText.Length; index += 3)
{
CheckEqual(splice.Expected.LineNumberOf(index), splice.Spliced.LineNumberOf(index));
}
for (int line = 0; line < splice.Expected.HardLineCount; line += 3)
{
CheckEqual(splice.Expected.StartOfLineNumber(line),
splice.Spliced.StartOfLineNumber(line));
}
}
}
}
public void AfterTheSpliceIsSetAgain()
{
var spliced = new SplicedText();
var text = new StringSource("one\ntwo");
spliced.Set(text, 0, 3, "1\n2\n3");
CheckEqual(4, spliced.HardLineCount);
spliced.Set(text, 0, 3, "1");
CheckEqual(2, spliced.HardLineCount);
}
}
public class StartOfLineTests : TestFixture
{
public void AnIndexOutside_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
new SplicedText().StartOfLine(1);
}
}
public class EndOfLineTests : TestFixture
{
public void AnIndexOutside_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
new SplicedText().EndOfLine(-1);
}
}
public class LengthAtTests : TestFixture
{
public void AnIndexOutside_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
new SplicedText().LengthAt(1);
}
}
public class LengthBeforeTests : TestFixture
{
public void AnIndexOutside_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
new SplicedText().LengthBefore(1);
}
}
public class LineNumberOfTests : TestFixture
{
public void AnIndexOutside_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
new SplicedText().LineNumberOf(1);
}
}
public class StartOfLineNumberTests : TestFixture
{
public void PastTheLastLine_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
new SplicedText().StartOfLineNumber(1);
}
public void Negative_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
new SplicedText().StartOfLineNumber(-1);
}
}
}
}