Remove unused/unpurposeful classes.

- Several classes are legacy and left over from many changes in the Razor code base. This changeset removes those classes.
- Also moved several corresponding test cases

#778
This commit is contained in:
N. Taylor Mullen 2016-06-09 15:24:22 -07:00
parent 2256138650
commit 6e4f15bad4
11 changed files with 1 additions and 1089 deletions

View File

@ -1,165 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DEBUG
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Text;
namespace Microsoft.AspNetCore.Razor
{
internal static class RazorDebugHelpers
{
private static bool _outputDebuggingEnabled = IsDebuggingEnabled();
private static readonly Dictionary<char, string> _printableEscapeChars = new Dictionary<char, string>
{
{ '\0', "\\0" },
{ '\\', "\\\\" },
{ '\'', "'" },
{ '\"', "\\\"" },
{ '\a', "\\a" },
{ '\b', "\\b" },
{ '\f', "\\f" },
{ '\n', "\\n" },
{ '\r', "\\r" },
{ '\t', "\\t" },
{ '\v', "\\v" },
};
internal static bool OutputDebuggingEnabled
{
get { return _outputDebuggingEnabled; }
}
internal static void WriteDebugTree(string sourceFile, Block document, PartialParseResult result, TextChange change, RazorEditorParser parser, bool treeStructureChanged)
{
if (!OutputDebuggingEnabled)
{
return;
}
RunTask(() =>
{
var outputFileName = Normalize(sourceFile) + "_tree";
var outputPath = Path.Combine(Path.GetDirectoryName(sourceFile), outputFileName);
var treeBuilder = new StringBuilder();
WriteTree(document, treeBuilder);
treeBuilder.AppendLine();
treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Last Change: {0}", change);
treeBuilder.AppendLine();
treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Normalized To: {0}", change.Normalize());
treeBuilder.AppendLine();
treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Partial Parse Result: {0}", result);
treeBuilder.AppendLine();
if ((result & PartialParseResult.Rejected) == PartialParseResult.Rejected)
{
treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Tree Structure Changed: {0}", treeStructureChanged);
treeBuilder.AppendLine();
}
if ((result & PartialParseResult.AutoCompleteBlock) == PartialParseResult.AutoCompleteBlock)
{
treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Auto Complete Insert String: \"{0}\"", parser.GetAutoCompleteString());
treeBuilder.AppendLine();
}
File.WriteAllText(outputPath, treeBuilder.ToString());
});
}
private static void WriteTree(SyntaxTreeNode node, StringBuilder treeBuilder, int depth = 0)
{
if (node == null)
{
return;
}
if (depth > 1)
{
WriteIndent(treeBuilder, depth);
}
if (depth > 0)
{
treeBuilder.Append("|-- ");
}
treeBuilder.AppendLine(ConvertEscapseSequences(node.ToString()));
if (node.IsBlock)
{
foreach (SyntaxTreeNode child in ((Block)node).Children)
{
WriteTree(child, treeBuilder, depth + 1);
}
}
}
private static void RunTask(Action action)
{
Task.Factory.StartNew(() =>
{
try
{
action();
}
catch
{
// Catch all errors since this is just a debug helper
}
});
}
private static void WriteIndent(StringBuilder sb, int depth)
{
for (int i = 0; i < (depth - 1) * 4; ++i)
{
if (i % 4 == 0)
{
sb.Append("|");
}
else
{
sb.Append(" ");
}
}
}
private static string Normalize(string path)
{
return Path.GetFileName(path).Replace('.', '_');
}
private static string ConvertEscapseSequences(string value)
{
var sb = new StringBuilder();
foreach (var ch in value)
{
sb.Append(GetCharValue(ch));
}
return sb.ToString();
}
private static string GetCharValue(char ch)
{
string value;
if (_printableEscapeChars.TryGetValue(ch, out value))
{
return value;
}
return ch.ToString();
}
private static bool IsDebuggingEnabled()
{
bool enabled;
return Boolean.TryParse(Environment.GetEnvironmentVariable("RAZOR_DEBUG"), out enabled) && enabled;
}
}
}
#endif

View File

