* Redesign CompilationResult so that it does not throw when CompiledType is

accessed.
* Update to use ICompilationException interface from
  Microsoft.Framework.Runtime
* Update to use RoslynCompilationException

Fixes #955
This commit is contained in:
Pranav K 2015-02-19 17:16:53 -08:00 committed by Doug Bunting
parent 1d578ca2fd
commit 717c2bfd39
16 changed files with 359 additions and 279 deletions

View File

@ -3,13 +3,14 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Diagnostics;
using System.Linq;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// An exception thrown when accessing the result of a failed compilation.
/// An <see cref="Exception"/> thrown when accessing the result of a failed compilation.
/// </summary>
public class CompilationFailedException : Exception, ICompilationException
{
@ -20,12 +21,19 @@ namespace Microsoft.AspNet.Mvc.Razor
/// details of the compilation failure.</param>
public CompilationFailedException(
[NotNull] ICompilationFailure compilationFailure)
: base(Resources.FormatCompilationFailed(compilationFailure.SourceFilePath))
: base(FormatMessage(compilationFailure))
{
CompilationFailures = new[] { compilationFailure };
}
/// <inheritdoc />
public IEnumerable<ICompilationFailure> CompilationFailures { get; }
private static string FormatMessage(ICompilationFailure compilationFailure)
{
return Resources.CompilationFailed +
Environment.NewLine +
string.Join(Environment.NewLine, compilationFailure.Messages.Select(message => message.FormattedMessage));
}
}
}

View File

@ -1,53 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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 Microsoft.AspNet.Diagnostics;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// Default implementation of <see cref="ICompilationFailure"/>.
/// </summary>
public class CompilationFailure : ICompilationFailure
{
/// <summary>Initializes a new instance of <see cref="CompilationFailure"/>.</summary>
/// <param name="filePath">The path of the Razor source file that was compiled.</param>
/// <param name="fileContent">The contents of the Razor source file.</param>
/// <param name="compiledContent">The generated C# content that was compiled.</param>
/// <param name="messages">A sequence of <see cref="ICompilationMessage"/> encountered
/// during compilation.</param>
public CompilationFailure(
[NotNull] string filePath,
[NotNull] string fileContent,
[NotNull] string compiledContent,
[NotNull] IEnumerable<ICompilationMessage> messages)
{
SourceFilePath = filePath;
SourceFileContent = fileContent;
Messages = messages;
CompiledContent = compiledContent;
}
/// <summary>
/// Gets the path of the Razor source file that produced the compilation failure.
/// </summary>
public string SourceFilePath { get; }
/// <summary>
/// Gets the content of the Razor source file.
/// </summary>
public string SourceFileContent { get; }
/// <summary>
/// Gets the generated C# content that was compiled.
/// </summary>
public string CompiledContent { get; }
/// <summary>
/// Gets a sequence of <see cref="ICompilationMessage"/> instances encountered during compilation.
/// </summary>
public IEnumerable<ICompilationMessage> Messages { get; }
}
}

View File

@ -1,57 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Diagnostics;
namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// Represents a message encountered during compilation.
/// </summary>
public class CompilationMessage : ICompilationMessage
{
/// <summary>
/// Initializes a <see cref="CompilationMessage"/> with the specified message.
/// </summary>
/// <param name="message">A message <see cref="string"/> produced from compilation.</param>
public CompilationMessage(string message,
int startColumn,
int startLine,
int endColumn,
int endLine)
{
Message = message;
StartColumn = startColumn;
StartLine = startLine;
EndColumn = endColumn;
EndLine = endLine;
}
/// <summary>
/// Gets a message produced from compilation.
/// </summary>
public string Message { get; }
/// <inheritdoc />
public int StartColumn { get; }
/// <inheritdoc />
public int StartLine { get; }
/// <inheritdoc />
public int EndColumn { get; }
/// <inheritdoc />
public int EndLine { get; }
/// <summary>
/// Returns a <see cref="string"/> representation of this instance of <see cref="CompilationMessage"/>.
/// </summary>
/// <returns>A <see cref="string"/> representing this <see cref="CompilationMessage"/> instance.</returns>
/// <remarks>Returns same value as <see cref="Message"/>.</remarks>
public override string ToString()
{
return Message;
}
}
}

