Modifications to support providing tag descriptors from derived hosts

This commit is contained in:
Pranav K 2014-10-13 05:44:39 -07:00
parent f8020e8e85
commit 99fe8294e9
8 changed files with 222 additions and 96 deletions

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.Parser.TagHelpers;
using Microsoft.AspNet.Razor.Parser.TagHelpers.Internal; using Microsoft.AspNet.Razor.Parser.TagHelpers.Internal;
using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Text;
@ -17,34 +18,61 @@ namespace Microsoft.AspNet.Razor.Parser
{ {
public class RazorParser public class RazorParser
{ {
private ITagHelperDescriptorResolver _tagHelperDescriptorResolver; /// <summary>
/// Initializes a new instance of <see cref="RazorParser"/>.
/// </summary>
/// <param name="codeParser">The <see cref="ParserBase"/> used for parsing code content.</param>
/// <param name="markupParser">The <see cref="ParserBase"/> used for parsing markpup content.</param>
/// <param name="tagHelperDescriptorResolver">The <see cref=ITagHelperDescriptorResolver"/> used to resolve
/// <see cref="TagHelperDescriptor"/>s.</param>
public RazorParser([NotNull] ParserBase codeParser, public RazorParser([NotNull] ParserBase codeParser,
[NotNull] ParserBase markupParser, [NotNull] ParserBase markupParser,
ITagHelperDescriptorResolver tagHelperDescriptorResolver) ITagHelperDescriptorResolver tagHelperDescriptorResolver)
: this(codeParser,
markupParser,
tagHelperDescriptorResolver,
GetDefaultRewriters(markupParser))
{ {
_tagHelperDescriptorResolver = tagHelperDescriptorResolver;
MarkupParser = markupParser;
CodeParser = codeParser;
Optimizers = new List<ISyntaxTreeRewriter>()
{
// TODO: Modify the below WhiteSpaceRewriter & ConditionalAttributeCollapser to handle
// TagHelperBlock's: https://github.com/aspnet/Razor/issues/117
// Move whitespace from start of expression block to markup
new WhiteSpaceRewriter(MarkupParser.BuildSpan),
// Collapse conditional attributes where the entire value is literal
new ConditionalAttributeCollapser(MarkupParser.BuildSpan),
};
} }
internal ParserBase CodeParser { get; private set; } /// <summary>
internal ParserBase MarkupParser { get; private set; } /// Initializes a new instance of <see cref="RazorParser"/> from the specified <paramref name="parser" />.
internal IList<ISyntaxTreeRewriter> Optimizers { get; private set; } /// </summary>
/// <param name="parser">The <see cref="RazorParser"/> to copy values from.</param>
public RazorParser([NotNull] RazorParser parser)
: this(parser.CodeParser, parser.MarkupParser, parser.TagHelperDescriptorResolver, parser.Optimizers)
{
DesignTimeMode = parser.DesignTimeMode;
}
private RazorParser(ParserBase codeParser,
ParserBase markupParser,
ITagHelperDescriptorResolver tagHelperDescriptorResolver,
IEnumerable<ISyntaxTreeRewriter> optimizers)
{
CodeParser = codeParser;
MarkupParser = markupParser;
TagHelperDescriptorResolver = tagHelperDescriptorResolver;
Optimizers = optimizers.ToList();
}
public bool DesignTimeMode { get; set; } public bool DesignTimeMode { get; set; }
/// <summary>
/// Gets the <see cref=ITagHelperDescriptorResolver"/> used to resolve
/// <see cref="TagHelperDescriptor"/>s.</param>
/// </summary>
protected ITagHelperDescriptorResolver TagHelperDescriptorResolver { get; private set; }
// Internal for unit testing
internal ParserBase CodeParser { get; private set; }
// Internal for unit testing
internal ParserBase MarkupParser { get; private set; }
// Internal for unit testing
internal List<ISyntaxTreeRewriter> Optimizers { get; private set; }
public virtual void Parse(TextReader input, ParserVisitor visitor) public virtual void Parse(TextReader input, ParserVisitor visitor)
{ {
Parse(new SeekableTextReader(input), visitor); Parse(new SeekableTextReader(input), visitor);
@ -142,10 +170,10 @@ namespace Microsoft.AspNet.Razor.Parser
rewriter.Rewrite(rewritingContext); rewriter.Rewrite(rewritingContext);
} }
if (_tagHelperDescriptorResolver != null) if (TagHelperDescriptorResolver != null)
{ {
var tagHelperRegistrationVisitor = new TagHelperRegistrationVisitor(_tagHelperDescriptorResolver); var descriptors = GetTagHelperDescriptors(rewritingContext.SyntaxTree);
var tagHelperProvider = tagHelperRegistrationVisitor.CreateProvider(rewritingContext.SyntaxTree); var tagHelperProvider = new TagHelperDescriptorProvider(descriptors);
var tagHelperParseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperProvider); var tagHelperParseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperProvider);
// Rewrite the document to utilize tag helpers // Rewrite the document to utilize tag helpers
@ -173,5 +201,31 @@ namespace Microsoft.AspNet.Razor.Parser
// Return the new result // Return the new result
return new ParserResults(syntaxTree, errors); return new ParserResults(syntaxTree, errors);
} }
/// <summary>
/// Returns a sequence of <see cref="TagHelperDescriptor"/>s for tag helpers that are registered in the
/// specified <paramref name="documentRoot"/>.
/// </summary>
/// <param name="documentRoot">The <see cref="Block"/> to scan for tag helper registrations in.</param>
/// <returns></returns>
protected virtual IEnumerable<TagHelperDescriptor> GetTagHelperDescriptors([NotNull] Block documentRoot)
{
var tagHelperRegistrationVisitor = new TagHelperRegistrationVisitor(TagHelperDescriptorResolver);
return tagHelperRegistrationVisitor.GetDescriptors(documentRoot);
}
private static IEnumerable<ISyntaxTreeRewriter> GetDefaultRewriters(ParserBase markupParser)
{
return new ISyntaxTreeRewriter[]
{
// TODO: Modify the below WhiteSpaceRewriter & ConditionalAttributeCollapser to handle
// TagHelperBlock's: https://github.com/aspnet/Razor/issues/117
// Move whitespace from start of expression block to markup
new WhiteSpaceRewriter(markupParser.BuildSpan),
// Collapse conditional attributes where the entire value is literal
new ConditionalAttributeCollapser(markupParser.BuildSpan),
};
}
} }
} }

