Added SourceLocation to inherited chunks \ tag helpers
Updated CompilationResult to support compilation failures from multiple files. Fixes #2321
This commit is contained in:
parent
f878ca5b15
commit
fb451b51e5
|
|
@ -126,22 +126,33 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
||||||
|
|
||||||
private static CodeTree ParseViewFile(RazorTemplateEngine engine,
|
private static CodeTree ParseViewFile(RazorTemplateEngine engine,
|
||||||
IFileInfo fileInfo,
|
IFileInfo fileInfo,
|
||||||
string viewStartPath)
|
string globalImportPath)
|
||||||
{
|
{
|
||||||
using (var stream = fileInfo.CreateReadStream())
|
using (var stream = fileInfo.CreateReadStream())
|
||||||
{
|
{
|
||||||
using (var streamReader = new StreamReader(stream))
|
using (var streamReader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
var parseResults = engine.ParseTemplate(streamReader, viewStartPath);
|
var parseResults = engine.ParseTemplate(streamReader, globalImportPath);
|
||||||
var className = ParserHelpers.SanitizeClassName(fileInfo.Name);
|
var className = ParserHelpers.SanitizeClassName(fileInfo.Name);
|
||||||
var language = engine.Host.CodeLanguage;
|
var language = engine.Host.CodeLanguage;
|
||||||
var codeGenerator = language.CreateCodeGenerator(className,
|
var codeGenerator = language.CreateCodeGenerator(className,
|
||||||
engine.Host.DefaultNamespace,
|
engine.Host.DefaultNamespace,
|
||||||
viewStartPath,
|
globalImportPath,
|
||||||
engine.Host);
|
engine.Host);
|
||||||
codeGenerator.Visit(parseResults);
|
codeGenerator.Visit(parseResults);
|
||||||
|
|
||||||
return codeGenerator.Context.CodeTreeBuilder.CodeTree;
|
// Rewrite the location of inherited chunks so they point to the global import file.
|
||||||
|
var codeTree = codeGenerator.Context.CodeTreeBuilder.CodeTree;
|
||||||
|
foreach (var chunk in codeTree.Chunks)
|
||||||
|
{
|
||||||
|
chunk.Start = new SourceLocation(
|
||||||
|
globalImportPath,
|
||||||
|
chunk.Start.AbsoluteIndex,
|
||||||
|
chunk.Start.LineIndex,
|
||||||
|
chunk.Start.CharacterIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return codeTree;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
var descriptor = new TagHelperDirectiveDescriptor(
|
var descriptor = new TagHelperDirectiveDescriptor(
|
||||||
addTagHelperChunk.LookupText,
|
addTagHelperChunk.LookupText,
|
||||||
SourceLocation.Undefined,
|
chunk.Start,
|
||||||
TagHelperDirectiveType.AddTagHelper);
|
TagHelperDirectiveType.AddTagHelper);
|
||||||
|
|
||||||
descriptors.Add(descriptor);
|
descriptors.Add(descriptor);
|
||||||
|
|
@ -89,7 +89,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
var descriptor = new TagHelperDirectiveDescriptor(
|
var descriptor = new TagHelperDirectiveDescriptor(
|
||||||
removeTagHelperChunk.LookupText,
|
removeTagHelperChunk.LookupText,
|
||||||
SourceLocation.Undefined,
|
chunk.Start,
|
||||||
TagHelperDirectiveType.RemoveTagHelper);
|
TagHelperDirectiveType.RemoveTagHelper);
|
||||||
|
|
||||||
descriptors.Add(descriptor);
|
descriptors.Add(descriptor);
|
||||||
|
|
@ -102,7 +102,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
var descriptor = new TagHelperDirectiveDescriptor(
|
var descriptor = new TagHelperDirectiveDescriptor(
|
||||||
tagHelperPrefixDirectiveChunk.Prefix,
|
tagHelperPrefixDirectiveChunk.Prefix,
|
||||||
SourceLocation.Undefined,
|
chunk.Start,
|
||||||
TagHelperDirectiveType.TagHelperPrefix);
|
TagHelperDirectiveType.TagHelperPrefix);
|
||||||
|
|
||||||
descriptors.Add(descriptor);
|
descriptors.Add(descriptor);
|
||||||
|
|
|
||||||
|
|
@ -17,24 +17,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Instantiates a new instance of <see cref="CompilationFailedException"/>.
|
/// Instantiates a new instance of <see cref="CompilationFailedException"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="compilationFailure">The <see cref="ICompilationFailure"/> instance containing
|
/// <param name="compilationFailures"><see cref="ICompilationFailure"/>s containing
|
||||||
/// details of the compilation failure.</param>
|
/// details of the compilation failure.</param>
|
||||||
public CompilationFailedException(
|
public CompilationFailedException(
|
||||||
[NotNull] ICompilationFailure compilationFailure)
|
[NotNull] IEnumerable<ICompilationFailure> compilationFailures)
|
||||||
: base(FormatMessage(compilationFailure))
|
: base(FormatMessage(compilationFailures))
|
||||||
{
|
{
|
||||||
CompilationFailures = new[] { compilationFailure };
|
CompilationFailures = compilationFailures;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<ICompilationFailure> CompilationFailures { get; }
|
public IEnumerable<ICompilationFailure> CompilationFailures { get; }
|
||||||
|
|
||||||
private static string FormatMessage(ICompilationFailure compilationFailure)
|
private static string FormatMessage(IEnumerable<ICompilationFailure> compilationFailures)
|
||||||
{
|
{
|
||||||
return Resources.CompilationFailed + Environment.NewLine +
|
return Resources.CompilationFailed + Environment.NewLine +
|
||||||
string.Join(
|
string.Join(
|
||||||
Environment.NewLine,
|
Environment.NewLine,
|
||||||
compilationFailure.Messages.Select(message => message.FormattedMessage));
|
compilationFailures.SelectMany(f => f.Messages).Select(message => message.FormattedMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@
|
||||||
// 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 Microsoft.Framework.Runtime;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Runtime;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
{
|
{
|
||||||
|
|
@ -31,10 +33,11 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
public string CompiledContent { get; protected set; }
|
public string CompiledContent { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <see cref="ICompilationFailure"/> produced from parsing or compiling the Razor file.
|
/// Gets the <see cref="ICompilationFailure"/>s produced from parsing or compiling the Razor file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>This property is <c>null</c> when compilation succeeded.</remarks>
|
/// <remarks>This property is <c>null</c> when compilation succeeded. An empty sequence
|
||||||
public ICompilationFailure CompilationFailure { get; private set; }
|
/// indicates a failed compilation.</remarks>
|
||||||
|
public IEnumerable<ICompilationFailure> CompilationFailures { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <see cref="CompilationResult"/>.
|
/// Gets the <see cref="CompilationResult"/>.
|
||||||
|
|
@ -43,9 +46,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
/// <exception cref="CompilationFailedException">Thrown if compilation failed.</exception>
|
/// <exception cref="CompilationFailedException">Thrown if compilation failed.</exception>
|
||||||
public CompilationResult EnsureSuccessful()
|
public CompilationResult EnsureSuccessful()
|
||||||
{
|
{
|
||||||
if (CompilationFailure != null)
|
if (CompilationFailures != null)
|
||||||
{
|
{
|
||||||
throw new CompilationFailedException(CompilationFailure);
|
throw new CompilationFailedException(CompilationFailures);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -54,14 +57,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="CompilationResult"/> for a failed compilation.
|
/// Creates a <see cref="CompilationResult"/> for a failed compilation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="compilationFailure">The <see cref="ICompilationFailure"/> produced from parsing or
|
/// <param name="compilationFailures"><see cref="ICompilationFailure"/>s produced from parsing or
|
||||||
/// compiling the Razor file.</param>
|
/// compiling the Razor file.</param>
|
||||||
/// <returns>A <see cref="CompilationResult"/> instance for a failed compilation.</returns>
|
/// <returns>A <see cref="CompilationResult"/> instance for a failed compilation.</returns>
|
||||||
public static CompilationResult Failed([NotNull] ICompilationFailure compilationFailure)
|
public static CompilationResult Failed([NotNull] IEnumerable<ICompilationFailure> compilationFailures)
|
||||||
{
|
{
|
||||||
return new CompilationResult
|
return new CompilationResult
|
||||||
{
|
{
|
||||||
CompilationFailure = compilationFailure
|
CompilationFailures = compilationFailures
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// 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.
|
// 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.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNet.FileProviders;
|
using Microsoft.AspNet.FileProviders;
|
||||||
using Microsoft.AspNet.Razor;
|
using Microsoft.AspNet.Razor;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
{
|
{
|
||||||
|
|
@ -16,12 +19,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
{
|
{
|
||||||
private readonly ICompilationService _compilationService;
|
private readonly ICompilationService _compilationService;
|
||||||
private readonly IMvcRazorHost _razorHost;
|
private readonly IMvcRazorHost _razorHost;
|
||||||
|
private readonly IFileProvider _fileProvider;
|
||||||
|
|
||||||
public RazorCompilationService(ICompilationService compilationService,
|
public RazorCompilationService(ICompilationService compilationService,
|
||||||
IMvcRazorHost razorHost)
|
IMvcRazorHost razorHost,
|
||||||
|
IOptions<RazorViewEngineOptions> viewEngineOptions)
|
||||||
{
|
{
|
||||||
_compilationService = compilationService;
|
_compilationService = compilationService;
|
||||||
_razorHost = razorHost;
|
_razorHost = razorHost;
|
||||||
|
_fileProvider = viewEngineOptions.Options.FileProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -30,39 +36,61 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
GeneratorResults results;
|
GeneratorResults results;
|
||||||
using (var inputStream = file.FileInfo.CreateReadStream())
|
using (var inputStream = file.FileInfo.CreateReadStream())
|
||||||
{
|
{
|
||||||
results = _razorHost.GenerateCode(
|
results = _razorHost.GenerateCode(file.RelativePath, inputStream);
|
||||||
file.RelativePath, inputStream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!results.Success)
|
if (!results.Success)
|
||||||
{
|
{
|
||||||
var messages = results.ParserErrors
|
return GetCompilationFailedResult(file, results.ParserErrors);
|
||||||
.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, results.GeneratedCode);
|
return _compilationService.Compile(file, results.GeneratedCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ReadFileContentsSafely(IFileInfo fileInfo)
|
// Internal for unit testing
|
||||||
|
internal CompilationResult GetCompilationFailedResult(RelativeFileInfo file, IEnumerable<RazorError> errors)
|
||||||
{
|
{
|
||||||
try
|
// If a SourceLocation does not specify a file path, assume it is produced
|
||||||
|
// from parsing the current file.
|
||||||
|
var messageGroups = errors
|
||||||
|
.GroupBy(razorError =>
|
||||||
|
razorError.Location.FilePath ?? file.RelativePath,
|
||||||
|
StringComparer.Ordinal);
|
||||||
|
|
||||||
|
var failures = new List<RazorCompilationFailure>();
|
||||||
|
foreach (var group in messageGroups)
|
||||||
{
|
{
|
||||||
using (var reader = new StreamReader(fileInfo.CreateReadStream()))
|
var filePath = group.Key;
|
||||||
|
var fileContent = ReadFileContentsSafely(filePath);
|
||||||
|
var compilationFailure = new RazorCompilationFailure(
|
||||||
|
filePath,
|
||||||
|
fileContent,
|
||||||
|
group.Select(parserError => new RazorCompilationMessage(parserError, filePath)));
|
||||||
|
failures.Add(compilationFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompilationResult.Failed(failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ReadFileContentsSafely(string relativePath)
|
||||||
|
{
|
||||||
|
var fileInfo = _fileProvider.GetFileInfo(relativePath);
|
||||||
|
if (fileInfo.Exists)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
return reader.ReadToEnd();
|
using (var reader = new StreamReader(fileInfo.CreateReadStream()))
|
||||||
|
{
|
||||||
|
return reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore any failures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
{
|
return null;
|
||||||
// Ignore any failures
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
using Microsoft.CodeAnalysis.Emit;
|
using Microsoft.CodeAnalysis.Emit;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Microsoft.Framework.Runtime;
|
using Microsoft.Framework.Runtime;
|
||||||
using Microsoft.Framework.Runtime.Compilation;
|
using Microsoft.Framework.Runtime.Compilation;
|
||||||
using Microsoft.Framework.Runtime.Roslyn;
|
using Microsoft.Framework.Runtime.Roslyn;
|
||||||
|
|
@ -34,9 +35,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
private readonly IApplicationEnvironment _environment;
|
private readonly IApplicationEnvironment _environment;
|
||||||
private readonly IAssemblyLoadContext _loader;
|
private readonly IAssemblyLoadContext _loader;
|
||||||
private readonly ICompilerOptionsProvider _compilerOptionsProvider;
|
private readonly ICompilerOptionsProvider _compilerOptionsProvider;
|
||||||
|
private readonly IFileProvider _fileProvider;
|
||||||
private readonly Lazy<List<MetadataReference>> _applicationReferences;
|
private readonly Lazy<List<MetadataReference>> _applicationReferences;
|
||||||
|
|
||||||
private readonly string _classPrefix;
|
private readonly string _classPrefix;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -50,30 +50,28 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
IAssemblyLoadContextAccessor loaderAccessor,
|
IAssemblyLoadContextAccessor loaderAccessor,
|
||||||
ILibraryManager libraryManager,
|
ILibraryManager libraryManager,
|
||||||
ICompilerOptionsProvider compilerOptionsProvider,
|
ICompilerOptionsProvider compilerOptionsProvider,
|
||||||
IMvcRazorHost host)
|
IMvcRazorHost host,
|
||||||
|
IOptions<RazorViewEngineOptions> optionsAccessor)
|
||||||
{
|
{
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
_loader = loaderAccessor.GetLoadContext(typeof(RoslynCompilationService).GetTypeInfo().Assembly);
|
_loader = loaderAccessor.GetLoadContext(typeof(RoslynCompilationService).GetTypeInfo().Assembly);
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_applicationReferences = new Lazy<List<MetadataReference>>(GetApplicationReferences);
|
_applicationReferences = new Lazy<List<MetadataReference>>(GetApplicationReferences);
|
||||||
_compilerOptionsProvider = compilerOptionsProvider;
|
_compilerOptionsProvider = compilerOptionsProvider;
|
||||||
|
_fileProvider = optionsAccessor.Options.FileProvider;
|
||||||
_classPrefix = host.MainClassNamePrefix;
|
_classPrefix = host.MainClassNamePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public CompilationResult Compile([NotNull] RelativeFileInfo 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
|
var assemblyName = Path.GetRandomFileName();
|
||||||
// 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.FileInfo.PhysicalPath ?? fileInfo.RelativePath;
|
|
||||||
var compilationSettings = _compilerOptionsProvider.GetCompilationSettings(_environment);
|
var compilationSettings = _compilerOptionsProvider.GetCompilationSettings(_environment);
|
||||||
var syntaxTree = SyntaxTreeGenerator.Generate(compilationContent,
|
var syntaxTree = SyntaxTreeGenerator.Generate(compilationContent,
|
||||||
path,
|
assemblyName,
|
||||||
compilationSettings);
|
compilationSettings);
|
||||||
var references = _applicationReferences.Value;
|
var references = _applicationReferences.Value;
|
||||||
|
|
||||||
var assemblyName = Path.GetRandomFileName();
|
|
||||||
var compilationOptions = compilationSettings.CompilationOptions
|
var compilationOptions = compilationSettings.CompilationOptions
|
||||||
.WithOutputKind(OutputKind.DynamicallyLinkedLibrary);
|
.WithOutputKind(OutputKind.DynamicallyLinkedLibrary);
|
||||||
|
|
||||||
|
|
@ -99,15 +97,11 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
|
|
||||||
if (!result.Success)
|
if (!result.Success)
|
||||||
{
|
{
|
||||||
var failures = result.Diagnostics.Where(IsError);
|
return GetCompilationFailedResult(
|
||||||
var compilationFailure = new RoslynCompilationFailure(failures)
|
fileInfo.RelativePath,
|
||||||
{
|
compilationContent,
|
||||||
CompiledContent = compilationContent,
|
assemblyName,
|
||||||
SourceFileContent = ReadFileContentsSafely(fileInfo.FileInfo),
|
result.Diagnostics);
|
||||||
SourceFilePath = fileInfo.RelativePath
|
|
||||||
};
|
|
||||||
|
|
||||||
return CompilationResult.Failed(compilationFailure);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assembly assembly;
|
Assembly assembly;
|
||||||
|
|
@ -131,6 +125,56 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internal for unit testing
|
||||||
|
internal CompilationResult GetCompilationFailedResult(
|
||||||
|
string relativePath,
|
||||||
|
string compilationContent,
|
||||||
|
string assemblyName,
|
||||||
|
IEnumerable<Diagnostic> diagnostics)
|
||||||
|
{
|
||||||
|
var diagnosticGroups = diagnostics
|
||||||
|
.Where(IsError)
|
||||||
|
.GroupBy(diagnostic => GetFilePath(relativePath, diagnostic), StringComparer.Ordinal);
|
||||||
|
|
||||||
|
var failures = new List<ICompilationFailure>();
|
||||||
|
foreach (var group in diagnosticGroups)
|
||||||
|
{
|
||||||
|
var sourceFilePath = group.Key;
|
||||||
|
string sourceFileContent;
|
||||||
|
if (string.Equals(assemblyName, sourceFilePath, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
// The error is in the generated code and does not have a mapping line pragma
|
||||||
|
sourceFileContent = compilationContent;
|
||||||
|
sourceFilePath = Resources.GeneratedCodeFileName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sourceFileContent = ReadFileContentsSafely(_fileProvider, sourceFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var compilationFailure = new RoslynCompilationFailure(group)
|
||||||
|
{
|
||||||
|
CompiledContent = compilationContent,
|
||||||
|
SourceFileContent = sourceFileContent,
|
||||||
|
SourceFilePath = sourceFilePath
|
||||||
|
};
|
||||||
|
|
||||||
|
failures.Add(compilationFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompilationResult.Failed(failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetFilePath(string relativePath, Diagnostic diagnostic)
|
||||||
|
{
|
||||||
|
if (diagnostic.Location == Location.None)
|
||||||
|
{
|
||||||
|
return relativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return diagnostic.Location.GetMappedLineSpan().Path;
|
||||||
|
}
|
||||||
|
|
||||||
private List<MetadataReference> GetApplicationReferences()
|
private List<MetadataReference> GetApplicationReferences()
|
||||||
{
|
{
|
||||||
var references = new List<MetadataReference>();
|
var references = new List<MetadataReference>();
|
||||||
|
|
@ -222,20 +266,25 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
return diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error;
|
return diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ReadFileContentsSafely(IFileInfo fileInfo)
|
private static string ReadFileContentsSafely(IFileProvider fileProvider, string filePath)
|
||||||
{
|
{
|
||||||
try
|
var fileInfo = fileProvider.GetFileInfo(filePath);
|
||||||
|
if (fileInfo.Exists)
|
||||||
{
|
{
|
||||||
using (var reader = new StreamReader(fileInfo.CreateReadStream()))
|
try
|
||||||
{
|
{
|
||||||
return reader.ReadToEnd();
|
using (var reader = new StreamReader(fileInfo.CreateReadStream()))
|
||||||
|
{
|
||||||
|
return reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore any failures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
{
|
return null;
|
||||||
// Ignore any failures
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
||||||
isEnabledByDefault: true);
|
isEnabledByDefault: true);
|
||||||
|
|
||||||
var location = error.Location;
|
var location = error.Location;
|
||||||
if (location == SourceLocation.Undefined)
|
if (location.Equals(SourceLocation.Undefined))
|
||||||
{
|
{
|
||||||
location = SourceLocation.Zero;
|
location = SourceLocation.Zero;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -394,6 +394,22 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("RazorFileInfoCollection_ResourceCouldNotBeFound"), p0, p1);
|
return string.Format(CultureInfo.CurrentCulture, GetString("RazorFileInfoCollection_ResourceCouldNotBeFound"), p0, p1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generated Code
|
||||||
|
/// </summary>
|
||||||
|
internal static string GeneratedCodeFileName
|
||||||
|
{
|
||||||
|
get { return GetString("GeneratedCodeFileName"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generated Code
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatGeneratedCodeFileName()
|
||||||
|
{
|
||||||
|
return GetString("GeneratedCodeFileName");
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -189,4 +189,7 @@
|
||||||
<data name="RazorFileInfoCollection_ResourceCouldNotBeFound" xml:space="preserve">
|
<data name="RazorFileInfoCollection_ResourceCouldNotBeFound" xml:space="preserve">
|
||||||
<value>The resource '{0}' specified by '{1}' could not be found.</value>
|
<value>The resource '{0}' specified by '{1}' could not be found.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="GeneratedCodeFileName" xml:space="preserve">
|
||||||
|
<value>Generated Code</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -47,5 +47,26 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Contains($"/Views/ErrorPageMiddleware/{action}.cshtml", content);
|
Assert.Contains($"/Views/ErrorPageMiddleware/{action}.cshtml", content);
|
||||||
Assert.Contains(expected, content);
|
Assert.Contains(expected, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CompilationFailuresFromGlobalImportAreListed()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedMessage = "The type or namespace name 'NamespaceDoesNotExist' could not be found ("
|
||||||
|
+ "are you missing a using directive or an assembly reference?)";
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
var expectedMediaType = MediaTypeHeaderValue.Parse("text/html");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/ErrorFromGlobalImport");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
|
||||||
|
Assert.Equal(expectedMediaType, response.Content.Headers.ContentType);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Contains(@"Views\ErrorFromGlobalImport\_GlobalImport.cshtml", content);
|
||||||
|
Assert.Contains(expectedMessage, content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,33 +38,72 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
||||||
var codeTrees = utility.GetInheritedCodeTrees(@"Views\home\Index.cshtml");
|
var codeTrees = utility.GetInheritedCodeTrees(@"Views\home\Index.cshtml");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(2, codeTrees.Count);
|
Assert.Collection(codeTrees,
|
||||||
var viewStartChunks = codeTrees[0].Chunks;
|
codeTree =>
|
||||||
Assert.Equal(3, viewStartChunks.Count);
|
{
|
||||||
|
var globalImportPath = @"Views\home\_GlobalImport.cshtml";
|
||||||
|
Assert.Collection(codeTree.Chunks,
|
||||||
|
chunk =>
|
||||||
|
{
|
||||||
|
Assert.IsType<LiteralChunk>(chunk);
|
||||||
|
Assert.Equal(globalImportPath, chunk.Start.FilePath);
|
||||||
|
},
|
||||||
|
chunk =>
|
||||||
|
{
|
||||||
|
var usingChunk = Assert.IsType<UsingChunk>(chunk);
|
||||||
|
Assert.Equal("MyNamespace", usingChunk.Namespace);
|
||||||
|
Assert.Equal(globalImportPath, chunk.Start.FilePath);
|
||||||
|
},
|
||||||
|
chunk =>
|
||||||
|
{
|
||||||
|
Assert.IsType<LiteralChunk>(chunk);
|
||||||
|
Assert.Equal(globalImportPath, chunk.Start.FilePath);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
codeTree =>
|
||||||
|
{
|
||||||
|
var globalImportPath = @"Views\_GlobalImport.cshtml";
|
||||||
|
Assert.Collection(codeTree.Chunks,
|
||||||
|
chunk =>
|
||||||
|
{
|
||||||
|
Assert.IsType<LiteralChunk>(chunk);
|
||||||
|
Assert.Equal(globalImportPath, chunk.Start.FilePath);
|
||||||
|
},
|
||||||
|
chunk =>
|
||||||
|
{
|
||||||
|
var injectChunk = Assert.IsType<InjectChunk>(chunk);
|
||||||
|
Assert.Equal("MyHelper<TModel>", injectChunk.TypeName);
|
||||||
|
Assert.Equal("Helper", injectChunk.MemberName);
|
||||||
|
Assert.Equal(globalImportPath, chunk.Start.FilePath);
|
||||||
|
},
|
||||||
|
chunk =>
|
||||||
|
{
|
||||||
|
Assert.IsType<LiteralChunk>(chunk);
|
||||||
|
Assert.Equal(globalImportPath, chunk.Start.FilePath);
|
||||||
|
},
|
||||||
|
chunk =>
|
||||||
|
{
|
||||||
|
var setBaseTypeChunk = Assert.IsType<SetBaseTypeChunk>(chunk);
|
||||||
|
Assert.Equal("MyBaseType", setBaseTypeChunk.TypeName);
|
||||||
|
Assert.Equal(globalImportPath, chunk.Start.FilePath);
|
||||||
|
|
||||||
Assert.IsType<LiteralChunk>(viewStartChunks[0]);
|
},
|
||||||
var usingChunk = Assert.IsType<UsingChunk>(viewStartChunks[1]);
|
chunk =>
|
||||||
Assert.Equal("MyNamespace", usingChunk.Namespace);
|
{
|
||||||
Assert.IsType<LiteralChunk>(viewStartChunks[2]);
|
Assert.IsType<LiteralChunk>(chunk);
|
||||||
|
Assert.Equal(globalImportPath, chunk.Start.FilePath);
|
||||||
viewStartChunks = codeTrees[1].Chunks;
|
},
|
||||||
Assert.Equal(7, viewStartChunks.Count);
|
chunk =>
|
||||||
|
{
|
||||||
Assert.IsType<LiteralChunk>(viewStartChunks[0]);
|
Assert.IsType<StatementChunk>(chunk);
|
||||||
|
Assert.Equal(globalImportPath, chunk.Start.FilePath);
|
||||||
var injectChunk = Assert.IsType<InjectChunk>(viewStartChunks[1]);
|
},
|
||||||
Assert.Equal("MyHelper<TModel>", injectChunk.TypeName);
|
chunk =>
|
||||||
Assert.Equal("Helper", injectChunk.MemberName);
|
{
|
||||||
|
Assert.IsType<LiteralChunk>(chunk);
|
||||||
Assert.IsType<LiteralChunk>(viewStartChunks[2]);
|
Assert.Equal(globalImportPath, chunk.Start.FilePath);
|
||||||
|
});
|
||||||
var setBaseTypeChunk = Assert.IsType<SetBaseTypeChunk>(viewStartChunks[3]);
|
});
|
||||||
Assert.Equal("MyBaseType", setBaseTypeChunk.TypeName);
|
|
||||||
|
|
||||||
Assert.IsType<LiteralChunk>(viewStartChunks[4]);
|
|
||||||
|
|
||||||
Assert.IsType<StatementChunk>(viewStartChunks[5]);
|
|
||||||
Assert.IsType<LiteralChunk>(viewStartChunks[6]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
var actual = descriptors[i];
|
var actual = descriptors[i];
|
||||||
|
|
||||||
Assert.Equal(expected.DirectiveText, actual.DirectiveText, StringComparer.Ordinal);
|
Assert.Equal(expected.DirectiveText, actual.DirectiveText, StringComparer.Ordinal);
|
||||||
Assert.Equal(expected.Location, actual.Location);
|
Assert.Equal(SourceLocation.Zero, actual.Location);
|
||||||
Assert.Equal(expected.DirectiveType, actual.DirectiveType);
|
Assert.Equal(expected.DirectiveType, actual.DirectiveType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var compilationFailure = Mock.Of<ICompilationFailure>();
|
var compilationFailure = Mock.Of<ICompilationFailure>();
|
||||||
var result = CompilationResult.Failed(compilationFailure);
|
var failures = new[] { compilationFailure };
|
||||||
|
var result = CompilationResult.Failed(failures);
|
||||||
|
|
||||||
// Act and Assert
|
// Act and Assert
|
||||||
Assert.Null(result.CompiledType);
|
Assert.Null(result.CompiledType);
|
||||||
Assert.Same(compilationFailure, result.CompilationFailure);
|
Assert.Same(failures, result.CompilationFailures);
|
||||||
var exception = Assert.Throws<CompilationFailedException>(() => result.EnsureSuccessful());
|
var exception = Assert.Throws<CompilationFailedException>(() => result.EnsureSuccessful());
|
||||||
Assert.Collection(exception.CompilationFailures,
|
var failure = Assert.Single(exception.CompilationFailures);
|
||||||
failure => Assert.Same(compilationFailure, failure));
|
Assert.Same(compilationFailure, failure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ using Microsoft.AspNet.Razor;
|
||||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||||
using Microsoft.AspNet.Razor.TagHelpers;
|
using Microsoft.AspNet.Razor.TagHelpers;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -36,7 +37,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
compiler.Setup(c => c.Compile(relativeFileInfo, It.IsAny<string>()))
|
compiler.Setup(c => c.Compile(relativeFileInfo, It.IsAny<string>()))
|
||||||
.Returns(CompilationResult.Successful(typeof(RazorCompilationServiceTest)));
|
.Returns(CompilationResult.Successful(typeof(RazorCompilationServiceTest)));
|
||||||
|
|
||||||
var razorService = new RazorCompilationService(compiler.Object, host.Object);
|
var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
razorService.Compile(relativeFileInfo);
|
razorService.Compile(relativeFileInfo);
|
||||||
|
|
@ -68,15 +69,19 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
|
|
||||||
var compiler = new Mock<ICompilationService>(MockBehavior.Strict);
|
var compiler = new Mock<ICompilationService>(MockBehavior.Strict);
|
||||||
var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml");
|
var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml");
|
||||||
var razorService = new RazorCompilationService(compiler.Object, host.Object);
|
var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = razorService.Compile(relativeFileInfo);
|
var result = razorService.Compile(relativeFileInfo);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(result.CompilationFailure);
|
Assert.NotNull(result.CompilationFailures);
|
||||||
var message = Assert.Single(result.CompilationFailure.Messages);
|
Assert.Collection(result.CompilationFailures,
|
||||||
Assert.Equal("some message", message.Message);
|
failure =>
|
||||||
|
{
|
||||||
|
var message = Assert.Single(failure.Messages);
|
||||||
|
Assert.Equal("some message", message.Message);
|
||||||
|
});
|
||||||
host.Verify();
|
host.Verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,7 +110,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
compiler.Setup(c => c.Compile(relativeFileInfo, code))
|
compiler.Setup(c => c.Compile(relativeFileInfo, code))
|
||||||
.Returns(compilationResult)
|
.Returns(compilationResult)
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var razorService = new RazorCompilationService(compiler.Object, host.Object);
|
var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = razorService.Compile(relativeFileInfo);
|
var result = razorService.Compile(relativeFileInfo);
|
||||||
|
|
@ -115,6 +120,86 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
compiler.Verify();
|
compiler.Verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetCompilationFailedResult_ReturnsCompilationResult_WithGroupedMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var viewPath = @"views/index.razor";
|
||||||
|
var globalImportPath = @"views/global.import.cshtml";
|
||||||
|
var host = Mock.Of<IMvcRazorHost>();
|
||||||
|
|
||||||
|
var fileProvider = new TestFileProvider();
|
||||||
|
var file = fileProvider.AddFile(viewPath, "View Content");
|
||||||
|
fileProvider.AddFile(globalImportPath, "Global Import Content");
|
||||||
|
var relativeFileInfo = new RelativeFileInfo(file, viewPath);
|
||||||
|
var razorService = new RazorCompilationService(
|
||||||
|
Mock.Of<ICompilationService>(),
|
||||||
|
Mock.Of<IMvcRazorHost>(),
|
||||||
|
GetOptions(fileProvider));
|
||||||
|
var errors = new[]
|
||||||
|
{
|
||||||
|
new RazorError("message-1", new SourceLocation(1, 2, 17)),
|
||||||
|
new RazorError("message-2", new SourceLocation(viewPath, 1, 4, 6), 7),
|
||||||
|
new RazorError { Message = "message-3" },
|
||||||
|
new RazorError("message-4", new SourceLocation(globalImportPath, 1, 3, 8), 4),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = razorService.GetCompilationFailedResult(relativeFileInfo, errors);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result.CompilationFailures);
|
||||||
|
Assert.Collection(result.CompilationFailures,
|
||||||
|
failure =>
|
||||||
|
{
|
||||||
|
Assert.Equal(viewPath, failure.SourceFilePath);
|
||||||
|
Assert.Equal("View Content", failure.SourceFileContent);
|
||||||
|
Assert.Collection(failure.Messages,
|
||||||
|
message =>
|
||||||
|
{
|
||||||
|
Assert.Equal(errors[0].Message, message.Message);
|
||||||
|
Assert.Equal(viewPath, message.SourceFilePath);
|
||||||
|
Assert.Equal(3, message.StartLine);
|
||||||
|
Assert.Equal(17, message.StartColumn);
|
||||||
|
Assert.Equal(3, message.EndLine);
|
||||||
|
Assert.Equal(18, message.EndColumn);
|
||||||
|
},
|
||||||
|
message =>
|
||||||
|
{
|
||||||
|
Assert.Equal(errors[1].Message, message.Message);
|
||||||
|
Assert.Equal(viewPath, message.SourceFilePath);
|
||||||
|
Assert.Equal(5, message.StartLine);
|
||||||
|
Assert.Equal(6, message.StartColumn);
|
||||||
|
Assert.Equal(5, message.EndLine);
|
||||||
|
Assert.Equal(13, message.EndColumn);
|
||||||
|
},
|
||||||
|
message =>
|
||||||
|
{
|
||||||
|
Assert.Equal(errors[2].Message, message.Message);
|
||||||
|
Assert.Equal(viewPath, message.SourceFilePath);
|
||||||
|
Assert.Equal(0, message.StartLine);
|
||||||
|
Assert.Equal(-1, message.StartColumn);
|
||||||
|
Assert.Equal(0, message.EndLine);
|
||||||
|
Assert.Equal(0, message.EndColumn);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
failure =>
|
||||||
|
{
|
||||||
|
Assert.Equal(globalImportPath, failure.SourceFilePath);
|
||||||
|
Assert.Equal("Global Import Content", failure.SourceFileContent);
|
||||||
|
Assert.Collection(failure.Messages,
|
||||||
|
message =>
|
||||||
|
{
|
||||||
|
Assert.Equal(errors[3].Message, message.Message);
|
||||||
|
Assert.Equal(globalImportPath, message.SourceFilePath);
|
||||||
|
Assert.Equal(4, message.StartLine);
|
||||||
|
Assert.Equal(8, message.StartColumn);
|
||||||
|
Assert.Equal(4, message.EndLine);
|
||||||
|
Assert.Equal(12, message.EndColumn);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static GeneratorResults GetGeneratorResult()
|
private static GeneratorResults GetGeneratorResult()
|
||||||
{
|
{
|
||||||
return new GeneratorResults(
|
return new GeneratorResults(
|
||||||
|
|
@ -124,5 +209,18 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
new CodeBuilderResult("", new LineMapping[0]),
|
new CodeBuilderResult("", new LineMapping[0]),
|
||||||
new CodeTree());
|
new CodeTree());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IOptions<RazorViewEngineOptions> GetOptions(IFileProvider fileProvider = null)
|
||||||
|
{
|
||||||
|
var razorViewEngineOptions = new RazorViewEngineOptions
|
||||||
|
{
|
||||||
|
FileProvider = fileProvider ?? new TestFileProvider()
|
||||||
|
};
|
||||||
|
var options = new Mock<IOptions<RazorViewEngineOptions>>();
|
||||||
|
options.SetupGet(o => o.Options)
|
||||||
|
.Returns(razorViewEngineOptions);
|
||||||
|
|
||||||
|
return options.Object;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using Microsoft.AspNet.FileProviders;
|
using Microsoft.AspNet.FileProviders;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Microsoft.Framework.Runtime;
|
using Microsoft.Framework.Runtime;
|
||||||
using Microsoft.Framework.Runtime.Compilation;
|
using Microsoft.Framework.Runtime.Compilation;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -38,7 +41,8 @@ public class MyTestType {}";
|
||||||
accessor,
|
accessor,
|
||||||
libraryManager,
|
libraryManager,
|
||||||
compilerOptionsProvider.Object,
|
compilerOptionsProvider.Object,
|
||||||
mvcRazorHost.Object);
|
mvcRazorHost.Object,
|
||||||
|
GetOptions());
|
||||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
||||||
"some-relative-path");
|
"some-relative-path");
|
||||||
|
|
||||||
|
|
@ -52,11 +56,14 @@ public class MyTestType {}";
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Compile_ReturnsCompilationFailureWithRelativePath()
|
public void Compile_ReturnsCompilationFailureWithPathsFromLinePragmas()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var viewPath = "some-relative-path";
|
||||||
var fileContent = "test file content";
|
var fileContent = "test file content";
|
||||||
var content = @"this should fail";
|
var content = $@"
|
||||||
|
#line 1 ""{viewPath}""
|
||||||
|
this should fail";
|
||||||
var applicationEnvironment = GetApplicationEnvironment();
|
var applicationEnvironment = GetApplicationEnvironment();
|
||||||
var accessor = GetLoadContextAccessor();
|
var accessor = GetLoadContextAccessor();
|
||||||
var libraryManager = GetLibraryManager();
|
var libraryManager = GetLibraryManager();
|
||||||
|
|
@ -67,17 +74,15 @@ public class MyTestType {}";
|
||||||
applicationEnvironment.Configuration))
|
applicationEnvironment.Configuration))
|
||||||
.Returns(new CompilerOptions());
|
.Returns(new CompilerOptions());
|
||||||
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
||||||
|
var fileProvider = new TestFileProvider();
|
||||||
|
var fileInfo = fileProvider.AddFile(viewPath, fileContent);
|
||||||
|
|
||||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
||||||
accessor,
|
accessor,
|
||||||
libraryManager,
|
libraryManager,
|
||||||
compilerOptionsProvider.Object,
|
compilerOptionsProvider.Object,
|
||||||
mvcRazorHost);
|
mvcRazorHost,
|
||||||
var fileInfo = new TestFileInfo
|
GetOptions(fileProvider));
|
||||||
{
|
|
||||||
Content = fileContent,
|
|
||||||
PhysicalPath = "physical path"
|
|
||||||
};
|
|
||||||
var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");
|
var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -86,12 +91,13 @@ public class MyTestType {}";
|
||||||
// Assert
|
// Assert
|
||||||
Assert.IsType<CompilationResult>(result);
|
Assert.IsType<CompilationResult>(result);
|
||||||
Assert.Null(result.CompiledType);
|
Assert.Null(result.CompiledType);
|
||||||
Assert.Equal(relativeFileInfo.RelativePath, result.CompilationFailure.SourceFilePath);
|
var compilationFailure = Assert.Single(result.CompilationFailures);
|
||||||
Assert.Equal(fileContent, result.CompilationFailure.SourceFileContent);
|
Assert.Equal(relativeFileInfo.RelativePath, compilationFailure.SourceFilePath);
|
||||||
|
Assert.Equal(fileContent, compilationFailure.SourceFileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Compile_ReturnsApplicationRelativePath_IfPhyicalPathIsNotSpecified()
|
public void Compile_ReturnsGeneratedCodePath_IfLinePragmaIsNotAvailable()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var fileContent = "file content";
|
var fileContent = "file content";
|
||||||
|
|
@ -111,7 +117,8 @@ public class MyTestType {}";
|
||||||
accessor,
|
accessor,
|
||||||
libraryManager,
|
libraryManager,
|
||||||
compilerOptionsProvider.Object,
|
compilerOptionsProvider.Object,
|
||||||
mvcRazorHost);
|
mvcRazorHost,
|
||||||
|
GetOptions());
|
||||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { Content = fileContent },
|
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { Content = fileContent },
|
||||||
"some-relative-path");
|
"some-relative-path");
|
||||||
|
|
||||||
|
|
@ -121,15 +128,20 @@ public class MyTestType {}";
|
||||||
// Assert
|
// Assert
|
||||||
Assert.IsType<CompilationResult>(result);
|
Assert.IsType<CompilationResult>(result);
|
||||||
Assert.Null(result.CompiledType);
|
Assert.Null(result.CompiledType);
|
||||||
Assert.Equal("some-relative-path", result.CompilationFailure.SourceFilePath);
|
|
||||||
Assert.Equal(fileContent, result.CompilationFailure.SourceFileContent);
|
var compilationFailure = Assert.Single(result.CompilationFailures);
|
||||||
|
Assert.Equal("Generated Code", compilationFailure.SourceFilePath);
|
||||||
|
Assert.Equal(content, compilationFailure.SourceFileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Compile_DoesNotThrow_IfFileCannotBeRead()
|
public void Compile_DoesNotThrow_IfFileCannotBeRead()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = @"this should fail";
|
var path = "some-relative-path";
|
||||||
|
var content = $@"
|
||||||
|
#line 1 ""{path}""
|
||||||
|
this should fail";
|
||||||
var applicationEnvironment = GetApplicationEnvironment();
|
var applicationEnvironment = GetApplicationEnvironment();
|
||||||
var accessor = GetLoadContextAccessor();
|
var accessor = GetLoadContextAccessor();
|
||||||
var libraryManager = GetLibraryManager();
|
var libraryManager = GetLibraryManager();
|
||||||
|
|
@ -141,15 +153,20 @@ public class MyTestType {}";
|
||||||
.Returns(new CompilerOptions());
|
.Returns(new CompilerOptions());
|
||||||
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
||||||
|
|
||||||
|
var mockFileInfo = new Mock<IFileInfo>();
|
||||||
|
mockFileInfo.Setup(f => f.CreateReadStream())
|
||||||
|
.Throws(new Exception());
|
||||||
|
var fileProvider = new TestFileProvider();
|
||||||
|
fileProvider.AddFile(path, mockFileInfo.Object);
|
||||||
|
|
||||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
||||||
accessor,
|
accessor,
|
||||||
libraryManager,
|
libraryManager,
|
||||||
compilerOptionsProvider.Object,
|
compilerOptionsProvider.Object,
|
||||||
mvcRazorHost);
|
mvcRazorHost,
|
||||||
var mockFileInfo = new Mock<IFileInfo>();
|
GetOptions(fileProvider));
|
||||||
mockFileInfo.Setup(f => f.CreateReadStream())
|
|
||||||
.Throws(new Exception());
|
var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path);
|
||||||
var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, "some-relative-path");
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = compilationService.Compile(relativeFileInfo, content);
|
var result = compilationService.Compile(relativeFileInfo, content);
|
||||||
|
|
@ -157,8 +174,9 @@ public class MyTestType {}";
|
||||||
// Assert
|
// Assert
|
||||||
Assert.IsType<CompilationResult>(result);
|
Assert.IsType<CompilationResult>(result);
|
||||||
Assert.Null(result.CompiledType);
|
Assert.Null(result.CompiledType);
|
||||||
Assert.Equal("some-relative-path", result.CompilationFailure.SourceFilePath);
|
var compilationFailure = Assert.Single(result.CompilationFailures);
|
||||||
Assert.Null(result.CompilationFailure.SourceFileContent);
|
Assert.Equal(path, compilationFailure.SourceFilePath);
|
||||||
|
Assert.Null(compilationFailure.SourceFileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -189,7 +207,8 @@ public class MyNonCustomDefinedClass {}
|
||||||
accessor,
|
accessor,
|
||||||
libraryManager,
|
libraryManager,
|
||||||
compilerOptionsProvider.Object,
|
compilerOptionsProvider.Object,
|
||||||
mvcRazorHost.Object);
|
mvcRazorHost.Object,
|
||||||
|
GetOptions());
|
||||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
||||||
"some-relative-path");
|
"some-relative-path");
|
||||||
|
|
||||||
|
|
@ -225,7 +244,8 @@ public class NotRazorPrefixType {}";
|
||||||
accessor,
|
accessor,
|
||||||
libraryManager,
|
libraryManager,
|
||||||
compilerOptionsProvider.Object,
|
compilerOptionsProvider.Object,
|
||||||
mvcRazorHost.Object);
|
mvcRazorHost.Object,
|
||||||
|
GetOptions());
|
||||||
|
|
||||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
||||||
"some-relative-path");
|
"some-relative-path");
|
||||||
|
|
@ -238,6 +258,113 @@ public class NotRazorPrefixType {}";
|
||||||
Assert.Equal("RazorPrefixType", result.CompiledType.Name);
|
Assert.Equal("RazorPrefixType", result.CompiledType.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetCompilationFailedResult_ReturnsCompilationResult_WithGroupedMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var viewPath = "Views/Home/Index";
|
||||||
|
var generatedCodeFileName = "Generated Code";
|
||||||
|
var fileProvider = new TestFileProvider();
|
||||||
|
fileProvider.AddFile(viewPath, "view-content");
|
||||||
|
var options = new Mock<IOptions<RazorViewEngineOptions>>();
|
||||||
|
options.SetupGet(o => o.Options)
|
||||||
|
.Returns(new RazorViewEngineOptions
|
||||||
|
{
|
||||||
|
FileProvider = fileProvider
|
||||||
|
});
|
||||||
|
var compilationService = new RoslynCompilationService(
|
||||||
|
GetApplicationEnvironment(),
|
||||||
|
GetLoadContextAccessor(),
|
||||||
|
GetLibraryManager(),
|
||||||
|
Mock.Of<ICompilerOptionsProvider>(),
|
||||||
|
Mock.Of<IMvcRazorHost>(),
|
||||||
|
options.Object);
|
||||||
|
|
||||||
|
var assemblyName = "random-assembly-name";
|
||||||
|
|
||||||
|
var diagnostics = new[]
|
||||||
|
{
|
||||||
|
Diagnostic.Create(
|
||||||
|
GetDiagnosticDescriptor("message-1"),
|
||||||
|
Location.Create(
|
||||||
|
viewPath,
|
||||||
|
new TextSpan(10, 5),
|
||||||
|
new LinePositionSpan(new LinePosition(10, 1), new LinePosition(10, 2)))),
|
||||||
|
Diagnostic.Create(
|
||||||
|
GetDiagnosticDescriptor("message-2"),
|
||||||
|
Location.Create(
|
||||||
|
assemblyName,
|
||||||
|
new TextSpan(1, 6),
|
||||||
|
new LinePositionSpan(new LinePosition(1, 2), new LinePosition(3, 4)))),
|
||||||
|
Diagnostic.Create(
|
||||||
|
GetDiagnosticDescriptor("message-3"),
|
||||||
|
Location.Create(
|
||||||
|
viewPath,
|
||||||
|
new TextSpan(40, 50),
|
||||||
|
new LinePositionSpan(new LinePosition(30, 5), new LinePosition(40, 12)))),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var compilationResult = compilationService.GetCompilationFailedResult(
|
||||||
|
viewPath,
|
||||||
|
"compilation-content",
|
||||||
|
assemblyName,
|
||||||
|
diagnostics);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(compilationResult.CompilationFailures,
|
||||||
|
failure =>
|
||||||
|
{
|
||||||
|
Assert.Equal(viewPath, failure.SourceFilePath);
|
||||||
|
Assert.Equal("view-content", failure.SourceFileContent);
|
||||||
|
Assert.Collection(failure.Messages,
|
||||||
|
message =>
|
||||||
|
{
|
||||||
|
Assert.Equal("message-1", message.Message);
|
||||||
|
Assert.Equal(viewPath, message.SourceFilePath);
|
||||||
|
Assert.Equal(11, message.StartLine);
|
||||||
|
Assert.Equal(2, message.StartColumn);
|
||||||
|
Assert.Equal(11, message.EndLine);
|
||||||
|
Assert.Equal(3, message.EndColumn);
|
||||||
|
},
|
||||||
|
message =>
|
||||||
|
{
|
||||||
|
Assert.Equal("message-3", message.Message);
|
||||||
|
Assert.Equal(viewPath, message.SourceFilePath);
|
||||||
|
Assert.Equal(31, message.StartLine);
|
||||||
|
Assert.Equal(6, message.StartColumn);
|
||||||
|
Assert.Equal(41, message.EndLine);
|
||||||
|
Assert.Equal(13, message.EndColumn);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
failure =>
|
||||||
|
{
|
||||||
|
Assert.Equal(generatedCodeFileName, failure.SourceFilePath);
|
||||||
|
Assert.Equal("compilation-content", failure.SourceFileContent);
|
||||||
|
Assert.Collection(failure.Messages,
|
||||||
|
message =>
|
||||||
|
{
|
||||||
|
Assert.Equal("message-2", message.Message);
|
||||||
|
Assert.Equal(assemblyName, message.SourceFilePath);
|
||||||
|
Assert.Equal(2, message.StartLine);
|
||||||
|
Assert.Equal(3, message.StartColumn);
|
||||||
|
Assert.Equal(4, message.EndLine);
|
||||||
|
Assert.Equal(5, message.EndColumn);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DiagnosticDescriptor GetDiagnosticDescriptor(string messageFormat)
|
||||||
|
{
|
||||||
|
return new DiagnosticDescriptor(
|
||||||
|
id: "someid",
|
||||||
|
title: "sometitle",
|
||||||
|
messageFormat: messageFormat,
|
||||||
|
category: "some-category",
|
||||||
|
defaultSeverity: DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true);
|
||||||
|
}
|
||||||
|
|
||||||
private static ILibraryManager GetLibraryManager()
|
private static ILibraryManager GetLibraryManager()
|
||||||
{
|
{
|
||||||
var fileReference = new Mock<IMetadataFileReference>();
|
var fileReference = new Mock<IMetadataFileReference>();
|
||||||
|
|
@ -285,5 +412,18 @@ public class NotRazorPrefixType {}";
|
||||||
|
|
||||||
return applicationEnvironment.Object;
|
return applicationEnvironment.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IOptions<RazorViewEngineOptions> GetOptions(IFileProvider fileProvider = null)
|
||||||
|
{
|
||||||
|
var razorViewEngineOptions = new RazorViewEngineOptions
|
||||||
|
{
|
||||||
|
FileProvider = fileProvider ?? new TestFileProvider()
|
||||||
|
};
|
||||||
|
var options = new Mock<IOptions<RazorViewEngineOptions>>();
|
||||||
|
options.SetupGet(o => o.Options)
|
||||||
|
.Returns(razorViewEngineOptions);
|
||||||
|
|
||||||
|
return options.Object;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddFile(string path, string contents)
|
public TestFileInfo AddFile(string path, string contents)
|
||||||
{
|
{
|
||||||
var fileInfo = new TestFileInfo
|
var fileInfo = new TestFileInfo
|
||||||
{
|
{
|
||||||
|
|
@ -32,6 +32,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
};
|
};
|
||||||
|
|
||||||
AddFile(path, fileInfo);
|
AddFile(path, fileInfo);
|
||||||
|
|
||||||
|
return fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddFile(string path, IFileInfo contents)
|
public void AddFile(string path, IFileInfo contents)
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,11 @@ namespace ErrorPageMiddlewareWebSite
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("/ErrorFromGlobalImport")]
|
||||||
|
public IActionResult GlobalImportError()
|
||||||
|
{
|
||||||
|
return View("~/Views/ErrorFromGlobalImport/Index");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<h1>Hello world!</h1>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
@using NamespaceDoesNotExist
|
||||||
Loading…
Reference in New Issue