View File

@ -2,9 +2,7 @@
// 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 Microsoft.AspNet.FileProviders;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Razor
@ -14,8 +12,6 @@ namespace Microsoft.AspNet.Mvc.Razor
/// </summary>
public class CompilationResult
{
private Type _type;
/// <summary>
/// Creates a new instance of <see cref="CompilationResult"/>.
/// </summary>
@ -24,24 +20,10 @@ namespace Microsoft.AspNet.Mvc.Razor
}
/// <summary>
/// Gets the path of the Razor file that was compiled.
/// Gets (or sets in derived types) the type produced as a result of compilation.
/// </summary>
public string FilePath
{
get
{
if (File != null)
{
return File.PhysicalPath;
}
return null;
}
}
/// <summary>
/// Gets a sequence of <see cref="CompilationMessage"/> instances encountered during compilation.
/// </summary>
public IEnumerable<CompilationMessage> Messages { get; private set; }
/// <remarks>This property is <c>null</c> when compilation failed.</remarks>
public Type CompiledType { get; protected set; }
/// <summary>
/// Gets (or sets in derived types) the generated C# content that was compiled.
@ -49,52 +31,45 @@ namespace Microsoft.AspNet.Mvc.Razor
public string CompiledContent { get; protected set; }
/// <summary>
/// Gets (or sets in derived types) the type produced as a result of compilation.
/// Gets the <see cref="ICompilationFailure"/> produced from parsing or compiling the Razor file.
/// </summary>
/// <exception cref="CompilationFailedException">An error occured during compilation.</exception>
public Type CompiledType
{
get
{
if (_type == null)
{
throw CreateCompilationFailedException();
}
return _type;
}
protected set
{
_type = value;
}
}
private IFileInfo File { get; set; }
/// <remarks>This property is <c>null</c> when compilation succeeded.</remarks>
public ICompilationFailure CompilationFailure { get; private set; }
/// <summary>
/// Creates a <see cref="CompilationResult"/> that represents a failure in compilation.
/// Gets the <see cref="CompilationResult"/>.
/// </summary>
/// <param name="fileInfo">The <see cref="IFileInfo"/> for the Razor file that was compiled.</param>
/// <param name="compilationContent">The generated C# content to be compiled.</param>
/// <param name="messages">The sequence of failure messages encountered during compilation.</param>
/// <returns>A CompilationResult instance representing a failure.</returns>
public static CompilationResult Failed([NotNull] IFileInfo file,
[NotNull] string compilationContent,
[NotNull] IEnumerable<CompilationMessage> messages)
/// <returns>The current <see cref="CompilationResult"/> instance.</returns>
/// <exception cref="CompilationFailedException">Thrown if compilation failed.</exception>
public CompilationResult EnsureSuccessful()
{
if (CompilationFailure != null)
{
throw new CompilationFailedException(CompilationFailure);
}
return this;
}
/// <summary>
/// Creates a <see cref="CompilationResult"/> for a failed compilation.
/// </summary>
/// <param name="compilationFailure">The <see cref="ICompilationFailure"/> produced from parsing or
/// compiling the Razor file.</param>
/// <returns>A <see cref="CompilationResult"/> instance for a failed compilation.</returns>
public static CompilationResult Failed([NotNull] ICompilationFailure compilationFailure)
{
return new CompilationResult
{
File = file,
CompiledContent = compilationContent,
Messages = messages,
CompilationFailure = compilationFailure
};
}
/// <summary>
/// Creates a <see cref="CompilationResult"/> that represents a success in compilation.
/// Creates a <see cref="CompilationResult"/> for a successful compilation.
/// </summary>
/// <param name="type">The compiled type.</param>
/// <returns>A CompilationResult instance representing a success.</returns>
/// <returns>A <see cref="CompilationResult"/> instance for a successful compilation.</returns>
public static CompilationResult Successful([NotNull] Type type)
{
return new CompilationResult
@ -102,31 +77,5 @@ namespace Microsoft.AspNet.Mvc.Razor
CompiledType = type
};
}
private CompilationFailedException CreateCompilationFailedException()
{
var fileContent = ReadContent(File);
var compilationFailure = new CompilationFailure(FilePath, fileContent, CompiledContent, Messages);
return new CompilationFailedException(compilationFailure);
}
private static string ReadContent(IFileInfo file)
{
try
{
using (var stream = file.CreateReadStream())
{
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
catch (Exception)
{
// Don't throw if reading the file fails.
return string.Empty;
}
}
}
}

