Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Razor.Generator;
using System.Web.Razor.Parser;
using Microsoft.CSharp;
using Xunit;
namespace System.Web.Razor.Test
{
public class CSharpRazorCodeLanguageTest
{
[Fact]
public void CreateCodeParserReturnsNewCSharpCodeParser()
{
// Arrange
RazorCodeLanguage service = new CSharpRazorCodeLanguage();
// Act
ParserBase parser = service.CreateCodeParser();
// Assert
Assert.NotNull(parser);
Assert.IsType<CSharpCodeParser>(parser);
}
[Fact]
public void CreateCodeGeneratorParserListenerReturnsNewCSharpCodeGeneratorParserListener()
{
// Arrange
RazorCodeLanguage service = new CSharpRazorCodeLanguage();
// Act
RazorEngineHost host = new RazorEngineHost(service);
RazorCodeGenerator generator = service.CreateCodeGenerator("Foo", "Bar", "Baz", host);
// Assert
Assert.NotNull(generator);
Assert.IsType<CSharpRazorCodeGenerator>(generator);
Assert.Equal("Foo", generator.ClassName);
Assert.Equal("Bar", generator.RootNamespaceName);
Assert.Equal("Baz", generator.SourceFileName);
Assert.Same(host, generator.Host);
}
[Fact]
public void CodeDomProviderTypeReturnsVBCodeProvider()
{
// Arrange
RazorCodeLanguage service = new CSharpRazorCodeLanguage();
// Assert
Assert.Equal(typeof(CSharpCodeProvider), service.CodeDomProviderType);
}
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Text;
namespace System.Web.Razor.Test
{
internal static class CodeCompileUnitExtensions
{
public static string GenerateCode<T>(this CodeCompileUnit ccu) where T : CodeDomProvider, new()
{
StringBuilder output = new StringBuilder();
using (StringWriter writer = new StringWriter(output))
{
T provider = new T();
provider.GenerateCodeFromCompileUnit(ccu, writer, new CodeGeneratorOptions() { IndentString = " " });
}
return output.ToString();
}
}
}

View File

@ -0,0 +1,218 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Threading;
using System.Web.Razor.Editor;
using System.Web.Razor.Parser;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Resources;
using System.Web.Razor.Test.Framework;
using System.Web.Razor.Test.Utils;
using System.Web.Razor.Text;
using System.Web.WebPages.TestUtils;
using Microsoft.CSharp;
using Moq;
using Xunit;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Razor.Test.Editor
{
public class RazorEditorParserTest
{
private static readonly TestFile SimpleCSHTMLDocument = TestFile.Create("DesignTime.Simple.cshtml");
private static readonly TestFile SimpleCSHTMLDocumentGenerated = TestFile.Create("DesignTime.Simple.txt");
private const string TestLinePragmaFileName = "C:\\This\\Path\\Is\\Just\\For\\Line\\Pragmas.cshtml";
[Fact]
public void ConstructorRequiresNonNullHost()
{
Assert.ThrowsArgumentNull(() => new RazorEditorParser(null, TestLinePragmaFileName),
"host");
}
[Fact]
public void ConstructorRequiresNonNullPhysicalPath()
{
Assert.ThrowsArgumentNullOrEmptyString(() => new RazorEditorParser(CreateHost(), null),
"sourceFileName");
}
[Fact]
public void ConstructorRequiresNonEmptyPhysicalPath()
{
Assert.ThrowsArgumentNullOrEmptyString(() => new RazorEditorParser(CreateHost(), String.Empty),
"sourceFileName");
}
[Fact]
public void TreesAreDifferentReturnsTrueIfTreeStructureIsDifferent()
{
var factory = SpanFactory.CreateCsHtml();
Block original = new MarkupBlock(
factory.Markup("<p>"),
new ExpressionBlock(
factory.CodeTransition()),
factory.Markup("</p>"));
Block modified = new MarkupBlock(
factory.Markup("<p>"),
new ExpressionBlock(
factory.CodeTransition("@"),
factory.Code("f")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)),
factory.Markup("</p>"));
ITextBuffer oldBuffer = new StringTextBuffer("<p>@</p>");
ITextBuffer newBuffer = new StringTextBuffer("<p>@f</p>");
Assert.True(RazorEditorParser.TreesAreDifferent(
original, modified, new[] {
new TextChange(position: 4, oldLength: 0, oldBuffer: oldBuffer, newLength: 1, newBuffer: newBuffer)
}));
}
[Fact]
public void TreesAreDifferentReturnsFalseIfTreeStructureIsSame()
{
var factory = SpanFactory.CreateCsHtml();
Block original = new MarkupBlock(
factory.Markup("<p>"),
new ExpressionBlock(
factory.CodeTransition(),
factory.Code("f")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)),
factory.Markup("</p>"));
factory.Reset();
Block modified = new MarkupBlock(
factory.Markup("<p>"),
new ExpressionBlock(
factory.CodeTransition(),
factory.Code("foo")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)),
factory.Markup("</p>"));
original.LinkNodes();
modified.LinkNodes();
ITextBuffer oldBuffer = new StringTextBuffer("<p>@f</p>");
ITextBuffer newBuffer = new StringTextBuffer("<p>@foo</p>");
Assert.False(RazorEditorParser.TreesAreDifferent(
original, modified, new[] {
new TextChange(position: 5, oldLength: 0, oldBuffer: oldBuffer, newLength: 2, newBuffer: newBuffer)
}));
}
[Fact]
public void CheckForStructureChangesRequiresNonNullBufferInChange()
{
TextChange change = new TextChange();
Assert.ThrowsArgument(
() => new RazorEditorParser(
CreateHost(),
"C:\\Foo.cshtml").CheckForStructureChanges(change),
"change",
String.Format(RazorResources.Structure_Member_CannotBeNull, "Buffer", "TextChange"));
}
private static RazorEngineHost CreateHost()
{
return new RazorEngineHost(new CSharpRazorCodeLanguage()) { DesignTimeMode = true };
}
[Fact]
public void CheckForStructureChangesStartsReparseAndFiresDocumentParseCompletedEventIfNoAdditionalChangesQueued()
{
// Arrange
using (RazorEditorParser parser = CreateClientParser())
{
StringTextBuffer input = new StringTextBuffer(SimpleCSHTMLDocument.ReadAllText());
DocumentParseCompleteEventArgs capturedArgs = null;
ManualResetEventSlim parseComplete = new ManualResetEventSlim(false);
parser.DocumentParseComplete += (sender, args) =>
{
capturedArgs = args;
parseComplete.Set();
};
// Act
parser.CheckForStructureChanges(new TextChange(0, 0, new StringTextBuffer(String.Empty), input.Length, input));
// Assert
MiscUtils.DoWithTimeoutIfNotDebugging(parseComplete.Wait);
Assert.Equal(
SimpleCSHTMLDocumentGenerated.ReadAllText(),
MiscUtils.StripRuntimeVersion(
capturedArgs.GeneratorResults.GeneratedCode.GenerateCode<CSharpCodeProvider>()
));
}
}
[Fact]
public void CheckForStructureChangesStartsFullReparseIfChangeOverlapsMultipleSpans()
{
// Arrange
RazorEditorParser parser = new RazorEditorParser(CreateHost(), TestLinePragmaFileName);
ITextBuffer original = new StringTextBuffer("Foo @bar Baz");
ITextBuffer changed = new StringTextBuffer("Foo @bap Daz");
TextChange change = new TextChange(7, 3, original, 3, changed);
ManualResetEventSlim parseComplete = new ManualResetEventSlim();
int parseCount = 0;
parser.DocumentParseComplete += (sender, args) =>
{
Interlocked.Increment(ref parseCount);
parseComplete.Set();
};
Assert.Equal(PartialParseResult.Rejected, parser.CheckForStructureChanges(new TextChange(0, 0, new StringTextBuffer(String.Empty), 12, original)));
MiscUtils.DoWithTimeoutIfNotDebugging(parseComplete.Wait); // Wait for the parse to finish
parseComplete.Reset();
// Act
PartialParseResult result = parser.CheckForStructureChanges(change);
// Assert
Assert.Equal(PartialParseResult.Rejected, result);
MiscUtils.DoWithTimeoutIfNotDebugging(parseComplete.Wait);
Assert.Equal(2, parseCount);
}
private static void SetupMockWorker(Mock<RazorEditorParser> parser, ManualResetEventSlim running)
{
SetUpMockWorker(parser, running, null);
}
private static void SetUpMockWorker(Mock<RazorEditorParser> parser, ManualResetEventSlim running, Action<int> backgroundThreadIdReceiver)
{
parser.Setup(p => p.CreateBackgroundTask(It.IsAny<RazorEngineHost>(), It.IsAny<string>(), It.IsAny<TextChange>()))
.Returns<RazorEngineHost, string, TextChange>((host, fileName, change) =>
{
var task = new Mock<BackgroundParseTask>(new RazorTemplateEngine(host), fileName, change) { CallBase = true };
task.Setup(t => t.Run(It.IsAny<CancellationToken>()))
.Callback<CancellationToken>((token) =>
{
if (backgroundThreadIdReceiver != null)
{
backgroundThreadIdReceiver(Thread.CurrentThread.ManagedThreadId);
}
running.Set();
while (!token.IsCancellationRequested)
{
}
});
return task.Object;
});
}
private TextChange CreateDummyChange()
{
return new TextChange(0, 0, new StringTextBuffer(String.Empty), 3, new StringTextBuffer("foo"));
}
private static Mock<RazorEditorParser> CreateMockParser()
{
return new Mock<RazorEditorParser>(CreateHost(), TestLinePragmaFileName) { CallBase = true };
}
private static RazorEditorParser CreateClientParser()
{
return new RazorEditorParser(CreateHost(), TestLinePragmaFileName);
}
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Razor.Parser.SyntaxTree;
namespace System.Web.Razor.Test.Framework
{
public static class BlockExtensions
{
public static void LinkNodes(this Block self)
{
Span first = null;
Span previous = null;
foreach (Span span in self.Flatten())
{
if (first == null)
{
first = span;
}
span.Previous = previous;
if (previous != null)
{
previous.Next = span;
}
previous = span;
}
}
}
}

View File

@ -0,0 +1,235 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Web.Razor.Generator;
using System.Web.Razor.Parser.SyntaxTree;
namespace System.Web.Razor.Test.Framework
{
// The product code doesn't need this, but having subclasses for the block types makes tests much cleaner :)
public class StatementBlock : Block
{
private const BlockType ThisBlockType = BlockType.Statement;
public StatementBlock(IBlockCodeGenerator codeGenerator, IEnumerable<SyntaxTreeNode> children)
: base(ThisBlockType, children, codeGenerator)
{
}
public StatementBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children)
: this(codeGenerator, (IEnumerable<SyntaxTreeNode>)children)
{
}
public StatementBlock(params SyntaxTreeNode[] children)
: this(BlockCodeGenerator.Null, children)
{
}
public StatementBlock(IEnumerable<SyntaxTreeNode> children)
: this(BlockCodeGenerator.Null, children)
{
}
}
public class DirectiveBlock : Block
{
private const BlockType ThisBlockType = BlockType.Directive;
public DirectiveBlock(IBlockCodeGenerator codeGenerator, IEnumerable<SyntaxTreeNode> children)
: base(ThisBlockType, children, codeGenerator)
{
}
public DirectiveBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children)
: this(codeGenerator, (IEnumerable<SyntaxTreeNode>)children)
{
}
public DirectiveBlock(params SyntaxTreeNode[] children)
: this(BlockCodeGenerator.Null, children)
{
}
public DirectiveBlock(IEnumerable<SyntaxTreeNode> children)
: this(BlockCodeGenerator.Null, children)
{
}
}
public class FunctionsBlock : Block
{
private const BlockType ThisBlockType = BlockType.Functions;
public FunctionsBlock(IBlockCodeGenerator codeGenerator, IEnumerable<SyntaxTreeNode> children)
: base(ThisBlockType, children, codeGenerator)
{
}
public FunctionsBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children)
: this(codeGenerator, (IEnumerable<SyntaxTreeNode>)children)
{
}
public FunctionsBlock(params SyntaxTreeNode[] children)
: this(BlockCodeGenerator.Null, children)
{
}
public FunctionsBlock(IEnumerable<SyntaxTreeNode> children)
: this(BlockCodeGenerator.Null, children)
{
}
}
public class ExpressionBlock : Block
{
private const BlockType ThisBlockType = BlockType.Expression;
public ExpressionBlock(IBlockCodeGenerator codeGenerator, IEnumerable<SyntaxTreeNode> children)
: base(ThisBlockType, children, codeGenerator)
{
}
public ExpressionBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children)
: this(codeGenerator, (IEnumerable<SyntaxTreeNode>)children)
{
}
public ExpressionBlock(params SyntaxTreeNode[] children)
: this(new ExpressionCodeGenerator(), children)
{
}
public ExpressionBlock(IEnumerable<SyntaxTreeNode> children)
: this(new ExpressionCodeGenerator(), children)
{
}
}
public class HelperBlock : Block
{
private const BlockType ThisBlockType = BlockType.Helper;
public HelperBlock(IBlockCodeGenerator codeGenerator, IEnumerable<SyntaxTreeNode> children)
: base(ThisBlockType, children, codeGenerator)
{
}
public HelperBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children)
: this(codeGenerator, (IEnumerable<SyntaxTreeNode>)children)
{
}
public HelperBlock(params SyntaxTreeNode[] children)
: this(BlockCodeGenerator.Null, children)
{
}
public HelperBlock(IEnumerable<SyntaxTreeNode> children)
: this(BlockCodeGenerator.Null, children)
{
}
}
public class MarkupBlock : Block
{
private const BlockType ThisBlockType = BlockType.Markup;
public MarkupBlock(IBlockCodeGenerator codeGenerator, IEnumerable<SyntaxTreeNode> children)
: base(ThisBlockType, children, codeGenerator)
{
}
public MarkupBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children)
: this(codeGenerator, (IEnumerable<SyntaxTreeNode>)children)
{
}
public MarkupBlock(params SyntaxTreeNode[] children)
: this(BlockCodeGenerator.Null, children)
{
}
public MarkupBlock(IEnumerable<SyntaxTreeNode> children)
: this(BlockCodeGenerator.Null, children)
{
}
}
public class SectionBlock : Block
{
private const BlockType ThisBlockType = BlockType.Section;
public SectionBlock(IBlockCodeGenerator codeGenerator, IEnumerable<SyntaxTreeNode> children)
: base(ThisBlockType, children, codeGenerator)
{
}
public SectionBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children)
: this(codeGenerator, (IEnumerable<SyntaxTreeNode>)children)
{
}
public SectionBlock(params SyntaxTreeNode[] children)
: this(BlockCodeGenerator.Null, children)
{
}
public SectionBlock(IEnumerable<SyntaxTreeNode> children)
: this(BlockCodeGenerator.Null, children)
{
}
}
public class TemplateBlock : Block
{
private const BlockType ThisBlockType = BlockType.Template;
public TemplateBlock(IBlockCodeGenerator codeGenerator, IEnumerable<SyntaxTreeNode> children)
: base(ThisBlockType, children, codeGenerator)
{
}
public TemplateBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children)
: this(codeGenerator, (IEnumerable<SyntaxTreeNode>)children)
{
}
public TemplateBlock(params SyntaxTreeNode[] children)
: this(new TemplateBlockCodeGenerator(), children)
{
}
public TemplateBlock(IEnumerable<SyntaxTreeNode> children)
: this(new TemplateBlockCodeGenerator(), children)
{
}
}
public class CommentBlock : Block
{
private const BlockType ThisBlockType = BlockType.Comment;
public CommentBlock(IBlockCodeGenerator codeGenerator, IEnumerable<SyntaxTreeNode> children)
: base(ThisBlockType, children, codeGenerator)
{
}
public CommentBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children)
: this(codeGenerator, (IEnumerable<SyntaxTreeNode>)children)
{
}
public CommentBlock(params SyntaxTreeNode[] children)
: this(new RazorCommentCodeGenerator(), children)
{
}
public CommentBlock(IEnumerable<SyntaxTreeNode> children)
: this(new RazorCommentCodeGenerator(), children)
{
}
}
}