@ -83,7 +83,7 @@ namespace Microsoft.AspNetCore.Razor
}
// See ParseTemplate(ITextBuffer, CancellationToken?),
// this overload simply wraps a TextReader in a TextBuffer (see ITextBuffer and BufferingTextReader)
// this overload simply wraps a TextReader in a TextBuffer (see ITextBuffer)
public ParserResults ParseTemplate(TextReader input, string sourceFileName)
{
if (input == null)

View File

@ -1,201 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.AspNetCore.Razor.Utils;
namespace Microsoft.AspNetCore.Razor.Text
{
public class BufferingTextReader : LookaheadTextReader
{
private Stack<BacktrackContext> _backtrackStack = new Stack<BacktrackContext>();
private int _currentBufferPosition;
private int _currentCharacter;
private SourceLocationTracker _locationTracker;
public BufferingTextReader(TextReader source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
InnerReader = source;
_locationTracker = new SourceLocationTracker();
UpdateCurrentCharacter();
}
internal StringBuilder Buffer { get; set; }
internal bool Buffering { get; set; }
internal TextReader InnerReader { get; private set; }
public override SourceLocation CurrentLocation
{
get { return _locationTracker.CurrentLocation; }
}
protected virtual int CurrentCharacter
{
get { return _currentCharacter; }
}
public override int Read()
{
var ch = CurrentCharacter;
NextCharacter();
return ch;
}
// TODO: Optimize Read(char[],int,int) to copy direct from the buffer where possible
public override int Peek()
{
return CurrentCharacter;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
InnerReader.Dispose();
}
base.Dispose(disposing);
}
public override IDisposable BeginLookahead()
{
// Is this our first lookahead?
if (Buffer == null)
{
// Yes, setup the backtrack buffer
Buffer = new StringBuilder();
}
if (!Buffering)
{
// We're not already buffering, so we need to expand the buffer to hold the first character
ExpandBuffer();
Buffering = true;
}
// Mark the position to return to when we backtrack
// Use the closures and the "using" statement rather than an explicit stack
var context = new BacktrackContext()
{
BufferIndex = _currentBufferPosition,
Location = CurrentLocation
};
_backtrackStack.Push(context);
return new DisposableAction(() =>
{
EndLookahead(context);
});
}
// REVIEW: This really doesn't sound like the best name for this...
public override void CancelBacktrack()
{
if (_backtrackStack.Count == 0)
{
throw new InvalidOperationException(RazorResources.CancelBacktrack_Must_Be_Called_Within_Lookahead);
}
// Just pop the current backtrack context so that when the lookahead ends, it won't be backtracked
_backtrackStack.Pop();
}
private void EndLookahead(BacktrackContext context)
{
// If the specified context is not the one on the stack, it was popped by a call to DoNotBacktrack
if (_backtrackStack.Count > 0 && ReferenceEquals(_backtrackStack.Peek(), context))
{
_backtrackStack.Pop();
_currentBufferPosition = context.BufferIndex;
_locationTracker.CurrentLocation = context.Location;
UpdateCurrentCharacter();
}
}
protected virtual void NextCharacter()
{
var prevChar = CurrentCharacter;
if (prevChar == -1)
{
return; // We're at the end of the source
}
if (Buffering)
{
if (_currentBufferPosition >= Buffer.Length - 1)
{
// If there are no more lookaheads (thus no need to continue with the buffer) we can just clean up the buffer
if (_backtrackStack.Count == 0)
{
// Reset the buffer
Buffer.Length = 0;
_currentBufferPosition = 0;
Buffering = false;
}
else if (!ExpandBuffer())
{
// Failed to expand the buffer, because we're at the end of the source
_currentBufferPosition = Buffer.Length; // Force the position past the end of the buffer
}
}
else
{
// Not at the end yet, just advance the buffer pointer
_currentBufferPosition++;
}
}
else
{
// Just act like normal
InnerReader.Read(); // Don't care about the return value, Peek() is used to get characters from the source
}
UpdateCurrentCharacter();
_locationTracker.UpdateLocation((char)prevChar, (char)CurrentCharacter);
}
protected bool ExpandBuffer()
{
// Pull another character into the buffer and update the position
var ch = InnerReader.Read();
// Only append the character to the buffer if there actually is one
if (ch != -1)
{
Buffer.Append((char)ch);
_currentBufferPosition = Buffer.Length - 1;
return true;
}
return false;
}
private void UpdateCurrentCharacter()
{
if (Buffering && _currentBufferPosition < Buffer.Length)
{
// Read from the buffer
_currentCharacter = (int)Buffer[_currentBufferPosition];
}
else
{
// No buffer? Peek from the source
_currentCharacter = InnerReader.Peek();
}
}
private class BacktrackContext
{
public int BufferIndex { get; set; }
public SourceLocation Location { get; set; }
}
}
}