View File

@ -180,7 +180,7 @@ namespace Microsoft.AspNet.Mvc.Razor
string normalizedPath,
Func<RelativeFileInfo, CompilationResult> compile)
{
var compilationResult = compile(file);
var compilationResult = compile(file).EnsureSuccessful();
// Concurrent addition to MemoryCache with the same key result in safe race.
var cacheEntry = _cache.Set(normalizedPath,

View File

@ -1,8 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.FileProviders;
namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
@ -13,11 +11,11 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <summary>
/// Compiles content and returns the result of compilation.
/// </summary>
/// <param name="fileInfo">The <see cref="IFileInfo"/> for the Razor file that was compiled.</param>
/// <param name="fileInfo">The <see cref="RelativeFileInfo"/> for the Razor file that was compiled.</param>
/// <param name="compilationContent">The generated C# content to be compiled.</param>
/// <returns>
/// A <see cref="CompilationResult"/> representing the result of compilation.
/// </returns>
CompilationResult Compile(IFileInfo fileInfo, string compilationContent);
CompilationResult Compile(RelativeFileInfo fileInfo, string compilationContent);
}
}

View File

@ -0,0 +1,42 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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 Microsoft.Framework.Runtime;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// <see cref="ICompilationFailure"/> for Razor parse failures.
/// </summary>
public class RazorCompilationFailure : ICompilationFailure
{
/// <summary>Initializes a new instance of <see cref="RazorCompilationFailure"/>.</summary>
/// <param name="sourceFilePath">The path of the Razor source file that was compiled.</param>
/// <param name="sourceFileContent">The contents of the Razor source file.</param>
/// <param name="messages">A sequence of <see cref="ICompilationMessage"/> encountered
/// during compilation.</param>
public RazorCompilationFailure(
[NotNull] string sourceFilePath,
[NotNull] string sourceFileContent,
[NotNull] IEnumerable<RazorCompilationMessage> messages)
{
SourceFilePath = sourceFilePath;
SourceFileContent = sourceFileContent;
Messages = messages;
}
/// <inheritdoc />
public string SourceFilePath { get; }
/// <inheritdoc />
public string SourceFileContent { get; }
/// <inheritdoc />
public string CompiledContent { get; } = null;
/// <inheritdoc />
public IEnumerable<ICompilationMessage> Messages { get; }
}
}

View File

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.Framework.Internal;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// <see cref="ICompilationMessage"/> for a <see cref="RazorError"/> encountered during parsing.
/// </summary>
public class RazorCompilationMessage : ICompilationMessage
{
/// <summary>
/// Initializes a <see cref="RazorCompilationMessage"/> with the specified message.
/// </summary>
/// <param name="razorError">A <see cref="RazorError"/>.</param>
/// <param name="sourceFilePath">The path of the Razor source file that was parsed.</param>
public RazorCompilationMessage(
[NotNull] RazorError razorError,
string sourceFilePath)
{
SourceFilePath = sourceFilePath;
Message = razorError.Message;
var location = razorError.Location;
FormattedMessage =
$"{sourceFilePath} ({location.LineIndex},{location.CharacterIndex}) {razorError.Message}";
StartColumn = location.CharacterIndex;
StartLine = location.LineIndex;
EndColumn = location.CharacterIndex + razorError.Length;
EndLine = location.LineIndex;
}
/// <summary>
/// Gets a message produced from compilation.
/// </summary>
public string Message { get; }
/// <inheritdoc />
public int StartColumn { get; }
/// <inheritdoc />
public int StartLine { get; }
/// <inheritdoc />
public int EndColumn { get; }
/// <inheritdoc />
public int EndLine { get; }
/// <inheritdoc />
public string SourceFilePath { get; }
/// <inheritdoc />
public string FormattedMessage { get; }
/// <inheritdoc />
// All Razor diagnostics are errors
public CompilationMessageSeverity Severity { get; } = CompilationMessageSeverity.Error;
/// <summary>
/// Returns a <see cref="string"/> representation of this instance of <see cref="RazorCompilationMessage"/>.
/// </summary>
/// <returns>A <see cref="string"/> representing this <see cref="RazorCompilationMessage"/> instance.</returns>
/// <remarks>Returns same value as <see cref="Message"/>.</remarks>
public override string ToString()
{
return FormattedMessage;
}
}
}