View File

@ -7,27 +7,31 @@ using Microsoft.AspNet.Razor.Generator;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.TagHelpers;
namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal namespace Microsoft.AspNet.Razor.Parser.TagHelpers
{ {
/// <summary>
/// A <see cref="ParserVisitor"/> that generates <see cref="TagHelperDescriptor"/>s from
/// tag helper code generators in a Razor document.
/// </summary>
public class TagHelperRegistrationVisitor : ParserVisitor public class TagHelperRegistrationVisitor : ParserVisitor
{ {
private readonly ITagHelperDescriptorResolver _descriptorResolver; private readonly ITagHelperDescriptorResolver _descriptorResolver;
private HashSet<TagHelperDescriptor> _descriptors; private List<TagHelperDescriptor> _descriptors;
public TagHelperRegistrationVisitor(ITagHelperDescriptorResolver descriptorResolver) public TagHelperRegistrationVisitor(ITagHelperDescriptorResolver descriptorResolver)
{ {
_descriptorResolver = descriptorResolver; _descriptorResolver = descriptorResolver;
} }
public TagHelperDescriptorProvider CreateProvider(Block root) public IEnumerable<TagHelperDescriptor> GetDescriptors([NotNull] Block root)
{ {
_descriptors = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default); _descriptors = new List<TagHelperDescriptor>();
// This will recurse through the syntax tree. // This will recurse through the syntax tree.
VisitBlock(root); VisitBlock(root);
return new TagHelperDescriptorProvider(_descriptors); return _descriptors;
} }
public override void VisitSpan(Span span) public override void VisitSpan(Span span)
@ -50,10 +54,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
var descriptors = _descriptorResolver.Resolve(addGenerator.LookupText); var descriptors = _descriptorResolver.Resolve(addGenerator.LookupText);
// Add all the found descriptors to our HashSet. // Add all the found descriptors to our HashSet.
foreach (var descriptor in descriptors) _descriptors.AddRange(descriptors);
{
_descriptors.Add(descriptor);
}
} }
} }
} }

View File

