From 99fe8294e90f7f887eac5b10357571fac674e404 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 13 Oct 2014 05:44:39 -0700 Subject: [PATCH] Modifications to support providing tag descriptors from derived hosts --- .../Parser/RazorParser.cs | 98 +++++++++++++---- .../TagHelperRegistrationVisitor.cs | 19 ++-- src/Microsoft.AspNet.Razor/RazorEngineHost.cs | 13 +++ .../RazorTemplateEngine.cs | 26 +++-- .../TagHelpers/TagHelperDescriptorComparer.cs | 5 - .../Generator/TagHelperTestBase.cs | 4 +- .../Parser/RazorParserTest.cs | 50 +++++++-- .../RazorTemplateEngineTest.cs | 103 ++++++++++++------ 8 files changed, 222 insertions(+), 96 deletions(-) diff --git a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs index da5d8692a1..8655b17329 100644 --- a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs +++ b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Parser.TagHelpers; using Microsoft.AspNet.Razor.Parser.TagHelpers.Internal; using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.Text; @@ -17,34 +18,61 @@ namespace Microsoft.AspNet.Razor.Parser { public class RazorParser { - private ITagHelperDescriptorResolver _tagHelperDescriptorResolver; - + /// + /// Initializes a new instance of . + /// + /// The used for parsing code content. + /// The used for parsing markpup content. + /// The used to resolve + /// s. public RazorParser([NotNull] ParserBase codeParser, [NotNull] ParserBase markupParser, ITagHelperDescriptorResolver tagHelperDescriptorResolver) + : this(codeParser, + markupParser, + tagHelperDescriptorResolver, + GetDefaultRewriters(markupParser)) { - _tagHelperDescriptorResolver = tagHelperDescriptorResolver; - MarkupParser = markupParser; - CodeParser = codeParser; - - Optimizers = new List() - { - // 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; } - internal ParserBase MarkupParser { get; private set; } - internal IList Optimizers { get; private set; } + /// + /// Initializes a new instance of from the specified . + /// + /// The to copy values from. + 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 optimizers) + { + CodeParser = codeParser; + MarkupParser = markupParser; + TagHelperDescriptorResolver = tagHelperDescriptorResolver; + Optimizers = optimizers.ToList(); + } public bool DesignTimeMode { get; set; } + /// + /// Gets the used to resolve + /// s. + /// + 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 Optimizers { get; private set; } + public virtual void Parse(TextReader input, ParserVisitor visitor) { Parse(new SeekableTextReader(input), visitor); @@ -142,10 +170,10 @@ namespace Microsoft.AspNet.Razor.Parser rewriter.Rewrite(rewritingContext); } - if (_tagHelperDescriptorResolver != null) + if (TagHelperDescriptorResolver != null) { - var tagHelperRegistrationVisitor = new TagHelperRegistrationVisitor(_tagHelperDescriptorResolver); - var tagHelperProvider = tagHelperRegistrationVisitor.CreateProvider(rewritingContext.SyntaxTree); + var descriptors = GetTagHelperDescriptors(rewritingContext.SyntaxTree); + var tagHelperProvider = new TagHelperDescriptorProvider(descriptors); var tagHelperParseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperProvider); // Rewrite the document to utilize tag helpers @@ -173,5 +201,31 @@ namespace Microsoft.AspNet.Razor.Parser // Return the new result return new ParserResults(syntaxTree, errors); } + + /// + /// Returns a sequence of s for tag helpers that are registered in the + /// specified . + /// + /// The to scan for tag helper registrations in. + /// + protected virtual IEnumerable GetTagHelperDescriptors([NotNull] Block documentRoot) + { + var tagHelperRegistrationVisitor = new TagHelperRegistrationVisitor(TagHelperDescriptorResolver); + return tagHelperRegistrationVisitor.GetDescriptors(documentRoot); + } + + private static IEnumerable 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), + }; + } } } diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs index 3681f85f01..818852f666 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs @@ -7,27 +7,31 @@ using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.TagHelpers; -namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal +namespace Microsoft.AspNet.Razor.Parser.TagHelpers { + /// + /// A that generates s from + /// tag helper code generators in a Razor document. + /// public class TagHelperRegistrationVisitor : ParserVisitor { private readonly ITagHelperDescriptorResolver _descriptorResolver; - private HashSet _descriptors; + private List _descriptors; public TagHelperRegistrationVisitor(ITagHelperDescriptorResolver descriptorResolver) { _descriptorResolver = descriptorResolver; } - public TagHelperDescriptorProvider CreateProvider(Block root) + public IEnumerable GetDescriptors([NotNull] Block root) { - _descriptors = new HashSet(TagHelperDescriptorComparer.Default); + _descriptors = new List(); // This will recurse through the syntax tree. VisitBlock(root); - return new TagHelperDescriptorProvider(_descriptors); + return _descriptors; } public override void VisitSpan(Span span) @@ -50,10 +54,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal var descriptors = _descriptorResolver.Resolve(addGenerator.LookupText); // Add all the found descriptors to our HashSet. - foreach (var descriptor in descriptors) - { - _descriptors.Add(descriptor); - } + _descriptors.AddRange(descriptors); } } } diff --git a/src/Microsoft.AspNet.Razor/RazorEngineHost.cs b/src/Microsoft.AspNet.Razor/RazorEngineHost.cs index e941932d4e..ba5b501398 100644 --- a/src/Microsoft.AspNet.Razor/RazorEngineHost.cs +++ b/src/Microsoft.AspNet.Razor/RazorEngineHost.cs @@ -165,6 +165,19 @@ namespace Microsoft.AspNet.Razor return null; } + /// + /// Provides an opportunity for derived types to modify the instance of + /// used by the to parse the Razor tree. + /// + /// The + /// The file name of the Razor file being parsed. + /// Either the same code parser, after modifications, or a different code parser. + public virtual RazorParser DecorateRazorParser([NotNull] RazorParser incomingRazorParser, + string sourceFileName) + { + return incomingRazorParser; + } + /// /// Gets an instance of the code parser and is provided an opportunity to decorate or replace it /// diff --git a/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs b/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs index 72f06192e5..949b698b91 100644 --- a/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs +++ b/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs @@ -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")] public ParserResults ParseTemplate(ITextBuffer input, CancellationToken? cancelToken) { - return ParseTemplateCore(input.ToDocument(), cancelToken); + return ParseTemplateCore(input.ToDocument(), sourceFileName: null, cancelToken: cancelToken); } // See ParseTemplate(ITextBuffer, CancellationToken?), // 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")] 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 - RazorParser parser = CreateParser(); + var parser = CreateParser(sourceFileName); Debug.Assert(parser != null); return parser.Parse(input); } @@ -243,7 +245,7 @@ namespace Microsoft.AspNet.Razor rootNamespace = (rootNamespace ?? Host.DefaultNamespace) ?? DefaultNamespace; // Run the parser - var parser = CreateParser(); + var parser = CreateParser(sourceFileName); Debug.Assert(parser != null); var results = parser.Parse(input); @@ -267,17 +269,19 @@ namespace Microsoft.AspNet.Razor 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 markupParser = Host.CreateMarkupParser(); - return new RazorParser(Host.DecorateCodeParser(codeParser), - Host.DecorateMarkupParser(markupParser), - Host.TagHelperDescriptorResolver) + var parser = new RazorParser(Host.DecorateCodeParser(codeParser), + Host.DecorateMarkupParser(markupParser), + Host.TagHelperDescriptorResolver) { DesignTimeMode = Host.DesignTimeMode }; + + return Host.DecorateRazorParser(parser, sourceFileName); } protected internal virtual CodeBuilder CreateCodeBuilder(CodeBuilderContext context) diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs index bca5f415ce..f49f41eb10 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs @@ -18,11 +18,6 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// public static readonly TagHelperDescriptorComparer Default = new TagHelperDescriptorComparer(); - // Internal for testing - internal TagHelperDescriptorComparer() - { - } - /// /// Determines if the two given tag helpers are equal. /// diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs index b88c31f367..11113b2231 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs @@ -57,9 +57,9 @@ namespace Microsoft.AspNet.Razor.Test.Generator _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, parser.MarkupParser, diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/RazorParserTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/RazorParserTest.cs index 2c6df35c33..196b9774ef 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/RazorParserTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/RazorParserTest.cs @@ -2,10 +2,15 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.Test.Framework; +using Moq; +using Moq.Protected; using Xunit; namespace Microsoft.AspNet.Razor.Test.Parser @@ -18,9 +23,9 @@ namespace Microsoft.AspNet.Razor.Test.Parser var factory = SpanFactory.CreateCsHtml(); // Arrange - RazorParser parser = new RazorParser(new CSharpCodeParser(), - new HtmlMarkupParser(), - tagHelperDescriptorResolver: null); + var parser = new RazorParser(new CSharpCodeParser(), + new HtmlMarkupParser(), + tagHelperDescriptorResolver: null); // Act/Assert ParserTestBase.EvaluateResults(parser.Parse(new StringReader("foo @bar baz")), @@ -40,12 +45,12 @@ namespace Microsoft.AspNet.Razor.Test.Parser var factory = SpanFactory.CreateCsHtml(); // Arrange - RazorParser parser = new RazorParser(new CSharpCodeParser(), - new HtmlMarkupParser(), - tagHelperDescriptorResolver: null); + var parser = new RazorParser(new CSharpCodeParser(), + new HtmlMarkupParser(), + tagHelperDescriptorResolver: null); // Act - ParserResults results = parser.Parse(new StringReader("foo @bar baz")); + var results = parser.Parse(new StringReader("foo @bar baz")); // Assert ParserTestBase.EvaluateResults(results, @@ -59,6 +64,27 @@ namespace Microsoft.AspNet.Razor.Test.Parser factory.Markup(" baz"))); } + [Fact] + public void GetTagHelperDescriptors_IsInvokedToLocateTagHelperDescriptors() + { + // Arrange + var factory = SpanFactory.CreateCsHtml(); + var parser = new Mock(new CSharpCodeParser(), + new HtmlMarkupParser(), + Mock.Of()); + parser.CallBase = true; + parser.Protected() + .Setup>("GetTagHelperDescriptors", ItExpr.IsAny()) + .Returns(Enumerable.Empty()) + .Verifiable(); + + // Act + parser.Object.Parse(new StringReader("

Hello world. The time is @DateTime.UtcNow

")); + + // Assert + parser.Verify(); + } + [Fact] public void ParseMethodSetsUpRunWithSpecifiedCodeParserMarkupParserAndListenerAndPassesToMarkupParser() { @@ -68,16 +94,16 @@ namespace Microsoft.AspNet.Razor.Test.Parser private static void RunParseWithListenerTest(Action parserAction) { // Arrange - ParserBase markupParser = new MockMarkupParser(); - ParserBase codeParser = new CSharpCodeParser(); - RazorParser parser = new RazorParser(codeParser, markupParser, tagHelperDescriptorResolver: null); - TextReader expectedReader = new StringReader("foo"); + var markupParser = new MockMarkupParser(); + var codeParser = new CSharpCodeParser(); + var parser = new RazorParser(codeParser, markupParser, tagHelperDescriptorResolver: null); + var expectedReader = new StringReader("foo"); // Act parserAction(parser, expectedReader); // Assert - ParserContext actualContext = markupParser.Context; + var actualContext = markupParser.Context; Assert.NotNull(actualContext); Assert.Same(markupParser, actualContext.MarkupParser); Assert.Same(markupParser, actualContext.ActiveParser); diff --git a/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs b/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs index de5b09d984..8cc7fda407 100644 --- a/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs @@ -29,10 +29,10 @@ namespace Microsoft.AspNet.Razor.Test public void ConstructorInitializesHost() { // Arrange - RazorEngineHost host = new RazorEngineHost(new CSharpRazorCodeLanguage()); + var host = new RazorEngineHost(new CSharpRazorCodeLanguage()); // Act - RazorTemplateEngine engine = new RazorTemplateEngine(host); + var engine = new RazorTemplateEngine(host); // Assert Assert.Same(host, engine.Host); @@ -42,11 +42,11 @@ namespace Microsoft.AspNet.Razor.Test public void CreateParserMethodIsConstructedFromHost() { // Arrange - RazorEngineHost host = CreateHost(); - RazorTemplateEngine engine = new RazorTemplateEngine(host); + var host = CreateHost(); + var engine = new RazorTemplateEngine(host); // Act - RazorParser parser = engine.CreateParser(); + var parser = engine.CreateParser("some-file"); // Assert Assert.IsType(parser.CodeParser); @@ -57,12 +57,12 @@ namespace Microsoft.AspNet.Razor.Test public void CreateParserMethodSetsParserContextToDesignTimeModeIfHostSetToDesignTimeMode() { // Arrange - RazorEngineHost host = CreateHost(); - RazorTemplateEngine engine = new RazorTemplateEngine(host); + var host = CreateHost(); + var engine = new RazorTemplateEngine(host); host.DesignTimeMode = true; // Act - RazorParser parser = engine.CreateParser(); + var parser = engine.CreateParser("some-file"); // Assert Assert.True(parser.DesignTimeMode); @@ -72,18 +72,18 @@ namespace Microsoft.AspNet.Razor.Test public void CreateParserMethodPassesParsersThroughDecoratorMethodsOnHost() { // Arrange - ParserBase expectedCode = new Mock().Object; - ParserBase expectedMarkup = new Mock().Object; + var expectedCode = new Mock().Object; + var expectedMarkup = new Mock().Object; var mockHost = new Mock(new CSharpRazorCodeLanguage()) { CallBase = true }; mockHost.Setup(h => h.DecorateCodeParser(It.IsAny())) .Returns(expectedCode); mockHost.Setup(h => h.DecorateMarkupParser(It.IsAny())) .Returns(expectedMarkup); - RazorTemplateEngine engine = new RazorTemplateEngine(mockHost.Object); + var engine = new RazorTemplateEngine(mockHost.Object); // Act - RazorParser actual = engine.CreateParser(); + var actual = engine.CreateParser("some-file"); // Assert Assert.Equal(expectedCode, actual.CodeParser); @@ -96,14 +96,14 @@ namespace Microsoft.AspNet.Razor.Test // Arrange var mockHost = new Mock(new CSharpRazorCodeLanguage()) { CallBase = true }; - RazorCodeGenerator expected = new Mock("Foo", "Bar", "Baz", mockHost.Object).Object; + var expected = new Mock("Foo", "Bar", "Baz", mockHost.Object).Object; mockHost.Setup(h => h.DecorateCodeGenerator(It.IsAny())) .Returns(expected); - RazorTemplateEngine engine = new RazorTemplateEngine(mockHost.Object); + var engine = new RazorTemplateEngine(mockHost.Object); // Act - RazorCodeGenerator actual = engine.CreateCodeGenerator("Foo", "Bar", "Baz"); + var actual = engine.CreateCodeGenerator("Foo", "Bar", "Baz"); // Assert Assert.Equal(expected, actual); @@ -138,15 +138,16 @@ namespace Microsoft.AspNet.Razor.Test public void ParseTemplateCopiesTextReaderContentToSeekableTextReaderAndPassesToParseTemplateCore() { // Arrange - Mock mockEngine = new Mock(CreateHost()); - TextReader reader = new StringReader("foo"); - CancellationTokenSource source = new CancellationTokenSource(); + var mockEngine = new Mock(CreateHost()); + var reader = new StringReader("foo"); + var source = new CancellationTokenSource(); // Act mockEngine.Object.ParseTemplate(reader, cancelToken: source.Token); // Assert mockEngine.Verify(e => e.ParseTemplateCore(It.Is(l => l.ReadToEnd() == "foo"), + null, source.Token)); } @@ -154,12 +155,12 @@ namespace Microsoft.AspNet.Razor.Test public void GenerateCodeCopiesTextReaderContentToSeekableTextReaderAndPassesToGenerateCodeCore() { // Arrange - Mock mockEngine = new Mock(CreateHost()); - TextReader reader = new StringReader("foo"); - CancellationTokenSource source = new CancellationTokenSource(); - string className = "Foo"; - string ns = "Bar"; - string src = "Baz"; + var mockEngine = new Mock(CreateHost()); + var reader = new StringReader("foo"); + var source = new CancellationTokenSource(); + var className = "Foo"; + var ns = "Bar"; + var src = "Baz"; // Act 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() { // Arrange - RazorTemplateEngine engine = new RazorTemplateEngine(CreateHost()); + var engine = new RazorTemplateEngine(CreateHost()); // Act - ParserResults results = engine.ParseTemplate(new StringTextBuffer("foo @bar(")); + var results = engine.ParseTemplate(new StringTextBuffer("foo @bar(")); // Assert Assert.False(results.Success); @@ -188,10 +189,10 @@ namespace Microsoft.AspNet.Razor.Test public void GenerateOutputsResultsOfParsingAndGeneration() { // Arrange - RazorTemplateEngine engine = new RazorTemplateEngine(CreateHost()); + var engine = new RazorTemplateEngine(CreateHost()); // Act - GeneratorResults results = engine.GenerateCode(new StringTextBuffer("foo @bar(")); + var results = engine.GenerateCode(new StringTextBuffer("foo @bar(")); // Assert Assert.False(results.Success); @@ -204,10 +205,10 @@ namespace Microsoft.AspNet.Razor.Test public void GenerateOutputsDesignTimeMappingsIfDesignTimeSetOnHost() { // Arrange - RazorTemplateEngine engine = new RazorTemplateEngine(CreateHost(designTime: true)); + var engine = new RazorTemplateEngine(CreateHost(designTime: true)); // 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.True(results.Success); @@ -267,6 +268,38 @@ namespace Microsoft.AspNet.Razor.Test Assert.Null(engine.Checksum); } + [Fact] + public void GenerateCode_UsesDecoratedRazorParser() + { + // Arrange + Mock parser = null; + var host = new Mock(new CSharpRazorCodeLanguage()) + { + CallBase = true + }; + host.Setup(p => p.DecorateRazorParser(It.IsAny(), "foo.cshtml")) + .Returns((RazorParser p, string file) => + { + parser = new Mock(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()), Times.Once()); + host.Verify(); + } + private static RazorEngineHost CreateHost(bool designTime = false) { return new RazorEngineHost(new CSharpRazorCodeLanguage()) @@ -284,11 +317,11 @@ namespace Microsoft.AspNet.Razor.Test public string Checksum { get; set; } - protected internal override GeneratorResults GenerateCodeCore(ITextDocument input, - string className, - string rootNamespace, - string sourceFileName, - string checksum, + protected internal override GeneratorResults GenerateCodeCore(ITextDocument input, + string className, + string rootNamespace, + string sourceFileName, + string checksum, CancellationToken? cancelToken) { Checksum = checksum;