View File

@ -60,12 +60,12 @@ namespace Microsoft.AspNet.Mvc.Razor
}
/// <inheritdoc />
public CompilationResult Compile([NotNull] IFileInfo fileInfo, [NotNull] string compilationContent)
public CompilationResult Compile([NotNull] RelativeFileInfo fileInfo, [NotNull] string compilationContent)
{
// The path passed to SyntaxTreeGenerator.Generate is used by the compiler to generate symbols (pdb) that
// map to the source file. If a file does not exist on a physical file system, PhysicalPath will be null.
// This prevents files that exist in a non-physical file system from being debugged.
var path = fileInfo.PhysicalPath ?? fileInfo.Name;
var path = fileInfo.FileInfo.PhysicalPath ?? fileInfo.RelativePath;
var compilationSettings = _compilerOptionsProvider.GetCompilationSettings(_environment);
var syntaxTree = SyntaxTreeGenerator.Generate(compilationContent,
path,
@ -98,14 +98,15 @@ namespace Microsoft.AspNet.Mvc.Razor
if (!result.Success)
{
var formatter = new DiagnosticFormatter();
var failures = result.Diagnostics.Where(IsError);
var compilationFailure = new RoslynCompilationFailure(failures)
{
CompiledContent = compilationContent,
SourceFileContent = ReadFileContentsSafely(fileInfo.FileInfo),
SourceFilePath = fileInfo.RelativePath
};
var messages = result.Diagnostics
.Where(IsError)
.Select(d => GetCompilationMessage(formatter, d))
.ToList();
return CompilationResult.Failed(fileInfo, compilationContent, messages);
return CompilationResult.Failed(compilationFailure);
}
Assembly assembly;
@ -215,21 +216,25 @@ namespace Microsoft.AspNet.Mvc.Razor
return metadata.GetReference();
}
private static CompilationMessage GetCompilationMessage(DiagnosticFormatter formatter, Diagnostic diagnostic)
{
var lineSpan = diagnostic.Location.GetMappedLineSpan();
return new CompilationMessage(formatter.Format(diagnostic),
startColumn: lineSpan.StartLinePosition.Character,
startLine: lineSpan.StartLinePosition.Line,
endColumn: lineSpan.EndLinePosition.Character,
endLine: lineSpan.EndLinePosition.Line);
}
private static bool IsError(Diagnostic diagnostic)
{
return diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error;
}
private static string ReadFileContentsSafely(IFileInfo fileInfo)
{
try
{
using (var reader = new StreamReader(fileInfo.CreateReadStream()))
{
return reader.ReadToEnd();
}
}
catch
{
// Ignore any failures
return null;
}
}
}
}

View File

@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Mvc.Razor
}
/// <summary>
/// Error compiling page at '{0}'.
/// One or more compilation failures occured:
/// </summary>
internal static string CompilationFailed
{
@ -35,11 +35,11 @@ namespace Microsoft.AspNet.Mvc.Razor
}
/// <summary>
/// Error compiling page at '{0}'.
/// One or more compilation failures occured:
/// </summary>
internal static string FormatCompilationFailed(object p0)
internal static string FormatCompilationFailed()
{
return string.Format(CultureInfo.CurrentCulture, GetString("CompilationFailed"), p0);
return GetString("CompilationFailed");
}
/// <summary>