View File

@ -1,11 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Text
{
public class SourceSpan
{
public SourceLocation Begin { get; set; }
public SourceLocation End { get; set; }
}
}

View File

@ -1,106 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Utils;
namespace Microsoft.AspNetCore.Razor.Text
{
public class TextBufferReader : LookaheadTextReader
{
private Stack<BacktrackContext> _bookmarks = new Stack<BacktrackContext>();
private SourceLocationTracker _tracker = new SourceLocationTracker();
public TextBufferReader(ITextBuffer buffer)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
InnerBuffer = buffer;
}
internal ITextBuffer InnerBuffer { get; private set; }
public override SourceLocation CurrentLocation
{
get { return _tracker.CurrentLocation; }
}
public override int Peek()
{
return InnerBuffer.Peek();
}
public override int Read()
{
var read = InnerBuffer.Read();
if (read != -1)
{
var nextChar = '\0';
var next = Peek();
if (next != -1)
{
nextChar = (char)next;
}
_tracker.UpdateLocation((char)read, nextChar);
}
return read;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
var disposable = InnerBuffer as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
base.Dispose(disposing);
}
public override IDisposable BeginLookahead()
{
var context = new BacktrackContext() { Location = CurrentLocation };
_bookmarks.Push(context);
return new DisposableAction(() =>
{
EndLookahead(context);
});
}
public override void CancelBacktrack()
{
if (_bookmarks.Count == 0)
{
throw new InvalidOperationException(RazorResources.CancelBacktrack_Must_Be_Called_Within_Lookahead);
}
_bookmarks.Pop();
}
private void EndLookahead(BacktrackContext context)
{
if (_bookmarks.Count > 0 && ReferenceEquals(_bookmarks.Peek(), context))
{
// Backtrack wasn't cancelled, so pop it
_bookmarks.Pop();
// Set the new current location
_tracker.CurrentLocation = context.Location;
InnerBuffer.Position = context.Location.AbsoluteIndex;
}
}
/// <summary>
/// Need a class for reference equality to support cancelling backtrack.
/// </summary>
private class BacktrackContext
{
public SourceLocation Location { get; set; }
}
}
}

View File

@ -1,51 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Razor.Tokenizer
{
internal static class XmlHelpers
{
public static bool IsXmlNameStartChar(char chr)
{
// [4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] |
// [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] |
// [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
// http://www.w3.org/TR/REC-xml/#NT-Name
return Char.IsLetter(chr) ||
chr == ':' ||
chr == '_' ||
IsInRange(chr, 0xC0, 0xD6) ||
IsInRange(chr, 0xD8, 0xF6) ||
IsInRange(chr, 0xF8, 0x2FF) ||
IsInRange(chr, 0x370, 0x37D) ||
IsInRange(chr, 0x37F, 0x1FFF) ||
IsInRange(chr, 0x200C, 0x200D) ||
IsInRange(chr, 0x2070, 0x218F) ||
IsInRange(chr, 0x2C00, 0x2FEF) ||
IsInRange(chr, 0x3001, 0xD7FF) ||
IsInRange(chr, 0xF900, 0xFDCF) ||
IsInRange(chr, 0xFDF0, 0xFFFD) ||
IsInRange(chr, 0x10000, 0xEFFFF);
}
public static bool IsXmlNameChar(char chr)
{
// [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
// http://www.w3.org/TR/REC-xml/#NT-Name
return Char.IsDigit(chr) ||
IsXmlNameStartChar(chr) ||
chr == '-' ||
chr == '.' ||
chr == '·' || // (U+00B7 is middle dot: ·)
IsInRange(chr, 0x0300, 0x036F) ||
IsInRange(chr, 0x203F, 0x2040);
}
public static bool IsInRange(char chr, int low, int high)
{
return ((int)chr >= low) && ((int)chr <= high);
}
}
}

View File