View File

@ -0,0 +1,76 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Web.Razor.Parser;
using System.Web.Razor.Parser.SyntaxTree;
namespace System.Web.Razor.Test.Framework
{
public abstract class CodeParserTestBase : ParserTestBase
{
protected abstract ISet<string> KeywordSet { get; }
protected override ParserBase SelectActiveParser(ParserBase codeParser, ParserBase markupParser)
{
return codeParser;
}
protected void ImplicitExpressionTest(string input, params RazorError[] errors)
{
ImplicitExpressionTest(input, AcceptedCharacters.NonWhiteSpace, errors);
}
protected void ImplicitExpressionTest(string input, AcceptedCharacters acceptedCharacters, params RazorError[] errors)
{
ImplicitExpressionTest(input, input, acceptedCharacters, errors);
}
protected void ImplicitExpressionTest(string input, string expected, params RazorError[] errors)
{
ImplicitExpressionTest(input, expected, AcceptedCharacters.NonWhiteSpace, errors);
}
protected override void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any)
{
SingleSpanBlockTest(document, blockType, spanType, acceptedCharacters, expectedError: null);
}
protected override void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any)
{
SingleSpanBlockTest(document, spanContent, blockType, spanType, acceptedCharacters, expectedErrors: null);
}
protected override void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, params RazorError[] expectedError)
{
SingleSpanBlockTest(document, document, blockType, spanType, expectedError);
}
protected override void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, params RazorError[] expectedErrors)
{
SingleSpanBlockTest(document, spanContent, blockType, spanType, AcceptedCharacters.Any, expectedErrors ?? new RazorError[0]);
}
protected override void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters, params RazorError[] expectedError)
{
SingleSpanBlockTest(document, document, blockType, spanType, acceptedCharacters, expectedError);
}
protected override void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters, params RazorError[] expectedErrors)
{
Block b = CreateSimpleBlockAndSpan(spanContent, blockType, spanType, acceptedCharacters);
ParseBlockTest(document, b, expectedErrors ?? new RazorError[0]);
}
protected void ImplicitExpressionTest(string input, string expected, AcceptedCharacters acceptedCharacters, params RazorError[] errors)
{
var factory = CreateSpanFactory();
ParseBlockTest(SyntaxConstants.TransitionString + input,
new ExpressionBlock(
factory.CodeTransition(),
factory.Code(expected)
.AsImplicitExpression(KeywordSet)
.Accepts(acceptedCharacters)),
errors);
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Web.Razor.Parser;
namespace System.Web.Razor.Test.Framework
{
public abstract class CsHtmlCodeParserTestBase : CodeParserTestBase
{
protected override ISet<string> KeywordSet
{
get { return CSharpCodeParser.DefaultKeywords; }
}
protected override SpanFactory CreateSpanFactory()
{
return SpanFactory.CreateCsHtml();
}
public override ParserBase CreateMarkupParser()
{
return new HtmlMarkupParser();
}
public override ParserBase CreateCodeParser()
{
return new CSharpCodeParser();
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Web.Razor.Parser;
namespace System.Web.Razor.Test.Framework
{
public abstract class CsHtmlMarkupParserTestBase : MarkupParserTestBase
{
protected override ISet<string> KeywordSet
{
get { return CSharpCodeParser.DefaultKeywords; }
}
protected override SpanFactory CreateSpanFactory()
{
return SpanFactory.CreateCsHtml();
}
public override ParserBase CreateMarkupParser()
{
return new HtmlMarkupParser();
}
public override ParserBase CreateCodeParser()
{
return new CSharpCodeParser();
}
}
}

View File

@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Text;
using System.Web.Razor.Utils;
namespace System.Web.Razor.Test.Framework
{
public class ErrorCollector
{
private StringBuilder _message = new StringBuilder();
private int _indent = 0;
public bool Success { get; private set; }
public string Message
{
get { return _message.ToString(); }
}
public ErrorCollector()
{
Success = true;
}
public void AddError(string msg, params object[] args)
{
Append("F", msg, args);
Success = false;
}
public void AddMessage(string msg, params object[] args)
{
Append("P", msg, args);
}
public IDisposable Indent()
{
_indent++;
return new DisposableAction(Unindent);
}
public void Unindent()
{
_indent--;
}
private void Append(string prefix, string msg, object[] args)
{
_message.Append(prefix);
_message.Append(":");
_message.Append(new String('\t', _indent));
_message.AppendFormat(msg, args);
_message.AppendLine();
}
}
}

View File

@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Razor.Parser;
using System.Web.Razor.Parser.SyntaxTree;
namespace System.Web.Razor.Test.Framework
{
public abstract class MarkupParserTestBase : CodeParserTestBase
{
protected override ParserBase SelectActiveParser(ParserBase codeParser, ParserBase markupParser)
{
return markupParser;
}
protected virtual void SingleSpanDocumentTest(string document, BlockType blockType, SpanKind spanType)
{
Block b = CreateSimpleBlockAndSpan(document, blockType, spanType);
ParseDocumentTest(document, b);
}
protected virtual void ParseDocumentTest(string document)
{
ParseDocumentTest(document, null, false);
}
protected virtual void ParseDocumentTest(string document, Block expectedRoot)
{
ParseDocumentTest(document, expectedRoot, false, null);
}
protected virtual void ParseDocumentTest(string document, Block expectedRoot, params RazorError[] expectedErrors)
{
ParseDocumentTest(document, expectedRoot, false, expectedErrors);
}
protected virtual void ParseDocumentTest(string document, bool designTimeParser)
{
ParseDocumentTest(document, null, designTimeParser);
}
protected virtual void ParseDocumentTest(string document, Block expectedRoot, bool designTimeParser)
{
ParseDocumentTest(document, expectedRoot, designTimeParser, null);
}
protected virtual void ParseDocumentTest(string document, Block expectedRoot, bool designTimeParser, params RazorError[] expectedErrors)
{
RunParseTest(document, parser => parser.ParseDocument, expectedRoot, expectedErrors, designTimeParser);
}
protected virtual ParserResults ParseDocument(string document)
{
return ParseDocument(document, designTimeParser: false);
}
protected virtual ParserResults ParseDocument(string document, bool designTimeParser)
{
return RunParse(document, parser => parser.ParseDocument, designTimeParser);
}
protected virtual ParserResults ParseBlock(string document)
{
return ParseBlock(document, designTimeParser: false);
}
protected virtual ParserResults ParseBlock(string document, bool designTimeParser)
{
return RunParse(document, parser => parser.ParseBlock, designTimeParser);
}
}
}

View File

@ -0,0 +1,383 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
//#define PARSER_TRACE
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Web.Razor.Generator;
using System.Web.Razor.Parser;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Text;
using Xunit;
namespace System.Web.Razor.Test.Framework
{
public abstract class ParserTestBase
{
protected static Block IgnoreOutput = new IgnoreOutputBlock();
public SpanFactory Factory { get; private set; }
protected ParserTestBase()
{
Factory = CreateSpanFactory();
}
public abstract ParserBase CreateMarkupParser();
public abstract ParserBase CreateCodeParser();
protected abstract ParserBase SelectActiveParser(ParserBase codeParser, ParserBase markupParser);
public virtual ParserContext CreateParserContext(ITextDocument input, ParserBase codeParser, ParserBase markupParser)
{
return new ParserContext(input, codeParser, markupParser, SelectActiveParser(codeParser, markupParser));
}
protected abstract SpanFactory CreateSpanFactory();
protected virtual void ParseBlockTest(string document)
{
ParseBlockTest(document, null, false, new RazorError[0]);
}
protected virtual void ParseBlockTest(string document, bool designTimeParser)
{
ParseBlockTest(document, null, designTimeParser, new RazorError[0]);
}
protected virtual void ParseBlockTest(string document, params RazorError[] expectedErrors)
{
ParseBlockTest(document, false, expectedErrors);
}
protected virtual void ParseBlockTest(string document, bool designTimeParser, params RazorError[] expectedErrors)
{
ParseBlockTest(document, null, designTimeParser, expectedErrors);
}
protected virtual void ParseBlockTest(string document, Block expectedRoot)
{
ParseBlockTest(document, expectedRoot, false, null);
}
protected virtual void ParseBlockTest(string document, Block expectedRoot, bool designTimeParser)
{
ParseBlockTest(document, expectedRoot, designTimeParser, null);
}
protected virtual void ParseBlockTest(string document, Block expectedRoot, params RazorError[] expectedErrors)
{
ParseBlockTest(document, expectedRoot, false, expectedErrors);
}
protected virtual void ParseBlockTest(string document, Block expectedRoot, bool designTimeParser, params RazorError[] expectedErrors)
{
RunParseTest(document, parser => parser.ParseBlock, expectedRoot, (expectedErrors ?? new RazorError[0]).ToList(), designTimeParser);
}
protected virtual void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any)
{
SingleSpanBlockTest(document, blockType, spanType, acceptedCharacters, expectedError: null);
}
protected virtual void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any)
{
SingleSpanBlockTest(document, spanContent, blockType, spanType, acceptedCharacters, expectedErrors: null);
}
protected virtual void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, params RazorError[] expectedError)
{
SingleSpanBlockTest(document, document, blockType, spanType, expectedError);
}
protected virtual void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, params RazorError[] expectedErrors)
{
SingleSpanBlockTest(document, spanContent, blockType, spanType, AcceptedCharacters.Any, expectedErrors ?? new RazorError[0]);
}
protected virtual void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters, params RazorError[] expectedError)
{
SingleSpanBlockTest(document, document, blockType, spanType, acceptedCharacters, expectedError);
}
protected virtual void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters, params RazorError[] expectedErrors)
{
BlockBuilder builder = new BlockBuilder();
builder.Type = blockType;
ParseBlockTest(
document,
ConfigureAndAddSpanToBlock(
builder,
Factory.Span(spanType, spanContent, spanType == SpanKind.Markup)
.Accepts(acceptedCharacters)),
expectedErrors ?? new RazorError[0]);
}
protected virtual ParserResults RunParse(string document, Func<ParserBase, Action> parserActionSelector, bool designTimeParser)
{
// Create the source
ParserResults results = null;
using (SeekableTextReader reader = new SeekableTextReader(document))
{
try
{
ParserBase codeParser = CreateCodeParser();
ParserBase markupParser = CreateMarkupParser();
ParserContext context = CreateParserContext(reader, codeParser, markupParser);
context.DesignTimeMode = designTimeParser;
codeParser.Context = context;
markupParser.Context = context;
// Run the parser
parserActionSelector(context.ActiveParser)();
results = context.CompleteParse();
}
finally
{
if (results != null && results.Document != null)
{
WriteTraceLine(String.Empty);
WriteTraceLine("Actual Parse Tree:");
WriteNode(0, results.Document);
}
}
}
return results;
}
protected virtual void RunParseTest(string document, Func<ParserBase, Action> parserActionSelector, Block expectedRoot, IList<RazorError> expectedErrors, bool designTimeParser)
{
// Create the source
ParserResults results = RunParse(document, parserActionSelector, designTimeParser);
// Evaluate the results
if (!ReferenceEquals(expectedRoot, IgnoreOutput))
{
EvaluateResults(results, expectedRoot, expectedErrors);
}
}
[Conditional("PARSER_TRACE")]
private void WriteNode(int indent, SyntaxTreeNode node)
{
string content = node.ToString().Replace("\r", "\\r")
.Replace("\n", "\\n")
.Replace("{", "{{")
.Replace("}", "}}");
if (indent > 0)
{
content = new String('.', indent * 2) + content;
}
WriteTraceLine(content);
Block block = node as Block;
if (block != null)
{
foreach (SyntaxTreeNode child in block.Children)
{
WriteNode(indent + 1, child);
}
}
}
public static void EvaluateResults(ParserResults results, Block expectedRoot)
{
EvaluateResults(results, expectedRoot, null);
}
public static void EvaluateResults(ParserResults results, Block expectedRoot, IList<RazorError> expectedErrors)
{
EvaluateParseTree(results.Document, expectedRoot);
EvaluateRazorErrors(results.ParserErrors, expectedErrors);
}
public static void EvaluateParseTree(Block actualRoot, Block expectedRoot)
{
// Evaluate the result
ErrorCollector collector = new ErrorCollector();
// Link all the nodes
expectedRoot.LinkNodes();
if (expectedRoot == null)
{
Assert.Null(actualRoot);
}
else
{
Assert.NotNull(actualRoot);
EvaluateSyntaxTreeNode(collector, actualRoot, expectedRoot);
if (collector.Success)
{
WriteTraceLine("Parse Tree Validation Succeeded:\r\n{0}", collector.Message);
}
else
{
Assert.True(false, String.Format("\r\n{0}", collector.Message));
}
}
}
private static void EvaluateSyntaxTreeNode(ErrorCollector collector, SyntaxTreeNode actual, SyntaxTreeNode expected)
{
if (actual == null)
{
AddNullActualError(collector, actual, expected);
}
if (actual.IsBlock != expected.IsBlock)
{
AddMismatchError(collector, actual, expected);
}
else
{
if (expected.IsBlock)
{
EvaluateBlock(collector, (Block)actual, (Block)expected);
}
else
{
EvaluateSpan(collector, (Span)actual, (Span)expected);
}
}
}
private static void EvaluateSpan(ErrorCollector collector, Span actual, Span expected)
{
if (!Equals(expected, actual))
{
AddMismatchError(collector, actual, expected);
}
else
{
AddPassedMessage(collector, expected);
}
}
private static void EvaluateBlock(ErrorCollector collector, Block actual, Block expected)
{
if (actual.Type != expected.Type || !expected.CodeGenerator.Equals(actual.CodeGenerator))
{
AddMismatchError(collector, actual, expected);
}
else
{
AddPassedMessage(collector, expected);
using (collector.Indent())
{
IEnumerator<SyntaxTreeNode> expectedNodes = expected.Children.GetEnumerator();
IEnumerator<SyntaxTreeNode> actualNodes = actual.Children.GetEnumerator();
while (expectedNodes.MoveNext())
{
if (!actualNodes.MoveNext())
{
collector.AddError("{0} - FAILED :: No more elements at this node", expectedNodes.Current);
}
else
{
EvaluateSyntaxTreeNode(collector, actualNodes.Current, expectedNodes.Current);
}
}
while (actualNodes.MoveNext())
{
collector.AddError("End of Node - FAILED :: Found Node: {0}", actualNodes.Current);
}
}
}
}
private static void AddPassedMessage(ErrorCollector collector, SyntaxTreeNode expected)
{
collector.AddMessage("{0} - PASSED", expected);
}
private static void AddMismatchError(ErrorCollector collector, SyntaxTreeNode actual, SyntaxTreeNode expected)
{
collector.AddError("{0} - FAILED :: Actual: {1}", expected, actual);
}
private static void AddNullActualError(ErrorCollector collector, SyntaxTreeNode actual, SyntaxTreeNode expected)
{
collector.AddError("{0} - FAILED :: Actual: << Null >>", expected);
}
public static void EvaluateRazorErrors(IList<RazorError> actualErrors, IList<RazorError> expectedErrors)
{
// Evaluate the errors
if (expectedErrors == null || expectedErrors.Count == 0)
{
Assert.True(actualErrors.Count == 0,
String.Format("Expected that no errors would be raised, but the following errors were:\r\n{0}", FormatErrors(actualErrors)));
}
else
{
Assert.True(expectedErrors.Count == actualErrors.Count,
String.Format("Expected that {0} errors would be raised, but {1} errors were.\r\nExpected Errors: \r\n{2}\r\nActual Errors: \r\n{3}",
expectedErrors.Count,
actualErrors.Count,
FormatErrors(expectedErrors),
FormatErrors(actualErrors)));
Assert.Equal(expectedErrors.ToArray(), actualErrors.ToArray());
}
WriteTraceLine("Expected Errors were raised:\r\n{0}", FormatErrors(expectedErrors));
}
public static string FormatErrors(IList<RazorError> errors)
{
if (errors == null)
{
return "\t<< No Errors >>";
}
StringBuilder builder = new StringBuilder();
foreach (RazorError err in errors)
{
builder.AppendFormat("\t{0}", err);
builder.AppendLine();
}
return builder.ToString();
}
[Conditional("PARSER_TRACE")]
private static void WriteTraceLine(string format, params object[] args)
{
Trace.WriteLine(String.Format(format, args));
}
protected virtual Block CreateSimpleBlockAndSpan(string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any)
{
SpanConstructor span = Factory.Span(spanType, spanContent, spanType == SpanKind.Markup).Accepts(acceptedCharacters);
BlockBuilder b = new BlockBuilder()
{
Type = blockType
};
return ConfigureAndAddSpanToBlock(b, span);
}
protected virtual Block ConfigureAndAddSpanToBlock(BlockBuilder block, SpanConstructor span)
{
switch (block.Type)
{
case BlockType.Markup:
span.With(new MarkupCodeGenerator());
break;
case BlockType.Statement:
span.With(new StatementCodeGenerator());
break;
case BlockType.Expression:
block.CodeGenerator = new ExpressionCodeGenerator();
span.With(new ExpressionCodeGenerator());
break;
}
block.Children.Add(span);
return block.Build();
}
private class IgnoreOutputBlock : Block
{
public IgnoreOutputBlock() : base(BlockType.Template, Enumerable.Empty<SyntaxTreeNode>(), null) { }
}
}
}