View File

@ -1,7 +1,9 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Linq;
using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Razor;
using Microsoft.Framework.Internal;
@ -35,16 +37,32 @@ namespace Microsoft.AspNet.Mvc.Razor
if (!results.Success)
{
var messages = results.ParserErrors
.Select(parseError =>
new CompilationMessage(parseError.Message,
parseError.Location.CharacterIndex,
parseError.Location.LineIndex,
parseError.Location.CharacterIndex + parseError.Length,
parseError.Location.LineIndex));
return CompilationResult.Failed(file.FileInfo, results.GeneratedCode, messages);
.Select(parseError => new RazorCompilationMessage(parseError, file.RelativePath));
var failure = new RazorCompilationFailure(
file.RelativePath,
ReadFileContentsSafely(file.FileInfo),
messages);
return CompilationResult.Failed(failure);
}
return _compilationService.Compile(file.FileInfo, results.GeneratedCode);
return _compilationService.Compile(file, results.GeneratedCode);
}
private static string ReadFileContentsSafely(IFileInfo fileInfo)
{
try
{
using (var reader = new StreamReader(fileInfo.CreateReadStream()))
{
return reader.ReadToEnd();
}
}
catch
{
// Ignore any failures
return null;
}
}
}
}

View File

@ -121,7 +121,7 @@
<value>Value cannot be null or empty.</value>
</data>
<data name="CompilationFailed" xml:space="preserve">
<value>Error compiling page at '{0}'.</value>
<value>One or more compilation failures occured:</value>
</data>
<data name="FlushPointCannotBeInvoked" xml:space="preserve">
<value>'{0}' cannot be invoked when a Layout page is set to be executed.</value>

View File

@ -1,9 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Text;
using Microsoft.AspNet.FileProviders;
using Microsoft.Framework.Runtime;
using Moq;
using Xunit;
@ -12,32 +10,18 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
public class CompilationResultTest
{
[Fact]
public void FailedResult_ThrowsWhenAccessingCompiledType()
public void EnsureSuccessful_ThrowsIfCompilationFailed()
{
// Arrange
var expected = @"Error compiling page at 'myfile'.";
var originalContent = "Original file content";
var fileInfo = new Mock<IFileInfo>();
fileInfo.SetupGet(f => f.PhysicalPath)
.Returns("myfile");
var contentBytes = Encoding.UTF8.GetBytes(originalContent);
fileInfo.Setup(f => f.CreateReadStream())
.Returns(new MemoryStream(contentBytes));
var messages = new[]
{
new CompilationMessage("hello", 1, 1, 2, 2),
new CompilationMessage("world", 3, 3, 4, 3)
};
var result = CompilationResult.Failed(fileInfo.Object,
"<h1>hello world</h1>",
messages);
var compilationFailure = Mock.Of<ICompilationFailure>();
var result = CompilationResult.Failed(compilationFailure);
// Act and Assert
var ex = Assert.Throws<CompilationFailedException>(() => result.CompiledType);
Assert.Equal(expected, ex.Message);
var compilationFailure = Assert.Single(ex.CompilationFailures);
Assert.Equal(originalContent, compilationFailure.SourceFileContent);
Assert.Equal(messages, compilationFailure.Messages);
Assert.Null(result.CompiledType);
Assert.Same(compilationFailure, result.CompilationFailure);
var exception = Assert.Throws<CompilationFailedException>(() => result.EnsureSuccessful());
Assert.Collection(exception.CompilationFailures,
failure => Assert.Same(compilationFailure, failure));
}
}
}

View File

