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())
@ -284,11 +317,11 @@ namespace Microsoft.AspNet.Razor.Test
public string Checksum { get; set; } public string Checksum { get; set; }
protected internal override GeneratorResults GenerateCodeCore(ITextDocument input, protected internal override GeneratorResults GenerateCodeCore(ITextDocument input,
string className, string className,
string rootNamespace, string rootNamespace,
string sourceFileName, string sourceFileName,
string checksum, string checksum,
CancellationToken? cancelToken) CancellationToken? cancelToken)
{ {
Checksum = checksum; Checksum = checksum;