@ -1,22 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Razor.Utils
{
internal static class CharUtils
{
internal static bool IsNonNewLineWhitespace(char c)
{
return Char.IsWhiteSpace(c) && !IsNewLine(c);
}
internal static bool IsNewLine(char c)
{
return c == 0x000d // Carriage return
|| c == 0x000a // Linefeed
|| c == 0x2028 // Line separator
|| c == 0x2029; // Paragraph separator
}
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Utils
{
internal static class EnumUtil
{
public static IEnumerable<T> Single<T>(T item)
{
yield return item;
}
public static IEnumerable<T> Prepend<T>(T item, IEnumerable<T> enumerable)
{
yield return item;
foreach (T t in enumerable)
{
yield return t;
}
}
}
}

View File

@ -1,16 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Utils
{
internal static class EnumeratorExtensions
{
public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> source)
{
return source.SelectMany(e => e);
}
}
}

View File

@ -1,264 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using Microsoft.AspNetCore.Razor.Text;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Test.Text
{
public class BufferingTextReaderTest : LookaheadTextReaderTestBase
{
private const string TestString = "abcdefg";
private class DisposeTestMockTextReader : TextReader
{
public bool Disposed { get; set; }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
Disposed = true;
}
}
protected override LookaheadTextReader CreateReader(string testString)
{
return new BufferingTextReader(new StringReader(testString));
}
[Fact]
public void PeekReturnsCurrentCharacterWithoutAdvancingPosition()
{
RunPeekTest("abc", peekAt: 2);
}
[Fact]
public void PeekReturnsNegativeOneAtEndOfSourceReader()
{
RunPeekTest("abc", peekAt: 3);
}
[Fact]
public void ReadReturnsCurrentCharacterAndAdvancesToNextCharacter()
{
RunReadTest("abc", readAt: 2);
}
[Fact]
public void EndingLookaheadReturnsReaderToPreviousLocation()
{
RunLookaheadTest("abcdefg", "abcb",
Read,
Lookahead(
Read,
Read),
Read);
}
[Fact]
public void MultipleLookaheadsCanBePerformed()
{
RunLookaheadTest("abcdefg", "abcbcdc",
Read,
Lookahead(
Read,
Read),
Read,
Lookahead(
Read,
Read),
Read);
}
[Fact]
public void LookaheadsCanBeNested()
{
RunLookaheadTest("abcdefg", "abcdefebc",
Read, // Appended: "a" Reader: "bcdefg"
Lookahead( // Reader: "bcdefg"
Read, // Appended: "b" Reader: "cdefg";
Read, // Appended: "c" Reader: "defg";
Read, // Appended: "d" Reader: "efg";
Lookahead( // Reader: "efg"
Read, // Appended: "e" Reader: "fg";
Read // Appended: "f" Reader: "g";
), // Reader: "efg"
Read // Appended: "e" Reader: "fg";
), // Reader: "bcdefg"
Read, // Appended: "b" Reader: "cdefg";
Read); // Appended: "c" Reader: "defg";
}
[Fact]
public void SourceLocationIsZeroWhenInitialized()
{
RunSourceLocationTest("abcdefg", SourceLocation.Zero, checkAt: 0);
}
[Fact]
public void CharacterAndAbsoluteIndicesIncreaseAsCharactersAreRead()
{
RunSourceLocationTest("abcdefg", new SourceLocation(4, 0, 4), checkAt: 4);
}
[Fact]
public void CharacterAndAbsoluteIndicesIncreaseAsSlashRInTwoCharacterNewlineIsRead()
{
RunSourceLocationTest("f\r\nb", new SourceLocation(2, 0, 2), checkAt: 2);
}
[Fact]
public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashNInTwoCharacterNewlineIsRead()
{
RunSourceLocationTest("f\r\nb", new SourceLocation(3, 1, 0), checkAt: 3);
}
[Fact]
public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashRInSingleCharacterNewlineIsRead()
{
RunSourceLocationTest("f\rb", new SourceLocation(2, 1, 0), checkAt: 2);
}
[Fact]
public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashNInSingleCharacterNewlineIsRead()
{
RunSourceLocationTest("f\nb", new SourceLocation(2, 1, 0), checkAt: 2);
}
[Fact]
public void EndingLookaheadResetsRawCharacterAndLineIndexToValuesWhenLookaheadBegan()
{
RunEndLookaheadUpdatesSourceLocationTest();
}
[Fact]
public void OnceBufferingBeginsReadsCanContinuePastEndOfBuffer()
{
RunLookaheadTest("abcdefg", "abcbcdefg",
Read,
Lookahead(Read(2)),
Read(2),
ReadToEnd);
}
[Fact]
public void DisposeDisposesSourceReader()
{
RunDisposeTest(r => r.Dispose());
}
#if !NETCOREAPP1_0
[Fact]
public void CloseDisposesSourceReader()
{
RunDisposeTest(r => r.Close());
}
#endif
[Fact]
public void ReadWithBufferSupportsLookahead()
{
RunBufferReadTest((reader, buffer, index, count) => reader.Read(buffer, index, count));
}
[Fact]
public void ReadBlockSupportsLookahead()
{
RunBufferReadTest((reader, buffer, index, count) => reader.ReadBlock(buffer, index, count));
}
[Fact]
public void ReadLineSupportsLookahead()
{
RunReadUntilTest(r => r.ReadLine(), expectedRaw: 8, expectedChar: 0, expectedLine: 2);
}
[Fact]
public void ReadToEndSupportsLookahead()
{
RunReadUntilTest(r => r.ReadToEnd(), expectedRaw: 11, expectedChar: 3, expectedLine: 2);
}
[Fact]
public void ReadLineMaintainsCorrectCharacterPosition()
{
RunSourceLocationTest("abc\r\ndef", new SourceLocation(5, 1, 0), r => r.ReadLine());
}
[Fact]
public void ReadToEndWorksAsInNormalTextReader()
{
RunReadToEndTest();
}
[Fact]
public void CancelBacktrackStopsNextEndLookaheadFromBacktracking()
{
RunLookaheadTest("abcdefg", "abcdefg",
Lookahead(
Read(2),
CancelBacktrack
),
ReadToEnd);
}
[Fact]
public void CancelBacktrackThrowsInvalidOperationExceptionIfCalledOutsideOfLookahead()
{
RunCancelBacktrackOutsideLookaheadTest();
}
[Fact]
public void CancelBacktrackOnlyCancelsBacktrackingForInnermostNestedLookahead()
{
RunLookaheadTest("abcdefg", "abcdabcdefg",
Lookahead(
Read(2),
Lookahead(
Read,
CancelBacktrack
),
Read
),
ReadToEnd);
}
[Fact]
public void BacktrackBufferIsClearedWhenEndReachedAndNoCurrentLookaheads()
{
// Arrange
var source = new StringReader(TestString);
var reader = new BufferingTextReader(source);
reader.Read(); // Reader: "bcdefg"
using (reader.BeginLookahead())
{
reader.Read(); // Reader: "cdefg"
} // Reader: "bcdefg"
reader.Read(); // Reader: "cdefg"
Assert.NotNull(reader.Buffer); // Verify our assumption that the buffer still exists
// Act
reader.Read();
// Assert
Assert.False(reader.Buffering, "The buffer was not reset when the end was reached");
Assert.Equal(0, reader.Buffer.Length);
}
private static void RunDisposeTest(Action<LookaheadTextReader> triggerAction)
{
// Arrange
var source = new DisposeTestMockTextReader();
var reader = new BufferingTextReader(source);
// Act
triggerAction(reader);
// Assert
Assert.True(source.Disposed);
}
}
}