@ -50,7 +50,7 @@ namespace Microsoft.AspNet.Mvc.Razor
// Assert
Assert.NotSame(CompilerCacheResult.FileNotFound, result);
var actual = result.CompilationResult;
var actual = Assert.IsType<UncachedCompilationResult>(result.CompilationResult);
Assert.NotNull(actual);
Assert.Same(expected, actual);
Assert.Equal("hello world", actual.CompiledContent);
@ -541,23 +541,18 @@ namespace Microsoft.AspNet.Mvc.Razor
// Act
cache.GetOrAdd("test", _ => uncachedResult);
var result1 = cache.GetOrAdd("test", _ => uncachedResult);
var result2 = cache.GetOrAdd("test", _ => uncachedResult);
var result1 = cache.GetOrAdd("test", _ => { throw new Exception("shouldn't be called."); });
var result2 = cache.GetOrAdd("test", _ => { throw new Exception("shouldn't be called."); });
// Assert
Assert.NotSame(CompilerCacheResult.FileNotFound, result1);
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
var actual1 = result1.CompilationResult;
var actual2 = result2.CompilationResult;
var actual1 = Assert.IsType<CompilationResult>(result1.CompilationResult);
var actual2 = Assert.IsType<CompilationResult>(result2.CompilationResult);
Assert.NotSame(uncachedResult, actual1);
Assert.NotSame(uncachedResult, actual2);
var result = Assert.IsType<CompilationResult>(actual1);
Assert.Null(actual1.CompiledContent);
Assert.Same(type, actual1.CompiledType);
result = Assert.IsType<CompilationResult>(actual2);
Assert.Null(actual2.CompiledContent);
Assert.Same(type, actual2.CompiledType);
}

View File

@ -31,14 +31,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
fileInfo.Setup(f => f.PhysicalPath).Returns(viewPath);
fileInfo.Setup(f => f.CreateReadStream()).Returns(Stream.Null);
var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml");
var compiler = new Mock<ICompilationService>();
compiler.Setup(c => c.Compile(fileInfo.Object, It.IsAny<string>()))
compiler.Setup(c => c.Compile(relativeFileInfo, It.IsAny<string>()))
.Returns(CompilationResult.Successful(typeof(RazorCompilationServiceTest)));
var razorService = new RazorCompilationService(compiler.Object, host.Object);
var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml");
// Act
razorService.Compile(relativeFileInfo);
@ -75,9 +75,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var result = razorService.Compile(relativeFileInfo);
// Assert
var ex = Assert.Throws<CompilationFailedException>(() => result.CompiledType);
var failure = Assert.Single(ex.CompilationFailures);
var message = Assert.Single(failure.Messages);
Assert.NotNull(result.CompilationFailure);
var message = Assert.Single(result.CompilationFailure.Messages);
Assert.Equal("some message", message.Message);
host.Verify();
}
@ -100,13 +99,13 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
var fileInfo = new Mock<IFileInfo>();
fileInfo.Setup(f => f.CreateReadStream())
.Returns(Stream.Null);
var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml");
var compilationResult = CompilationResult.Successful(typeof(object));
var compiler = new Mock<ICompilationService>();
compiler.Setup(c => c.Compile(fileInfo.Object, code))
compiler.Setup(c => c.Compile(relativeFileInfo, code))
.Returns(compilationResult)
.Verifiable();
var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml");
var razorService = new RazorCompilationService(compiler.Object, host.Object);
// Act

View File