View File

@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Globalization;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Text;
using System.Web.Razor.Tokenizer.Symbols;
using Microsoft.Internal.Web.Utils;
namespace System.Web.Razor.Test.Framework
{
internal class RawTextSymbol : ISymbol
{
public SourceLocation Start { get; private set; }
public string Content { get; private set; }
public RawTextSymbol(SourceLocation start, string content)
{
if (content == null)
{
throw new ArgumentNullException("content");
}
Start = start;
Content = content;
}
public override bool Equals(object obj)
{
RawTextSymbol other = obj as RawTextSymbol;
return Equals(Start, other.Start) && Equals(Content, other.Content);
}
internal bool EquivalentTo(ISymbol sym)
{
return Equals(Start, sym.Start) && Equals(Content, sym.Content);
}
public override int GetHashCode()
{
return HashCodeCombiner.Start()
.Add(Start)
.Add(Content)
.CombinedHash;
}
public void OffsetStart(SourceLocation documentStart)
{
Start = documentStart + Start;
}
public void ChangeStart(SourceLocation newStart)
{
Start = newStart;
}
public override string ToString()
{
return String.Format(CultureInfo.InvariantCulture, "{0} RAW - [{1}]", Start, Content);
}
internal void CalculateStart(Span prev)
{
if (prev == null)
{
Start = SourceLocation.Zero;
}
else
{
Start = new SourceLocationTracker(prev.Start).UpdateLocation(prev.Content).CurrentLocation;
}
}
}
}