@ -165,6 +165,19 @@ namespace Microsoft.AspNet.Razor
return null; return null;
} }
/// <summary>
/// Provides an opportunity for derived types to modify the instance of <see cref="RazorParser"/>
/// used by the <see cref="RazorTemplateEngine"/> to parse the Razor tree.
/// </summary>
/// <param name="incomingRazorParser">The <see cref="RazorParser"/></param>
/// <param name="sourceFileName">The file name of the Razor file being parsed.</param>
/// <returns>Either the same code parser, after modifications, or a different code parser.</returns>
public virtual RazorParser DecorateRazorParser([NotNull] RazorParser incomingRazorParser,
string sourceFileName)
{
return incomingRazorParser;
}
/// <summary> /// <summary>
/// Gets an instance of the code parser and is provided an opportunity to decorate or replace it /// Gets an instance of the code parser and is provided an opportunity to decorate or replace it
/// </summary> /// </summary>

View File

@ -68,26 +68,28 @@ namespace Microsoft.AspNet.Razor
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Input object would be disposed if we dispose the wrapper. We don't own the input so we don't want to dispose it")] [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Input object would be disposed if we dispose the wrapper. We don't own the input so we don't want to dispose it")]
public ParserResults ParseTemplate(ITextBuffer input, CancellationToken? cancelToken) public ParserResults ParseTemplate(ITextBuffer input, CancellationToken? cancelToken)
{ {
return ParseTemplateCore(input.ToDocument(), cancelToken); return ParseTemplateCore(input.ToDocument(), sourceFileName: null, cancelToken: cancelToken);
} }
// See ParseTemplate(ITextBuffer, CancellationToken?), // 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 and BufferingTextReader)
public ParserResults ParseTemplate(TextReader input) public ParserResults ParseTemplate(TextReader input, string sourceFileName)
{ {
return ParseTemplate(input, null); return ParseTemplateCore(new SeekableTextReader(input), sourceFileName, cancelToken: null);
} }
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Input object would be disposed if we dispose the wrapper. We don't own the input so we don't want to dispose it")] [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Input object would be disposed if we dispose the wrapper. We don't own the input so we don't want to dispose it")]
public ParserResults ParseTemplate(TextReader input, CancellationToken? cancelToken) public ParserResults ParseTemplate(TextReader input, CancellationToken? cancelToken)
{ {
return ParseTemplateCore(new SeekableTextReader(input), cancelToken); return ParseTemplateCore(new SeekableTextReader(input), sourceFileName: null, cancelToken: cancelToken);
} }
protected internal virtual ParserResults ParseTemplateCore(ITextDocument input, CancellationToken? cancelToken) protected internal virtual ParserResults ParseTemplateCore(ITextDocument input,
string sourceFileName,
CancellationToken? cancelToken)
{ {
// Construct the parser // Construct the parser
RazorParser parser = CreateParser(); var parser = CreateParser(sourceFileName);
Debug.Assert(parser != null); Debug.Assert(parser != null);
return parser.Parse(input); return parser.Parse(input);
} }
@ -243,7 +245,7 @@ namespace Microsoft.AspNet.Razor
rootNamespace = (rootNamespace ?? Host.DefaultNamespace) ?? DefaultNamespace; rootNamespace = (rootNamespace ?? Host.DefaultNamespace) ?? DefaultNamespace;
// Run the parser // Run the parser
var parser = CreateParser(); var parser = CreateParser(sourceFileName);
Debug.Assert(parser != null); Debug.Assert(parser != null);
var results = parser.Parse(input); var results = parser.Parse(input);
@ -267,17 +269,19 @@ namespace Microsoft.AspNet.Razor
Host.CodeLanguage.CreateCodeGenerator(className, rootNamespace, sourceFileName, Host)); Host.CodeLanguage.CreateCodeGenerator(className, rootNamespace, sourceFileName, Host));
} }
protected internal virtual RazorParser CreateParser() protected internal virtual RazorParser CreateParser(string sourceFileName)
{ {
var codeParser = Host.CodeLanguage.CreateCodeParser(); var codeParser = Host.CodeLanguage.CreateCodeParser();
var markupParser = Host.CreateMarkupParser(); var markupParser = Host.CreateMarkupParser();
return new RazorParser(Host.DecorateCodeParser(codeParser), var parser = new RazorParser(Host.DecorateCodeParser(codeParser),
Host.DecorateMarkupParser(markupParser), Host.DecorateMarkupParser(markupParser),
Host.TagHelperDescriptorResolver) Host.TagHelperDescriptorResolver)
{ {
DesignTimeMode = Host.DesignTimeMode DesignTimeMode = Host.DesignTimeMode
}; };
return Host.DecorateRazorParser(parser, sourceFileName);
} }
protected internal virtual CodeBuilder CreateCodeBuilder(CodeBuilderContext context) protected internal virtual CodeBuilder CreateCodeBuilder(CodeBuilderContext context)