@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Reflection;
using System.Runtime.Versioning;
using Microsoft.AspNet.FileProviders;
using Microsoft.Framework.Runtime;
using Moq;
using Xunit;
@ -37,14 +38,126 @@ public class MyTestType {}";
libraryManager,
compilerOptionsProvider.Object,
mvcRazorHost.Object);
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path");
// Act
var result = compilationService.Compile(new TestFileInfo { PhysicalPath = "SomePath" }, content);
var result = compilationService.Compile(relativeFileInfo, content);
// Assert
var uncachedResult = Assert.IsType<UncachedCompilationResult>(result);
Assert.Equal("MyTestType", result.CompiledType.Name);
Assert.Equal(content, result.CompiledContent);
Assert.Equal(content, uncachedResult.CompiledContent);
}
[Fact]
public void Compile_ReturnsCompilationFailureWithRelativePath()
{
// Arrange
var fileContent = "test file content";
var content = @"this should fail";
var applicationEnvironment = GetApplicationEnvironment();
var accessor = GetLoadContextAccessor();
var libraryManager = GetLibraryManager();
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationBasePath,
applicationEnvironment.RuntimeFramework,
applicationEnvironment.Configuration))
.Returns(new CompilerOptions());
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
var compilationService = new RoslynCompilationService(applicationEnvironment,
accessor,
libraryManager,
compilerOptionsProvider.Object,
mvcRazorHost);
var fileInfo = new TestFileInfo
{
Content = fileContent,
PhysicalPath = "physical path"
};
var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");
// Act
var result = compilationService.Compile(relativeFileInfo, content);
// Assert
Assert.IsType<CompilationResult>(result);
Assert.Null(result.CompiledType);
Assert.Equal(relativeFileInfo.RelativePath, result.CompilationFailure.SourceFilePath);
Assert.Equal(fileContent, result.CompilationFailure.SourceFileContent);
}
[Fact]
public void Compile_ReturnsApplicationRelativePath_IfPhyicalPathIsNotSpecified()
{
// Arrange
var fileContent = "file content";
var content = @"this should fail";
var applicationEnvironment = GetApplicationEnvironment();
var accessor = GetLoadContextAccessor();
var libraryManager = GetLibraryManager();
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationBasePath,
applicationEnvironment.RuntimeFramework,
applicationEnvironment.Configuration))
.Returns(new CompilerOptions());
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
var compilationService = new RoslynCompilationService(applicationEnvironment,
accessor,
libraryManager,
compilerOptionsProvider.Object,
mvcRazorHost);
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { Content = fileContent },
"some-relative-path");
// Act
var result = compilationService.Compile(relativeFileInfo, content);
// Assert
Assert.IsType<CompilationResult>(result);
Assert.Null(result.CompiledType);
Assert.Equal("some-relative-path", result.CompilationFailure.SourceFilePath);
Assert.Equal(fileContent, result.CompilationFailure.SourceFileContent);
}
[Fact]
public void Compile_DoesNotThrow_IfFileCannotBeRead()
{
// Arrange
var content = @"this should fail";
var applicationEnvironment = GetApplicationEnvironment();
var accessor = GetLoadContextAccessor();
var libraryManager = GetLibraryManager();
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationBasePath,
applicationEnvironment.RuntimeFramework,
applicationEnvironment.Configuration))
.Returns(new CompilerOptions());
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
var compilationService = new RoslynCompilationService(applicationEnvironment,
accessor,
libraryManager,
compilerOptionsProvider.Object,
mvcRazorHost);
var mockFileInfo = new Mock<IFileInfo>();
mockFileInfo.Setup(f => f.CreateReadStream())
.Throws(new Exception());
var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, "some-relative-path");
// Act
var result = compilationService.Compile(relativeFileInfo, content);
// Assert
Assert.IsType<CompilationResult>(result);
Assert.Null(result.CompiledType);
Assert.Equal("some-relative-path", result.CompilationFailure.SourceFilePath);
Assert.Null(result.CompilationFailure.SourceFileContent);
}
[Fact]
@ -76,9 +189,11 @@ public class MyNonCustomDefinedClass {}
libraryManager,
compilerOptionsProvider.Object,
mvcRazorHost.Object);
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path");
// Act
var result = compilationService.Compile(new TestFileInfo { PhysicalPath = "SomePath" }, content);
var result = compilationService.Compile(relativeFileInfo, content);
// Assert
Assert.NotNull(result.CompiledType);
@ -111,8 +226,11 @@ public class NotRazorPrefixType {}";
compilerOptionsProvider.Object,
mvcRazorHost.Object);
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path");
// Act
var result = compilationService.Compile(new TestFileInfo { PhysicalPath = "SomePath" }, content);
var result = compilationService.Compile(relativeFileInfo, content);
// Assert
Assert.NotNull(result.CompiledType);