View File

@ -0,0 +1,407 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Web.Razor.Editor;
using System.Web.Razor.Generator;
using System.Web.Razor.Parser;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Text;
using System.Web.Razor.Tokenizer;
using System.Web.Razor.Tokenizer.Symbols;
namespace System.Web.Razor.Test.Framework
{
public static class SpanFactoryExtensions
{
public static UnclassifiedCodeSpanConstructor EmptyCSharp(this SpanFactory self)
{
return new UnclassifiedCodeSpanConstructor(
self.Span(SpanKind.Code, new CSharpSymbol(self.LocationTracker.CurrentLocation, String.Empty, CSharpSymbolType.Unknown)));
}
public static UnclassifiedCodeSpanConstructor EmptyVB(this SpanFactory self)
{
return new UnclassifiedCodeSpanConstructor(
self.Span(SpanKind.Code, new VBSymbol(self.LocationTracker.CurrentLocation, String.Empty, VBSymbolType.Unknown)));
}
public static SpanConstructor EmptyHtml(this SpanFactory self)
{
return self.Span(SpanKind.Markup, new HtmlSymbol(self.LocationTracker.CurrentLocation, String.Empty, HtmlSymbolType.Unknown))
.With(new MarkupCodeGenerator());
}
public static UnclassifiedCodeSpanConstructor Code(this SpanFactory self, string content)
{
return new UnclassifiedCodeSpanConstructor(
self.Span(SpanKind.Code, content, markup: false));
}
public static SpanConstructor CodeTransition(this SpanFactory self)
{
return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, markup: false).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor CodeTransition(this SpanFactory self, string content)
{
return self.Span(SpanKind.Transition, content, markup: false).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor CodeTransition(this SpanFactory self, CSharpSymbolType type)
{
return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor CodeTransition(this SpanFactory self, string content, CSharpSymbolType type)
{
return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor CodeTransition(this SpanFactory self, VBSymbolType type)
{
return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor CodeTransition(this SpanFactory self, string content, VBSymbolType type)
{
return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor MarkupTransition(this SpanFactory self)
{
return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, markup: true).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor MarkupTransition(this SpanFactory self, string content)
{
return self.Span(SpanKind.Transition, content, markup: true).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor MarkupTransition(this SpanFactory self, HtmlSymbolType type)
{
return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor MarkupTransition(this SpanFactory self, string content, HtmlSymbolType type)
{
return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None);
}
public static SpanConstructor MetaCode(this SpanFactory self, string content)
{
return self.Span(SpanKind.MetaCode, content, markup: false);
}
public static SpanConstructor MetaCode(this SpanFactory self, string content, CSharpSymbolType type)
{
return self.Span(SpanKind.MetaCode, content, type);
}
public static SpanConstructor MetaCode(this SpanFactory self, string content, VBSymbolType type)
{
return self.Span(SpanKind.MetaCode, content, type);
}
public static SpanConstructor MetaMarkup(this SpanFactory self, string content)
{
return self.Span(SpanKind.MetaCode, content, markup: true);
}
public static SpanConstructor MetaMarkup(this SpanFactory self, string content, HtmlSymbolType type)
{
return self.Span(SpanKind.MetaCode, content, type);
}
public static SpanConstructor Comment(this SpanFactory self, string content, CSharpSymbolType type)
{
return self.Span(SpanKind.Comment, content, type);
}
public static SpanConstructor Comment(this SpanFactory self, string content, VBSymbolType type)
{
return self.Span(SpanKind.Comment, content, type);
}
public static SpanConstructor Comment(this SpanFactory self, string content, HtmlSymbolType type)
{
return self.Span(SpanKind.Comment, content, type);
}
public static SpanConstructor Markup(this SpanFactory self, string content)
{
return self.Span(SpanKind.Markup, content, markup: true).With(new MarkupCodeGenerator());
}
public static SpanConstructor Markup(this SpanFactory self, params string[] content)
{
return self.Span(SpanKind.Markup, content, markup: true).With(new MarkupCodeGenerator());
}
public static SourceLocation GetLocationAndAdvance(this SourceLocationTracker self, string content)
{
SourceLocation ret = self.CurrentLocation;
self.UpdateLocation(content);
return ret;
}
}
public class SpanFactory
{
public Func<ITextDocument, ITokenizer> MarkupTokenizerFactory { get; set; }
public Func<ITextDocument, ITokenizer> CodeTokenizerFactory { get; set; }
public SourceLocationTracker LocationTracker { get; private set; }
public static SpanFactory CreateCsHtml()
{
return new SpanFactory()
{
MarkupTokenizerFactory = doc => new HtmlTokenizer(doc),
CodeTokenizerFactory = doc => new CSharpTokenizer(doc)
};
}
public static SpanFactory CreateVbHtml()
{
return new SpanFactory()
{
MarkupTokenizerFactory = doc => new HtmlTokenizer(doc),
CodeTokenizerFactory = doc => new VBTokenizer(doc)
};
}
public SpanFactory()
{
LocationTracker = new SourceLocationTracker();
}
public SpanConstructor Span(SpanKind kind, string content, CSharpSymbolType type)
{
return CreateSymbolSpan(kind, content, st => new CSharpSymbol(st, content, type));
}
public SpanConstructor Span(SpanKind kind, string content, VBSymbolType type)
{
return CreateSymbolSpan(kind, content, st => new VBSymbol(st, content, type));
}
public SpanConstructor Span(SpanKind kind, string content, HtmlSymbolType type)
{
return CreateSymbolSpan(kind, content, st => new HtmlSymbol(st, content, type));
}
public SpanConstructor Span(SpanKind kind, string content, bool markup)
{
return new SpanConstructor(kind, Tokenize(new[] { content }, markup));
}
public SpanConstructor Span(SpanKind kind, string[] content, bool markup)
{
return new SpanConstructor(kind, Tokenize(content, markup));
}
public SpanConstructor Span(SpanKind kind, params ISymbol[] symbols)
{
return new SpanConstructor(kind, symbols);
}
private SpanConstructor CreateSymbolSpan(SpanKind kind, string content, Func<SourceLocation, ISymbol> ctor)
{
SourceLocation start = LocationTracker.CurrentLocation;
LocationTracker.UpdateLocation(content);
return new SpanConstructor(kind, new[] { ctor(start) });
}
public void Reset()
{
LocationTracker.CurrentLocation = SourceLocation.Zero;
}
private IEnumerable<ISymbol> Tokenize(IEnumerable<string> contentFragments, bool markup)
{
return contentFragments.SelectMany(fragment => Tokenize(fragment, markup));
}
private IEnumerable<ISymbol> Tokenize(string content, bool markup)
{
ITokenizer tok = MakeTokenizer(markup, new SeekableTextReader(content));
ISymbol sym;
ISymbol last = null;
while ((sym = tok.NextSymbol()) != null)
{
OffsetStart(sym, LocationTracker.CurrentLocation);
last = sym;
yield return sym;
}
LocationTracker.UpdateLocation(content);
}
private ITokenizer MakeTokenizer(bool markup, SeekableTextReader seekableTextReader)
{
if (markup)
{
return MarkupTokenizerFactory(seekableTextReader);
}
else
{
return CodeTokenizerFactory(seekableTextReader);
}
}
private void OffsetStart(ISymbol sym, SourceLocation sourceLocation)
{
sym.OffsetStart(sourceLocation);
}
}
public static class SpanConstructorExtensions
{
public static SpanConstructor Accepts(this SpanConstructor self, AcceptedCharacters accepted)
{
return self.With(eh => eh.AcceptedCharacters = accepted);
}
public static SpanConstructor AutoCompleteWith(this SpanConstructor self, string autoCompleteString)
{
return AutoCompleteWith(self, autoCompleteString, atEndOfSpan: false);
}
public static SpanConstructor AutoCompleteWith(this SpanConstructor self, string autoCompleteString, bool atEndOfSpan)
{
return self.With(new AutoCompleteEditHandler(SpanConstructor.TestTokenizer) { AutoCompleteString = autoCompleteString, AutoCompleteAtEndOfSpan = atEndOfSpan });
}
public static SpanConstructor WithEditorHints(this SpanConstructor self, EditorHints hints)
{
return self.With(eh => eh.EditorHints = hints);
}
}
public class UnclassifiedCodeSpanConstructor
{
SpanConstructor _self;
public UnclassifiedCodeSpanConstructor(SpanConstructor self)
{
_self = self;
}
public SpanConstructor AsMetaCode()
{
_self.Builder.Kind = SpanKind.MetaCode;
return _self;
}
public SpanConstructor AsStatement()
{
return _self.With(new StatementCodeGenerator());
}
public SpanConstructor AsExpression()
{
return _self.With(new ExpressionCodeGenerator());
}
public SpanConstructor AsImplicitExpression(ISet<string> keywords)
{
return AsImplicitExpression(keywords, acceptTrailingDot: false);
}
public SpanConstructor AsImplicitExpression(ISet<string> keywords, bool acceptTrailingDot)
{
return _self.With(new ImplicitExpressionEditHandler(SpanConstructor.TestTokenizer, keywords, acceptTrailingDot))
.With(new ExpressionCodeGenerator());
}
public SpanConstructor AsFunctionsBody()
{
return _self.With(new TypeMemberCodeGenerator());
}
public SpanConstructor AsNamespaceImport(string ns, int namespaceKeywordLength)
{
return _self.With(new AddImportCodeGenerator(ns, namespaceKeywordLength));
}
public SpanConstructor Hidden()
{
return _self.With(SpanCodeGenerator.Null);
}
public SpanConstructor AsBaseType(string baseType)
{
return _self.With(new SetBaseTypeCodeGenerator(baseType));
}
public SpanConstructor AsRazorDirectiveAttribute(string key, string value)
{
return _self.With(new RazorDirectiveAttributeCodeGenerator(key, value));
}
public SpanConstructor As(ISpanCodeGenerator codeGenerator)
{
return _self.With(codeGenerator);
}
}
public class SpanConstructor
{
public SpanBuilder Builder { get; private set; }
internal static IEnumerable<ISymbol> TestTokenizer(string str)
{
yield return new RawTextSymbol(SourceLocation.Zero, str);
}
public SpanConstructor(SpanKind kind, IEnumerable<ISymbol> symbols)
{
Builder = new SpanBuilder();
Builder.Kind = kind;
Builder.EditHandler = SpanEditHandler.CreateDefault(TestTokenizer);
foreach (ISymbol sym in symbols)
{
Builder.Accept(sym);
}
}
private Span Build()
{
return Builder.Build();
}
public SpanConstructor With(ISpanCodeGenerator generator)
{
Builder.CodeGenerator = generator;
return this;
}
public SpanConstructor With(SpanEditHandler handler)
{
Builder.EditHandler = handler;
return this;
}
public SpanConstructor With(Action<ISpanCodeGenerator> generatorConfigurer)
{
generatorConfigurer(Builder.CodeGenerator);
return this;
}
public SpanConstructor With(Action<SpanEditHandler> handlerConfigurer)
{
handlerConfigurer(Builder.EditHandler);
return this;
}
public static implicit operator Span(SpanConstructor self)
{
return self.Build();
}
public SpanConstructor Hidden()
{
Builder.CodeGenerator = SpanCodeGenerator.Null;
return this;
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Web.Razor.Parser;
namespace System.Web.Razor.Test.Framework
{
public abstract class VBHtmlCodeParserTestBase : CodeParserTestBase
{
protected override ISet<string> KeywordSet
{
get { return VBCodeParser.DefaultKeywords; }
}
protected override SpanFactory CreateSpanFactory()
{
return SpanFactory.CreateVbHtml();
}
public override ParserBase CreateMarkupParser()
{
return new HtmlMarkupParser();
}
public override ParserBase CreateCodeParser()
{
return new VBCodeParser();
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Web.Razor.Parser;
namespace System.Web.Razor.Test.Framework
{
public abstract class VBHtmlMarkupParserTestBase : MarkupParserTestBase
{
protected override ISet<string> KeywordSet
{
get { return VBCodeParser.DefaultKeywords; }
}
protected override SpanFactory CreateSpanFactory()
{
return SpanFactory.CreateVbHtml();
}
public override ParserBase CreateMarkupParser()
{
return new HtmlMarkupParser();
}
public override ParserBase CreateCodeParser()
{
return new VBCodeParser();
}
}
}

View File

@ -0,0 +1,291 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Web.Razor.Generator;
using Xunit;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Razor.Test.Generator
{
public class CSharpRazorCodeGeneratorTest : RazorCodeGeneratorTest<CSharpRazorCodeLanguage>
{
protected override string FileExtension
{
get { return "cshtml"; }
}
protected override string LanguageName
{
get { return "CS"; }
}
protected override string BaselineExtension
{
get { return "cs"; }
}
private const string TestPhysicalPath = @"C:\Bar.cshtml";
private const string TestVirtualPath = "~/Foo/Bar.cshtml";
[Fact]
public void ConstructorRequiresNonNullClassName()
{
Assert.ThrowsArgumentNullOrEmptyString(() => new CSharpRazorCodeGenerator(null, TestRootNamespaceName, TestPhysicalPath, CreateHost()), "className");
}
[Fact]
public void ConstructorRequiresNonEmptyClassName()
{
Assert.ThrowsArgumentNullOrEmptyString(() => new CSharpRazorCodeGenerator(String.Empty, TestRootNamespaceName, TestPhysicalPath, CreateHost()), "className");
}
[Fact]
public void ConstructorRequiresNonNullRootNamespaceName()
{
Assert.ThrowsArgumentNull(() => new CSharpRazorCodeGenerator("Foo", null, TestPhysicalPath, CreateHost()), "rootNamespaceName");
}
[Fact]
public void ConstructorAllowsEmptyRootNamespaceName()
{
new CSharpRazorCodeGenerator("Foo", String.Empty, TestPhysicalPath, CreateHost());
}
[Fact]
public void ConstructorRequiresNonNullHost()
{
Assert.ThrowsArgumentNull(() => new CSharpRazorCodeGenerator("Foo", TestRootNamespaceName, TestPhysicalPath, null), "host");
}
[Theory]
[InlineData("NestedCodeBlocks")]
[InlineData("CodeBlock")]
[InlineData("ExplicitExpression")]
[InlineData("MarkupInCodeBlock")]
[InlineData("Blocks")]
[InlineData("ImplicitExpression")]
[InlineData("Imports")]
[InlineData("ExpressionsInCode")]
[InlineData("FunctionsBlock")]
[InlineData("Templates")]
[InlineData("Sections")]
[InlineData("RazorComments")]
[InlineData("Helpers")]
[InlineData("HelpersMissingCloseParen")]
[InlineData("HelpersMissingOpenBrace")]
[InlineData("HelpersMissingOpenParen")]
[InlineData("NestedHelpers")]
[InlineData("InlineBlocks")]
[InlineData("NestedHelpers")]
[InlineData("LayoutDirective")]
[InlineData("ConditionalAttributes")]
[InlineData("ResolveUrl")]
public void CSharpCodeGeneratorCorrectlyGeneratesRunTimeCode(string testType)
{
RunTest(testType);
}
//// To regenerate individual baselines, uncomment this and set the appropriate test name in the Inline Data.
//// Please comment out again after regenerating.
//// TODO: Remove this when we go to a Source Control system that doesn't lock files, thus requiring we unlock them to regenerate them :(
//[Theory]
//[InlineData("ConditionalAttributes")]
//public void CSharpCodeGeneratorCorrectlyGeneratesRunTimeCode2(string testType)
//{
// RunTest(testType);
//}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesMappingsForRazorCommentsAtDesignTime()
{
RunTest("RazorComments", "RazorComments.DesignTime", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(4, 3, 3, 6),
/* 02 */ new GeneratedCodeMapping(5, 40, 39, 22),
/* 03 */ new GeneratedCodeMapping(6, 50, 49, 58),
/* 04 */ new GeneratedCodeMapping(12, 3, 3, 24),
/* 05 */ new GeneratedCodeMapping(13, 46, 46, 3),
/* 06 */ new GeneratedCodeMapping(15, 3, 7, 1),
/* 07 */ new GeneratedCodeMapping(15, 8, 8, 1)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesImportStatementsAtDesignTime()
{
RunTest("Imports", "Imports.DesignTime", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 2, 1, 15),
/* 02 */ new GeneratedCodeMapping(2, 2, 1, 32),
/* 03 */ new GeneratedCodeMapping(3, 2, 1, 12),
/* 04 */ new GeneratedCodeMapping(5, 30, 30, 21),
/* 05 */ new GeneratedCodeMapping(6, 36, 36, 20),
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesFunctionsBlocksAtDesignTime()
{
RunTest("FunctionsBlock", "FunctionsBlock.DesignTime", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 13, 13, 4),
/* 02 */ new GeneratedCodeMapping(5, 13, 13, 104),
/* 03 */ new GeneratedCodeMapping(12, 26, 26, 11)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesHiddenSpansWithinCode()
{
RunTest("HiddenSpansInCode", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>
{
/* 01 */ new GeneratedCodeMapping(1, 3, 3, 6),
/* 02 */ new GeneratedCodeMapping(2, 6, 6, 5)
});
}
[Fact]
public void CSharpCodeGeneratorGeneratesCodeWithParserErrorsInDesignTimeMode()
{
RunTest("ParserError", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 3, 3, 31)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesInheritsAtRuntime()
{
RunTest("Inherits", baselineName: "Inherits.Runtime");
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesInheritsAtDesigntime()
{
RunTest("Inherits", baselineName: "Inherits.Designtime", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 2, 7, 5),
/* 02 */ new GeneratedCodeMapping(3, 11, 11, 25),
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForUnfinishedExpressionsInCode()
{
RunTest("UnfinishedExpressionInCode", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 3, 3, 2),
/* 02 */ new GeneratedCodeMapping(2, 2, 7, 9),
/* 03 */ new GeneratedCodeMapping(2, 11, 11, 2)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasMarkupAndExpressions()
{
RunTest("DesignTime", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(2, 14, 13, 36),
/* 02 */ new GeneratedCodeMapping(3, 23, 23, 1),
/* 03 */ new GeneratedCodeMapping(3, 28, 28, 15),
/* 04 */ new GeneratedCodeMapping(8, 3, 7, 12),
/* 05 */ new GeneratedCodeMapping(9, 2, 7, 4),
/* 06 */ new GeneratedCodeMapping(9, 15, 15, 3),
/* 07 */ new GeneratedCodeMapping(9, 26, 26, 1),
/* 08 */ new GeneratedCodeMapping(14, 6, 7, 3),
/* 09 */ new GeneratedCodeMapping(17, 9, 24, 7),
/* 10 */ new GeneratedCodeMapping(17, 16, 16, 26),
/* 11 */ new GeneratedCodeMapping(19, 19, 19, 9),
/* 12 */ new GeneratedCodeMapping(21, 1, 1, 1)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForImplicitExpressionStartedAtEOF()
{
RunTest("ImplicitExpressionAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(3, 2, 7, 0)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForExplicitExpressionStartedAtEOF()
{
RunTest("ExplicitExpressionAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(3, 3, 7, 0)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForCodeBlockStartedAtEOF()
{
RunTest("CodeBlockAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 3, 3, 0)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyImplicitExpression()
{
RunTest("EmptyImplicitExpression", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(3, 2, 7, 0)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyImplicitExpressionInCode()
{
RunTest("EmptyImplicitExpressionInCode", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 3, 3, 6),
/* 02 */ new GeneratedCodeMapping(2, 6, 7, 0),
/* 03 */ new GeneratedCodeMapping(2, 6, 6, 2)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyExplicitExpression()
{
RunTest("EmptyExplicitExpression", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(3, 3, 7, 0)
});
}
[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyCodeBlock()
{
RunTest("EmptyCodeBlock", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(3, 3, 3, 0)
});
}
[Fact]
public void CSharpCodeGeneratorDoesNotRenderLinePragmasIfGenerateLinePragmasIsSetToFalse()
{
RunTest("NoLinePragmas", generatePragmas: false);
}
[Fact]
public void CSharpCodeGeneratorRendersHelpersBlockCorrectlyWhenInstanceHelperRequested()
{
RunTest("Helpers", baselineName: "Helpers.Instance", hostConfig: h => h.StaticHelpers = false);
}
[Fact]
public void CSharpCodeGeneratorCorrectlyInstrumentsRazorCodeWhenInstrumentationRequested()
{
RunTest("Instrumented", hostConfig: host =>
{
host.EnableInstrumentation = true;
host.InstrumentedSourceFilePath = String.Format("~/{0}.cshtml", host.DefaultClassName);
});
}
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Razor.Generator;
using Xunit;
namespace System.Web.Razor.Test.Generator
{
public class GeneratedCodeMappingTest
{
[Fact]
public void GeneratedCodeMappingsAreEqualIfDataIsEqual()
{
GeneratedCodeMapping left = new GeneratedCodeMapping(12, 34, 56, 78);
GeneratedCodeMapping right = new GeneratedCodeMapping(12, 34, 56, 78);
Assert.True(left == right);
Assert.True(left.Equals(right));
Assert.True(right.Equals(left));
Assert.True(Equals(left, right));
}
[Fact]
public void GeneratedCodeMappingsAreNotEqualIfCodeLengthIsNotEqual()
{
GeneratedCodeMapping left = new GeneratedCodeMapping(12, 34, 56, 87);
GeneratedCodeMapping right = new GeneratedCodeMapping(12, 34, 56, 78);
Assert.False(left == right);
Assert.False(left.Equals(right));
Assert.False(right.Equals(left));
Assert.False(Equals(left, right));
}
[Fact]
public void GeneratedCodeMappingsAreNotEqualIfStartGeneratedColumnIsNotEqual()
{
GeneratedCodeMapping left = new GeneratedCodeMapping(12, 34, 56, 87);
GeneratedCodeMapping right = new GeneratedCodeMapping(12, 34, 65, 87);
Assert.False(left == right);
Assert.False(left.Equals(right));
Assert.False(right.Equals(left));
Assert.False(Equals(left, right));
}
[Fact]
public void GeneratedCodeMappingsAreNotEqualIfStartColumnIsNotEqual()
{
GeneratedCodeMapping left = new GeneratedCodeMapping(12, 34, 56, 87);
GeneratedCodeMapping right = new GeneratedCodeMapping(12, 43, 56, 87);
Assert.False(left == right);
Assert.False(left.Equals(right));
Assert.False(right.Equals(left));
Assert.False(Equals(left, right));
}
[Fact]
public void GeneratedCodeMappingsAreNotEqualIfStartLineIsNotEqual()
{
GeneratedCodeMapping left = new GeneratedCodeMapping(12, 34, 56, 87);
GeneratedCodeMapping right = new GeneratedCodeMapping(21, 34, 56, 87);
Assert.False(left == right);
Assert.False(left.Equals(right));
Assert.False(right.Equals(left));
Assert.False(Equals(left, right));
}
}
}

View File

@ -0,0 +1,148 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
//#define GENERATE_BASELINES
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.Razor.Generator;
using System.Web.Razor.Test.Utils;
using System.Web.WebPages.TestUtils;
using Xunit;
namespace System.Web.Razor.Test.Generator
{
public abstract class RazorCodeGeneratorTest<TLanguage>
where TLanguage : RazorCodeLanguage, new()
{
protected static readonly string TestRootNamespaceName = "TestOutput";
protected abstract string FileExtension { get; }
protected abstract string LanguageName { get; }
protected abstract string BaselineExtension { get; }
protected RazorEngineHost CreateHost()
{
return new RazorEngineHost(new TLanguage());
}
protected void RunTest(string name, string baselineName = null, bool generatePragmas = true, bool designTimeMode = false, IList<GeneratedCodeMapping> expectedDesignTimePragmas = null, Action<RazorEngineHost> hostConfig = null)
{
// Load the test files
if (baselineName == null)
{
baselineName = name;
}
string source = TestFile.Create(String.Format("CodeGenerator.{1}.Source.{0}.{2}", name, LanguageName, FileExtension)).ReadAllText();
string expectedOutput = TestFile.Create(String.Format("CodeGenerator.{1}.Output.{0}.{2}", baselineName, LanguageName, BaselineExtension)).ReadAllText();
// Set up the host and engine
RazorEngineHost host = CreateHost();
host.NamespaceImports.Add("System");
host.DesignTimeMode = designTimeMode;
host.StaticHelpers = true;
host.DefaultClassName = name;
// Add support for templates, etc.
host.GeneratedClassContext = new GeneratedClassContext(GeneratedClassContext.DefaultExecuteMethodName,
GeneratedClassContext.DefaultWriteMethodName,
GeneratedClassContext.DefaultWriteLiteralMethodName,
"WriteTo",
"WriteLiteralTo",
"Template",
"DefineSection",
"BeginContext",
"EndContext")
{
LayoutPropertyName = "Layout",
ResolveUrlMethodName = "Href"
};
if (hostConfig != null)
{
hostConfig(host);
}
RazorTemplateEngine engine = new RazorTemplateEngine(host);
// Generate code for the file
GeneratorResults results = null;
using (StringTextBuffer buffer = new StringTextBuffer(source))
{
results = engine.GenerateCode(buffer, className: name, rootNamespace: TestRootNamespaceName, sourceFileName: generatePragmas ? String.Format("{0}.{1}", name, FileExtension) : null);
}
// Generate code
CodeCompileUnit ccu = results.GeneratedCode;
CodeDomProvider codeProvider = (CodeDomProvider)Activator.CreateInstance(host.CodeLanguage.CodeDomProviderType);
CodeGeneratorOptions options = new CodeGeneratorOptions();
// Both run-time and design-time use these settings. See:
// * $/Dev10/pu/SP_WebTools/venus/html/Razor/Impl/RazorCodeGenerator.cs:204
// * $/Dev10/Releases/RTMRel/ndp/fx/src/xsp/System/Web/Compilation/BuildManagerHost.cs:373
options.BlankLinesBetweenMembers = false;
options.IndentString = String.Empty;
StringBuilder output = new StringBuilder();
using (StringWriter writer = new StringWriter(output))
{
codeProvider.GenerateCodeFromCompileUnit(ccu, writer, options);
}
WriteBaseline(String.Format(@"test\System.Web.Razor.Test\TestFiles\CodeGenerator\{0}\Output\{1}.{2}", LanguageName, baselineName, BaselineExtension), MiscUtils.StripRuntimeVersion(output.ToString()));
// Verify code against baseline
#if !GENERATE_BASELINES
Assert.Equal(expectedOutput, MiscUtils.StripRuntimeVersion(output.ToString()));
#endif
// Verify design-time pragmas
if (designTimeMode)
{
Assert.True(expectedDesignTimePragmas != null || results.DesignTimeLineMappings == null || results.DesignTimeLineMappings.Count == 0);
Assert.True(expectedDesignTimePragmas == null || (results.DesignTimeLineMappings != null && results.DesignTimeLineMappings.Count > 0));
if (expectedDesignTimePragmas != null)
{
Assert.Equal(
expectedDesignTimePragmas.ToArray(),
results.DesignTimeLineMappings
.OrderBy(p => p.Key)
.Select(p => p.Value)
.ToArray());
}
}
}
[Conditional("GENERATE_BASELINES")]
private void WriteBaseline(string baselineFile, string output)
{
string root = RecursiveFind("Runtime.sln", Path.GetFullPath("."));
string baselinePath = Path.Combine(root, baselineFile);
// Update baseline
// IMPORTANT! Replace this path with the local path on your machine to the baseline files!
if (File.Exists(baselinePath))
{
File.Delete(baselinePath);
}
File.WriteAllText(baselinePath, output.ToString());
}
private string RecursiveFind(string path, string start)
{
string test = Path.Combine(start, path);
if (File.Exists(test))
{
return start;
}
else
{
return RecursiveFind(path, new DirectoryInfo(start).Parent.FullName);
}
}
}
}

View File

@ -0,0 +1,280 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Web.Razor.Generator;
using Xunit;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Razor.Test.Generator
{
public class VBRazorCodeGeneratorTest : RazorCodeGeneratorTest<VBRazorCodeLanguage>
{
private const string TestPhysicalPath = @"C:\Bar.vbhtml";
private const string TestVirtualPath = "~/Foo/Bar.vbhtml";
protected override string FileExtension
{
get { return "vbhtml"; }
}
protected override string LanguageName
{
get { return "VB"; }
}
protected override string BaselineExtension
{
get { return "vb"; }
}
[Fact]
public void ConstructorRequiresNonNullClassName()
{
Assert.ThrowsArgumentNullOrEmptyString(() => new VBRazorCodeGenerator(null, TestRootNamespaceName, TestPhysicalPath, CreateHost()), "className");
}
[Fact]
public void ConstructorRequiresNonEmptyClassName()
{
Assert.ThrowsArgumentNullOrEmptyString(() => new VBRazorCodeGenerator(String.Empty, TestRootNamespaceName, TestPhysicalPath, CreateHost()), "className");
}
[Fact]
public void ConstructorRequiresNonNullRootNamespaceName()
{
Assert.ThrowsArgumentNull(() => new VBRazorCodeGenerator("Foo", null, TestPhysicalPath, CreateHost()), "rootNamespaceName");
}
[Fact]
public void ConstructorAllowsEmptyRootNamespaceName()
{
new VBRazorCodeGenerator("Foo", String.Empty, TestPhysicalPath, CreateHost());
}
[Fact]
public void ConstructorRequiresNonNullHost()
{
Assert.ThrowsArgumentNull(() => new VBRazorCodeGenerator("Foo", TestRootNamespaceName, TestPhysicalPath, null), "host");
}
[Theory]
[InlineData("NestedCodeBlocks")]
[InlineData("NestedCodeBlocks")]
[InlineData("CodeBlock")]
[InlineData("ExplicitExpression")]
[InlineData("MarkupInCodeBlock")]
[InlineData("Blocks")]
[InlineData("ImplicitExpression")]
[InlineData("Imports")]
[InlineData("ExpressionsInCode")]
[InlineData("FunctionsBlock")]
[InlineData("Options")]
[InlineData("Templates")]
[InlineData("RazorComments")]
[InlineData("Sections")]
[InlineData("Helpers")]
[InlineData("HelpersMissingCloseParen")]
[InlineData("HelpersMissingOpenParen")]
[InlineData("NestedHelpers")]
[InlineData("LayoutDirective")]
[InlineData("ConditionalAttributes")]
[InlineData("ResolveUrl")]
public void VBCodeGeneratorCorrectlyGeneratesRunTimeCode(string testName)
{
RunTest(testName);
}
//// To regenerate individual baselines, uncomment this and set the appropriate test name in the Inline Data.
//// Please comment out again after regenerating.
//// TODO: Remove this when we go to a Source Control system that doesn't lock files, thus requiring we unlock them to regenerate them :(
//[Theory]
//[InlineData("RazorComments")]
//public void VBCodeGeneratorCorrectlyGeneratesRunTimeCode2(string testType)
//{
// RunTest(testType);
//}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesMappingsForRazorCommentsAtDesignTime()
{
// (4, 6) -> (?, 6) [6]
// ( 5, 40) -> (?, 39) [2]
// ( 8, 6) -> (?, 6) [33]
// ( 9, 46) -> (?, 46) [3]
// ( 12, 3) -> (?, 7) [3]
// ( 12, 8) -> (?, 8) [1]
RunTest("RazorComments", "RazorComments.DesignTime", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(4, 6, 6, 6),
/* 02 */ new GeneratedCodeMapping(5, 40, 39, 2),
/* 03 */ new GeneratedCodeMapping(8, 6, 6, 33),
/* 04 */ new GeneratedCodeMapping(9, 46, 46, 3),
/* 05 */ new GeneratedCodeMapping(12, 3, 7, 1),
/* 06 */ new GeneratedCodeMapping(12, 8, 8, 1)
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesHelperMissingNameAtDesignTime()
{
RunTest("HelpersMissingName", designTimeMode: true);
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesImportStatementsAtDesignTimeButCannotWrapPragmasAroundImportStatement()
{
RunTest("Imports", "Imports.DesignTime", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 2, 1, 19),
/* 02 */ new GeneratedCodeMapping(2, 2, 1, 36),
/* 03 */ new GeneratedCodeMapping(3, 2, 1, 16),
/* 04 */ new GeneratedCodeMapping(5, 30, 30, 22),
/* 05 */ new GeneratedCodeMapping(6, 36, 36, 21),
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesFunctionsBlocksAtDesignTime()
{
RunTest("FunctionsBlock", "FunctionsBlock.DesignTime", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 11, 11, 4),
/* 02 */ new GeneratedCodeMapping(5, 11, 11, 129),
/* 03 */ new GeneratedCodeMapping(12, 26, 26, 11)
});
}
[Fact]
public void VBCodeGeneratorGeneratesCodeWithParserErrorsInDesignTimeMode()
{
RunTest("ParserError", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 6, 6, 16)
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesInheritsAtRuntime()
{
RunTest("Inherits", baselineName: "Inherits.Runtime");
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesInheritsAtDesigntime()
{
RunTest("Inherits", baselineName: "Inherits.Designtime", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 11, 25, 27)
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForUnfinishedExpressionsInCode()
{
RunTest("UnfinishedExpressionInCode", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 6, 6, 2),
/* 02 */ new GeneratedCodeMapping(2, 2, 7, 9),
/* 03 */ new GeneratedCodeMapping(2, 11, 11, 2)
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasMarkupAndExpressions()
{
RunTest("DesignTime", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(2, 14, 13, 17),
/* 02 */ new GeneratedCodeMapping(3, 20, 20, 1),
/* 03 */ new GeneratedCodeMapping(3, 25, 25, 20),
/* 04 */ new GeneratedCodeMapping(8, 3, 7, 12),
/* 05 */ new GeneratedCodeMapping(9, 2, 7, 4),
/* 06 */ new GeneratedCodeMapping(9, 16, 16, 3),
/* 07 */ new GeneratedCodeMapping(9, 27, 27, 1),
/* 08 */ new GeneratedCodeMapping(14, 6, 7, 3),
/* 09 */ new GeneratedCodeMapping(17, 9, 24, 5),
/* 10 */ new GeneratedCodeMapping(17, 14, 14, 28),
/* 11 */ new GeneratedCodeMapping(19, 20, 20, 14)
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForImplicitExpressionStartedAtEOF()
{
RunTest("ImplicitExpressionAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(3, 2, 7, 0)
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForExplicitExpressionStartedAtEOF()
{
RunTest("ExplicitExpressionAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(3, 3, 7, 0)
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForCodeBlockStartedAtEOF()
{
RunTest("CodeBlockAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(3, 6, 6, 0)
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyImplicitExpression()
{
RunTest("EmptyImplicitExpression", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(3, 2, 7, 0)
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyImplicitExpressionInCode()
{
RunTest("EmptyImplicitExpressionInCode", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(1, 6, 6, 6),
/* 02 */ new GeneratedCodeMapping(2, 6, 7, 0),
/* 03 */ new GeneratedCodeMapping(2, 6, 6, 2)
});
}
[Fact]
public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyExplicitExpression()
{
RunTest("EmptyExplicitExpression", designTimeMode: true, expectedDesignTimePragmas: new List<GeneratedCodeMapping>()
{
/* 01 */ new GeneratedCodeMapping(3, 3, 7, 0)
});
}
[Fact]
public void VBCodeGeneratorDoesNotRenderLinePragmasIfGenerateLinePragmasIsSetToFalse()
{
RunTest("NoLinePragmas", generatePragmas: false);
}
[Fact]
public void VBCodeGeneratorRendersHelpersBlockCorrectlyWhenInstanceHelperRequested()
{
RunTest("Helpers", baselineName: "Helpers.Instance", hostConfig: h => h.StaticHelpers = false);
}
[Fact]
public void VBCodeGeneratorCorrectlyInstrumentsRazorCodeWhenInstrumentationRequested()
{
RunTest("Instrumented", hostConfig: host =>
{
host.EnableInstrumentation = true;
host.InstrumentedSourceFilePath = String.Format("~/{0}.vbhtml", host.DefaultClassName);
});
}
}
}

View File

@ -0,0 +1,124 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Linq;
using System.Web.Razor.Generator;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Test.Framework;
using System.Web.Razor.Text;
using System.Web.WebPages.TestUtils;
using Xunit;
namespace System.Web.Razor.Test.Parser
{
public class BlockTest
{
[Fact]
public void ConstructorWithBlockBuilderSetsParent()
{
// Arrange
BlockBuilder builder = new BlockBuilder() { Type = BlockType.Comment };
Span span = new SpanBuilder() { Kind = SpanKind.Code }.Build();
builder.Children.Add(span);
// Act
Block block = builder.Build();
// Assert
Assert.Same(block, span.Parent);
}
[Fact]
public void ConstructorCopiesBasicValuesFromBlockBuilder()
{
// Arrange
BlockBuilder builder = new BlockBuilder()
{
Name = "Foo",
Type = BlockType.Helper
};
// Act
Block actual = builder.Build();
// Assert
Assert.Equal("Foo", actual.Name);
Assert.Equal(BlockType.Helper, actual.Type);
}
[Fact]
public void ConstructorTransfersInstanceOfCodeGeneratorFromBlockBuilder()
{
// Arrange
IBlockCodeGenerator expected = new ExpressionCodeGenerator();
BlockBuilder builder = new BlockBuilder()
{
Type = BlockType.Helper,
CodeGenerator = expected
};
// Act
Block actual = builder.Build();
// Assert
Assert.Same(expected, actual.CodeGenerator);
}
[Fact]
public void ConstructorTransfersChildrenFromBlockBuilder()
{
// Arrange
Span expected = new SpanBuilder() { Kind = SpanKind.Code }.Build();
BlockBuilder builder = new BlockBuilder()
{
Type = BlockType.Functions
};
builder.Children.Add(expected);
// Act
Block block = builder.Build();
// Assert
Assert.Same(expected, block.Children.Single());
}
[Fact]
public void LocateOwnerReturnsNullIfNoSpanReturnsTrueForOwnsSpan()
{
// Arrange
var factory = SpanFactory.CreateCsHtml();
Block block = new MarkupBlock(
factory.Markup("Foo "),
new StatementBlock(
factory.CodeTransition(),
factory.Code("bar").AsStatement()),
factory.Markup(" Baz"));
TextChange change = new TextChange(128, 1, new StringTextBuffer("Foo @bar Baz"), 1, new StringTextBuffer("Foo @bor Baz"));
// Act
Span actual = block.LocateOwner(change);
// Assert
Assert.Null(actual);
}
[Fact]
public void LocateOwnerReturnsNullIfChangeCrossesMultipleSpans()
{
// Arrange
var factory = SpanFactory.CreateCsHtml();
Block block = new MarkupBlock(
factory.Markup("Foo "),
new StatementBlock(
factory.CodeTransition(),
factory.Code("bar").AsStatement()),
factory.Markup(" Baz"));
TextChange change = new TextChange(4, 10, new StringTextBuffer("Foo @bar Baz"), 10, new StringTextBuffer("Foo @bor Baz"));
// Act
Span actual = block.LocateOwner(change);
// Assert
Assert.Null(actual);
}
}
}

Some files were not shown because too many files have changed in this diff Show More