View File

@ -1,228 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Web.WebPages.TestUtils;
using Microsoft.AspNetCore.Razor.Text;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Test.Text
{
public class TextBufferReaderTest : LookaheadTextReaderTestBase
{
protected override LookaheadTextReader CreateReader(string testString)
{
return new TextBufferReader(new StringTextBuffer(testString));
}
[Fact]
public void PeekReturnsCurrentCharacterWithoutAdvancingPosition()
{
RunPeekTest("abc", peekAt: 2);
}
[Fact]
public void PeekReturnsNegativeOneAtEndOfSourceReader()
{
RunPeekTest("abc", peekAt: 3);
}
[Fact]
public void ReadReturnsCurrentCharacterAndAdvancesToNextCharacter()
{
RunReadTest("abc", readAt: 2);
}
[Fact]
public void EndingLookaheadReturnsReaderToPreviousLocation()
{
RunLookaheadTest("abcdefg", "abcb",
Read,
Lookahead(
Read,
Read),
Read);
}
[Fact]
public void MultipleLookaheadsCanBePerformed()
{
RunLookaheadTest("abcdefg", "abcbcdc",
Read,
Lookahead(
Read,
Read),
Read,
Lookahead(
Read,
Read),
Read);
}
[Fact]
public void LookaheadsCanBeNested()
{
RunLookaheadTest("abcdefg", "abcdefebc",
Read, // Appended: "a" Reader: "bcdefg"
Lookahead( // Reader: "bcdefg"
Read, // Appended: "b" Reader: "cdefg";
Read, // Appended: "c" Reader: "defg";
Read, // Appended: "d" Reader: "efg";
Lookahead( // Reader: "efg"
Read, // Appended: "e" Reader: "fg";
Read // Appended: "f" Reader: "g";
), // Reader: "efg"
Read // Appended: "e" Reader: "fg";
), // Reader: "bcdefg"
Read, // Appended: "b" Reader: "cdefg";
Read); // Appended: "c" Reader: "defg";
}
[Fact]
public void SourceLocationIsZeroWhenInitialized()
{
RunSourceLocationTest("abcdefg", SourceLocation.Zero, checkAt: 0);
}
[Fact]
public void CharacterAndAbsoluteIndicesIncreaseAsCharactersAreRead()
{
RunSourceLocationTest("abcdefg", new SourceLocation(4, 0, 4), checkAt: 4);
}
[Fact]
public void CharacterAndAbsoluteIndicesIncreaseAsSlashRInTwoCharacterNewlineIsRead()
{
RunSourceLocationTest("f\r\nb", new SourceLocation(2, 0, 2), checkAt: 2);
}
[Fact]
public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashNInTwoCharacterNewlineIsRead()
{
RunSourceLocationTest("f\r\nb", new SourceLocation(3, 1, 0), checkAt: 3);
}
[Fact]
public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashRInSingleCharacterNewlineIsRead()
{
RunSourceLocationTest("f\rb", new SourceLocation(2, 1, 0), checkAt: 2);
}
[Fact]
public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashNInSingleCharacterNewlineIsRead()
{
RunSourceLocationTest("f\nb", new SourceLocation(2, 1, 0), checkAt: 2);
}
[Fact]
public void EndingLookaheadResetsRawCharacterAndLineIndexToValuesWhenLookaheadBegan()
{
RunEndLookaheadUpdatesSourceLocationTest();
}
[Fact]
public void OnceBufferingBeginsReadsCanContinuePastEndOfBuffer()
{
RunLookaheadTest("abcdefg", "abcbcdefg",
Read,
Lookahead(Read(2)),
Read(2),
ReadToEnd);
}
[Fact]
public void DisposeDisposesSourceReader()
{
RunDisposeTest(r => r.Dispose());
}
#if !NETCOREAPP1_0
[Fact]
public void CloseDisposesSourceReader()
{
RunDisposeTest(r => r.Close());
}
#endif
[Fact]
public void ReadWithBufferSupportsLookahead()
{
RunBufferReadTest((reader, buffer, index, count) => reader.Read(buffer, index, count));
}
[Fact]
public void ReadBlockSupportsLookahead()
{
RunBufferReadTest((reader, buffer, index, count) => reader.ReadBlock(buffer, index, count));
}
[Fact]
public void ReadLineSupportsLookahead()
{
RunReadUntilTest(r => r.ReadLine(), expectedRaw: 8, expectedChar: 0, expectedLine: 2);
}
[Fact]
public void ReadToEndSupportsLookahead()
{
RunReadUntilTest(r => r.ReadToEnd(), expectedRaw: 11, expectedChar: 3, expectedLine: 2);
}
[Fact]
public void ReadLineMaintainsCorrectCharacterPosition()
{
RunSourceLocationTest("abc\r\ndef", new SourceLocation(5, 1, 0), r => r.ReadLine());
}
[Fact]
public void ReadToEndWorksAsInNormalTextReader()
{
RunReadToEndTest();
}
[Fact]
public void CancelBacktrackStopsNextEndLookaheadFromBacktracking()
{
RunLookaheadTest("abcdefg", "abcdefg",
Lookahead(
Read(2),
CancelBacktrack
),
ReadToEnd);
}
[Fact]
public void CancelBacktrackThrowsInvalidOperationExceptionIfCalledOutsideOfLookahead()
{
RunCancelBacktrackOutsideLookaheadTest();
}
[Fact]
public void CancelBacktrackOnlyCancelsBacktrackingForInnermostNestedLookahead()
{
RunLookaheadTest("abcdefg", "abcdabcdefg",
Lookahead(
Read(2),
Lookahead(
Read,
CancelBacktrack
),
Read
),
ReadToEnd);
}
private static void RunDisposeTest(Action<LookaheadTextReader> triggerAction)
{
// Arrange
var source = new StringTextBuffer("abcdefg");
var reader = new TextBufferReader(source);
// Act
triggerAction(reader);
// Assert
Assert.True(source.Disposed);
}
}
}