View File

@ -18,11 +18,6 @@ namespace Microsoft.AspNet.Razor.TagHelpers
/// </summary> /// </summary>
public static readonly TagHelperDescriptorComparer Default = new TagHelperDescriptorComparer(); public static readonly TagHelperDescriptorComparer Default = new TagHelperDescriptorComparer();
// Internal for testing
internal TagHelperDescriptorComparer()
{
}
/// <summary> /// <summary>
/// Determines if the two given tag helpers are equal. /// Determines if the two given tag helpers are equal.
/// </summary> /// </summary>

View File

@ -57,9 +57,9 @@ namespace Microsoft.AspNet.Razor.Test.Generator
_tagHelperDescriptors = tagHelperDescriptors; _tagHelperDescriptors = tagHelperDescriptors;
} }
protected internal override RazorParser CreateParser() protected internal override RazorParser CreateParser(string fileName)
{ {
var parser = base.CreateParser(); var parser = base.CreateParser(fileName);
return new RazorParser(parser.CodeParser, return new RazorParser(parser.CodeParser,
parser.MarkupParser, parser.MarkupParser,

View File

@ -2,10 +2,15 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.AspNet.Razor.Test.Framework; using Microsoft.AspNet.Razor.Test.Framework;
using Moq;
using Moq.Protected;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Razor.Test.Parser namespace Microsoft.AspNet.Razor.Test.Parser
@ -18,9 +23,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser
var factory = SpanFactory.CreateCsHtml(); var factory = SpanFactory.CreateCsHtml();
// Arrange // Arrange
RazorParser parser = new RazorParser(new CSharpCodeParser(), var parser = new RazorParser(new CSharpCodeParser(),
new HtmlMarkupParser(), new HtmlMarkupParser(),
tagHelperDescriptorResolver: null); tagHelperDescriptorResolver: null);
// Act/Assert // Act/Assert
ParserTestBase.EvaluateResults(parser.Parse(new StringReader("foo @bar baz")), ParserTestBase.EvaluateResults(parser.Parse(new StringReader("foo @bar baz")),
@ -40,12 +45,12 @@ namespace Microsoft.AspNet.Razor.Test.Parser
var factory = SpanFactory.CreateCsHtml(); var factory = SpanFactory.CreateCsHtml();
// Arrange // Arrange
RazorParser parser = new RazorParser(new CSharpCodeParser(), var parser = new RazorParser(new CSharpCodeParser(),
new HtmlMarkupParser(), new HtmlMarkupParser(),
tagHelperDescriptorResolver: null); tagHelperDescriptorResolver: null);
// Act // Act
ParserResults results = parser.Parse(new StringReader("foo @bar baz")); var results = parser.Parse(new StringReader("foo @bar baz"));
// Assert // Assert
ParserTestBase.EvaluateResults(results, ParserTestBase.EvaluateResults(results,
@ -59,6 +64,27 @@ namespace Microsoft.AspNet.Razor.Test.Parser
factory.Markup(" baz"))); factory.Markup(" baz")));
} }
[Fact]
public void GetTagHelperDescriptors_IsInvokedToLocateTagHelperDescriptors()
{
// Arrange
var factory = SpanFactory.CreateCsHtml();
var parser = new Mock<RazorParser>(new CSharpCodeParser(),
new HtmlMarkupParser(),
Mock.Of<ITagHelperDescriptorResolver>());
parser.CallBase = true;
parser.Protected()
.Setup<IEnumerable<TagHelperDescriptor>>("GetTagHelperDescriptors", ItExpr.IsAny<Block>())
.Returns(Enumerable.Empty<TagHelperDescriptor>())
.Verifiable();
// Act
parser.Object.Parse(new StringReader("<p>Hello world. The time is @DateTime.UtcNow</p>"));
// Assert
parser.Verify();
}
[Fact] [Fact]
public void ParseMethodSetsUpRunWithSpecifiedCodeParserMarkupParserAndListenerAndPassesToMarkupParser() public void ParseMethodSetsUpRunWithSpecifiedCodeParserMarkupParserAndListenerAndPassesToMarkupParser()
{ {
@ -68,16 +94,16 @@ namespace Microsoft.AspNet.Razor.Test.Parser
private static void RunParseWithListenerTest(Action<RazorParser, TextReader> parserAction) private static void RunParseWithListenerTest(Action<RazorParser, TextReader> parserAction)
{ {
// Arrange // Arrange
ParserBase markupParser = new MockMarkupParser(); var markupParser = new MockMarkupParser();
ParserBase codeParser = new CSharpCodeParser(); var codeParser = new CSharpCodeParser();
RazorParser parser = new RazorParser(codeParser, markupParser, tagHelperDescriptorResolver: null); var parser = new RazorParser(codeParser, markupParser, tagHelperDescriptorResolver: null);
TextReader expectedReader = new StringReader("foo"); var expectedReader = new StringReader("foo");
// Act // Act
parserAction(parser, expectedReader); parserAction(parser, expectedReader);
// Assert // Assert
ParserContext actualContext = markupParser.Context; var actualContext = markupParser.Context;
Assert.NotNull(actualContext); Assert.NotNull(actualContext);
Assert.Same(markupParser, actualContext.MarkupParser); Assert.Same(markupParser, actualContext.MarkupParser);
Assert.Same(markupParser, actualContext.ActiveParser); Assert.Same(markupParser, actualContext.ActiveParser);

View File

@ -29,10 +29,10 @@ namespace Microsoft.AspNet.Razor.Test
public void ConstructorInitializesHost() public void ConstructorInitializesHost()
{ {
// Arrange // Arrange
RazorEngineHost host = new RazorEngineHost(new CSharpRazorCodeLanguage()); var host = new RazorEngineHost(new CSharpRazorCodeLanguage());
// Act // Act
RazorTemplateEngine engine = new RazorTemplateEngine(host); var engine = new RazorTemplateEngine(host);
// Assert // Assert
Assert.Same(host, engine.Host); Assert.Same(host, engine.Host);
@ -42,11 +42,11 @@ namespace Microsoft.AspNet.Razor.Test
public void CreateParserMethodIsConstructedFromHost() public void CreateParserMethodIsConstructedFromHost()
{ {
// Arrange // Arrange
RazorEngineHost host = CreateHost(); var host = CreateHost();
RazorTemplateEngine engine = new RazorTemplateEngine(host); var engine = new RazorTemplateEngine(host);
// Act // Act
RazorParser parser = engine.CreateParser(); var parser = engine.CreateParser("some-file");
// Assert // Assert
Assert.IsType<CSharpCodeParser>(parser.CodeParser); Assert.IsType<CSharpCodeParser>(parser.CodeParser);
@ -57,12 +57,12 @@ namespace Microsoft.AspNet.Razor.Test
public void CreateParserMethodSetsParserContextToDesignTimeModeIfHostSetToDesignTimeMode() public void CreateParserMethodSetsParserContextToDesignTimeModeIfHostSetToDesignTimeMode()
{ {
// Arrange // Arrange
RazorEngineHost host = CreateHost(); var host = CreateHost();
RazorTemplateEngine engine = new RazorTemplateEngine(host); var engine = new RazorTemplateEngine(host);
host.DesignTimeMode = true; host.DesignTimeMode = true;
// Act // Act
RazorParser parser = engine.CreateParser(); var parser = engine.CreateParser("some-file");
// Assert // Assert
Assert.True(parser.DesignTimeMode); Assert.True(parser.DesignTimeMode);
@ -72,18 +72,18 @@ namespace Microsoft.AspNet.Razor.Test
public void CreateParserMethodPassesParsersThroughDecoratorMethodsOnHost() public void CreateParserMethodPassesParsersThroughDecoratorMethodsOnHost()
{ {
// Arrange // Arrange
ParserBase expectedCode = new Mock<ParserBase>().Object; var expectedCode = new Mock<ParserBase>().Object;
ParserBase expectedMarkup = new Mock<ParserBase>().Object; var expectedMarkup = new Mock<ParserBase>().Object;
var mockHost = new Mock<RazorEngineHost>(new CSharpRazorCodeLanguage()) { CallBase = true }; var mockHost = new Mock<RazorEngineHost>(new CSharpRazorCodeLanguage()) { CallBase = true };
mockHost.Setup(h => h.DecorateCodeParser(It.IsAny<CSharpCodeParser>())) mockHost.Setup(h => h.DecorateCodeParser(It.IsAny<CSharpCodeParser>()))
.Returns(expectedCode); .Returns(expectedCode);
mockHost.Setup(h => h.DecorateMarkupParser(It.IsAny<HtmlMarkupParser>())) mockHost.Setup(h => h.DecorateMarkupParser(It.IsAny<HtmlMarkupParser>()))
.Returns(expectedMarkup); .Returns(expectedMarkup);
RazorTemplateEngine engine = new RazorTemplateEngine(mockHost.Object); var engine = new RazorTemplateEngine(mockHost.Object);
// Act // Act
RazorParser actual = engine.CreateParser(); var actual = engine.CreateParser("some-file");
// Assert // Assert
Assert.Equal(expectedCode, actual.CodeParser); Assert.Equal(expectedCode, actual.CodeParser);
@ -96,14 +96,14 @@ namespace Microsoft.AspNet.Razor.Test
// Arrange // Arrange
var mockHost = new Mock<RazorEngineHost>(new CSharpRazorCodeLanguage()) { CallBase = true }; var mockHost = new Mock<RazorEngineHost>(new CSharpRazorCodeLanguage()) { CallBase = true };
RazorCodeGenerator expected = new Mock<RazorCodeGenerator>("Foo", "Bar", "Baz", mockHost.Object).Object; var expected = new Mock<RazorCodeGenerator>("Foo", "Bar", "Baz", mockHost.Object).Object;
mockHost.Setup(h => h.DecorateCodeGenerator(It.IsAny<CSharpRazorCodeGenerator>())) mockHost.Setup(h => h.DecorateCodeGenerator(It.IsAny<CSharpRazorCodeGenerator>()))
.Returns(expected); .Returns(expected);
RazorTemplateEngine engine = new RazorTemplateEngine(mockHost.Object); var engine = new RazorTemplateEngine(mockHost.Object);
// Act // Act
RazorCodeGenerator actual = engine.CreateCodeGenerator("Foo", "Bar", "Baz"); var actual = engine.CreateCodeGenerator("Foo", "Bar", "Baz");
// Assert // Assert
Assert.Equal(expected, actual); Assert.Equal(expected, actual);
@ -138,15 +138,16 @@ namespace Microsoft.AspNet.Razor.Test
public void ParseTemplateCopiesTextReaderContentToSeekableTextReaderAndPassesToParseTemplateCore() public void ParseTemplateCopiesTextReaderContentToSeekableTextReaderAndPassesToParseTemplateCore()
{ {
// Arrange // Arrange
Mock<RazorTemplateEngine> mockEngine = new Mock<RazorTemplateEngine>(CreateHost()); var mockEngine = new Mock<RazorTemplateEngine>(CreateHost());
TextReader reader = new StringReader("foo"); var reader = new StringReader("foo");
CancellationTokenSource source = new CancellationTokenSource(); var source = new CancellationTokenSource();
// Act // Act
mockEngine.Object.ParseTemplate(reader, cancelToken: source.Token); mockEngine.Object.ParseTemplate(reader, cancelToken: source.Token);
// Assert // Assert
mockEngine.Verify(e => e.ParseTemplateCore(It.Is<SeekableTextReader>(l => l.ReadToEnd() == "foo"), mockEngine.Verify(e => e.ParseTemplateCore(It.Is<SeekableTextReader>(l => l.ReadToEnd() == "foo"),
null,
source.Token)); source.Token));
} }
@ -154,12 +155,12 @@ namespace Microsoft.AspNet.Razor.Test
public void GenerateCodeCopiesTextReaderContentToSeekableTextReaderAndPassesToGenerateCodeCore() public void GenerateCodeCopiesTextReaderContentToSeekableTextReaderAndPassesToGenerateCodeCore()
{ {
// Arrange // Arrange
Mock<RazorTemplateEngine> mockEngine = new Mock<RazorTemplateEngine>(CreateHost()); var mockEngine = new Mock<RazorTemplateEngine>(CreateHost());
TextReader reader = new StringReader("foo"); var reader = new StringReader("foo");
CancellationTokenSource source = new CancellationTokenSource(); var source = new CancellationTokenSource();
string className = "Foo"; var className = "Foo";
string ns = "Bar"; var ns = "Bar";
string src = "Baz"; var src = "Baz";
// Act // Act
mockEngine.Object.GenerateCode(reader, className: className, rootNamespace: ns, sourceFileName: src, cancelToken: source.Token); mockEngine.Object.GenerateCode(reader, className: className, rootNamespace: ns, sourceFileName: src, cancelToken: source.Token);
@ -173,10 +174,10 @@ namespace Microsoft.AspNet.Razor.Test
public void ParseTemplateOutputsResultsOfParsingProvidedTemplateSource() public void ParseTemplateOutputsResultsOfParsingProvidedTemplateSource()
{ {
// Arrange // Arrange
RazorTemplateEngine engine = new RazorTemplateEngine(CreateHost()); var engine = new RazorTemplateEngine(CreateHost());
// Act // Act
ParserResults results = engine.ParseTemplate(new StringTextBuffer("foo @bar(")); var results = engine.ParseTemplate(new StringTextBuffer("foo @bar("));
// Assert // Assert
Assert.False(results.Success); Assert.False(results.Success);
@ -188,10 +189,10 @@ namespace Microsoft.AspNet.Razor.Test
public void GenerateOutputsResultsOfParsingAndGeneration() public void GenerateOutputsResultsOfParsingAndGeneration()
{ {
// Arrange // Arrange
RazorTemplateEngine engine = new RazorTemplateEngine(CreateHost()); var engine = new RazorTemplateEngine(CreateHost());
// Act // Act
GeneratorResults results = engine.GenerateCode(new StringTextBuffer("foo @bar(")); var results = engine.GenerateCode(new StringTextBuffer("foo @bar("));
// Assert // Assert
Assert.False(results.Success); Assert.False(results.Success);
@ -204,10 +205,10 @@ namespace Microsoft.AspNet.Razor.Test
public void GenerateOutputsDesignTimeMappingsIfDesignTimeSetOnHost() public void GenerateOutputsDesignTimeMappingsIfDesignTimeSetOnHost()
{ {
// Arrange // Arrange
RazorTemplateEngine engine = new RazorTemplateEngine(CreateHost(designTime: true)); var engine = new RazorTemplateEngine(CreateHost(designTime: true));
// Act // Act
GeneratorResults results = engine.GenerateCode(new StringTextBuffer("foo @bar()"), className: null, rootNamespace: null, sourceFileName: "foo.cshtml"); var results = engine.GenerateCode(new StringTextBuffer("foo @bar()"), className: null, rootNamespace: null, sourceFileName: "foo.cshtml");
// Assert // Assert
Assert.True(results.Success); Assert.True(results.Success);
@ -267,6 +268,38 @@ namespace Microsoft.AspNet.Razor.Test
Assert.Null(engine.Checksum); Assert.Null(engine.Checksum);
} }
[Fact]
public void GenerateCode_UsesDecoratedRazorParser()
{
// Arrange
Mock<RazorParser> parser = null;
var host = new Mock<RazorEngineHost>(new CSharpRazorCodeLanguage())
{
CallBase = true
};
host.Setup(p => p.DecorateRazorParser(It.IsAny<RazorParser>(), "foo.cshtml"))
.Returns((RazorParser p, string file) =>
{
parser = new Mock<RazorParser>(p)
{
CallBase = true
};
return parser.Object;
})
.Verifiable();
var engine = new RazorTemplateEngine(host.Object);
// Act
var results = engine.GenerateCode(Stream.Null, "some-class", "some-ns", "foo.cshtml");
// Assert
Assert.NotNull(parser);
parser.Verify(v => v.Parse(It.IsAny<ITextDocument>()), Times.Once());
host.Verify();
}
private static RazorEngineHost CreateHost(bool designTime = false) private static RazorEngineHost CreateHost(bool designTime = false)
{ {
return new RazorEngineHost(new CSharpRazorCodeLanguage()) return new RazorEngineHost(new CSharpRazorCodeLanguage())