aspnetcore/test/Microsoft.AspNetCore.Razor..../RazorTemplateEngineTest.cs

344 lines
11 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Web.WebPages.TestUtils;
using Microsoft.AspNetCore.Razor.Chunks.Generators;
using Microsoft.AspNetCore.Razor.CodeGenerators;
using Microsoft.AspNetCore.Razor.Parser;
using Microsoft.AspNetCore.Razor.Parser.Internal;
using Microsoft.AspNetCore.Razor.Text;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Test
{
public class RazorTemplateEngineTest
{
[Fact]
public void ConstructorInitializesHost()
{
// Arrange
var host = new RazorEngineHost(new CSharpRazorCodeLanguage());
// Act
var engine = new RazorTemplateEngine(host);
// Assert
Assert.Same(host, engine.Host);
}
[Fact]
public void CreateParserMethodIsConstructedFromHost()
{
// Arrange
var host = CreateHost();
var engine = new RazorTemplateEngine(host);
// Act
var parser = engine.CreateParser("some-file");
// Assert
Assert.IsType<CSharpCodeParser>(parser.CodeParser);
Assert.IsType<HtmlMarkupParser>(parser.MarkupParser);
}
[Fact]
public void CreateParserMethodSetsParserContextToDesignTimeModeIfHostSetToDesignTimeMode()
{
// Arrange
var host = CreateHost();
var engine = new RazorTemplateEngine(host);
host.DesignTimeMode = true;
// Act
var parser = engine.CreateParser("some-file");
// Assert
Assert.True(parser.DesignTimeMode);
}
[Fact]
public void CreateParserMethodPassesParsersThroughDecoratorMethodsOnHost()
{
// Arrange
var expectedCode = new Mock<ParserBase>().Object;
var expectedMarkup = new Mock<ParserBase>().Object;
var mockHost = new Mock<RazorEngineHost>(new CSharpRazorCodeLanguage()) { CallBase = true };
mockHost.Setup(h => h.DecorateCodeParser(It.IsAny<CSharpCodeParser>()))
.Returns(expectedCode);
mockHost.Setup(h => h.DecorateMarkupParser(It.IsAny<HtmlMarkupParser>()))
.Returns(expectedMarkup);
var engine = new RazorTemplateEngine(mockHost.Object);
// Act
var actual = engine.CreateParser("some-file");
// Assert
Assert.Equal(expectedCode, actual.CodeParser);
Assert.Equal(expectedMarkup, actual.MarkupParser);
}
[Fact]
public void CreateChunkGeneratorMethodPassesChunkGeneratorThroughDecorateMethodOnHost()
{
// Arrange
var mockHost = new Mock<RazorEngineHost>(new CSharpRazorCodeLanguage()) { CallBase = true };
var expected = new Mock<RazorChunkGenerator>("Foo", "Bar", "Baz", mockHost.Object).Object;
mockHost.Setup(h => h.DecorateChunkGenerator(It.IsAny<RazorChunkGenerator>()))
.Returns(expected);
var engine = new RazorTemplateEngine(mockHost.Object);
// Act
var actual = engine.CreateChunkGenerator("Foo", "Bar", "Baz");
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void CreateCodeGenerator_PassesChunkGeneratorThroughDecorateMethodOnHost()
{
// Arrange
var mockHost = new Mock<RazorEngineHost>(new CSharpRazorCodeLanguage()) { CallBase = true };
var codeGeneratorContext = new CodeGeneratorContext(
mockHost.Object,
"different-class",
"different-ns",
string.Empty,
shouldGenerateLinePragmas: true,
errorSink: new ErrorSink());
var expected = new CSharpCodeGenerator(codeGeneratorContext);
mockHost
.Setup(h => h.DecorateCodeGenerator(It.IsAny<CSharpCodeGenerator>(), codeGeneratorContext))
.Returns(expected);
var engine = new RazorTemplateEngine(mockHost.Object);
// Act
var actual = engine.CreateCodeGenerator(codeGeneratorContext);
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void ParseTemplateCopiesTextReaderContentToSeekableTextReaderAndPassesToParseTemplateCore()
{
// Arrange
var mockEngine = new Mock<RazorTemplateEngine>(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<SeekableTextReader>(l => l.ReadToEnd() == "foo"),
null,
source.Token));
}
[Fact]
public void GenerateCodeCopiesTextReaderContentToSeekableTextReaderAndPassesToGenerateCodeCore()
{
// Arrange
var mockEngine = new Mock<RazorTemplateEngine>(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);
// Assert
mockEngine.Verify(e => e.GenerateCodeCore(
It.Is<SeekableTextReader>(l => l.ReadToEnd() == "foo"),
className,
ns,
src,
null,
source.Token));
}
[Fact]
public void ParseTemplateOutputsResultsOfParsingProvidedTemplateSource()
{
// Arrange
var engine = new RazorTemplateEngine(CreateHost());
// Act
var results = engine.ParseTemplate(new StringTextBuffer("foo @bar("));
// Assert
Assert.False(results.Success);
Assert.Single(results.ParserErrors);
Assert.NotNull(results.Document);
}
[Fact]
public void GenerateOutputsResultsOfParsingAndGeneration()
{
// Arrange
var engine = new RazorTemplateEngine(CreateHost());
// Act
var results = engine.GenerateCode(new StringTextBuffer("foo @bar("));
// Assert
Assert.False(results.Success);
Assert.Single(results.ParserErrors);
Assert.NotNull(results.Document);
Assert.NotNull(results.GeneratedCode);
}
[Fact]
public void GenerateOutputsDesignTimeMappingsIfDesignTimeSetOnHost()
{
// Arrange
var engine = new RazorTemplateEngine(CreateHost(designTime: true));
// Act
var results = engine.GenerateCode(
new StringTextBuffer("foo @bar()"),
className: null,
rootNamespace: null,
sourceFileName: "foo.cshtml");
// Assert
Assert.True(results.Success);
Assert.Empty(results.ParserErrors);
Assert.NotNull(results.Document);
Assert.NotNull(results.GeneratedCode);
Assert.NotNull(results.DesignTimeLineMappings);
}
public static IEnumerable<object[]> GenerateCodeCalculatesLinePragma_IfStreamInputIsUsedData
{
get
{
// Seekable stream
var content = Encoding.UTF8.GetBytes("Hello world");
var stream = new MemoryStream(content);
yield return new[] { stream };
// Non seekable stream
var mockStream = new Mock<MemoryStream>(content)
{
CallBase = true
};
mockStream.Setup(m => m.CanSeek)
.Returns(false);
yield return new[] { mockStream.Object };
}
}
[Theory]
[MemberData(nameof(GenerateCodeCalculatesLinePragma_IfStreamInputIsUsedData))]
public void GenerateCodeCalculatesChecksum_IfStreamInputIsUsed(Stream stream)
{
// Arrange
var engine = new TestableRazorTemplateEngine();
// Act
var results = engine.GenerateCode(stream, "some-class", "some-ns", "foo.cshtml");
// Assert
Assert.Equal("7b502c3a1f48c8609ae212cdfb639dee39673f5e", engine.Checksum);
}
[Fact]
public void GenerateCode_DoesNotCalculateChecksum_InDesignTimeMode()
{
// Arrange
var engine = new TestableRazorTemplateEngine();
engine.Host.DesignTimeMode = true;
// Act
var results = engine.GenerateCode(Stream.Null, "some-class", "some-ns", "foo.cshtml");
// Assert
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)
{
return new RazorEngineHost(new CSharpRazorCodeLanguage())
{
DesignTimeMode = designTime
};
}
private class TestableRazorTemplateEngine : RazorTemplateEngine
{
public TestableRazorTemplateEngine()
: base(CreateHost())
{
}
public string Checksum { get; set; }
protected internal override GeneratorResults GenerateCodeCore(
ITextDocument input,
string className,
string rootNamespace,
string sourceFileName,
string checksum,
CancellationToken? cancelToken)
{
Checksum = checksum;
return null;
}
}
}
}