diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs
index d278980cd9..edee56406b 100644
--- a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs
@@ -126,22 +126,33 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
private static CodeTree ParseViewFile(RazorTemplateEngine engine,
IFileInfo fileInfo,
- string viewStartPath)
+ string globalImportPath)
{
using (var stream = fileInfo.CreateReadStream())
{
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 language = engine.Host.CodeLanguage;
var codeGenerator = language.CreateCodeGenerator(className,
engine.Host.DefaultNamespace,
- viewStartPath,
+ globalImportPath,
engine.Host);
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;
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorParser.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorParser.cs
index dd7ecf656b..f30e5bcbee 100644
--- a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorParser.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorParser.cs
@@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
var descriptor = new TagHelperDirectiveDescriptor(
addTagHelperChunk.LookupText,
- SourceLocation.Undefined,
+ chunk.Start,
TagHelperDirectiveType.AddTagHelper);
descriptors.Add(descriptor);
@@ -89,7 +89,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
var descriptor = new TagHelperDirectiveDescriptor(
removeTagHelperChunk.LookupText,
- SourceLocation.Undefined,
+ chunk.Start,
TagHelperDirectiveType.RemoveTagHelper);
descriptors.Add(descriptor);
@@ -102,7 +102,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
var descriptor = new TagHelperDirectiveDescriptor(
tagHelperPrefixDirectiveChunk.Prefix,
- SourceLocation.Undefined,
+ chunk.Start,
TagHelperDirectiveType.TagHelperPrefix);
descriptors.Add(descriptor);
diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationFailedException.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationFailedException.cs
index 96d697ce33..3e39805e2f 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationFailedException.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationFailedException.cs
@@ -17,24 +17,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
///
/// Instantiates a new instance of .
///
- /// The instance containing
+ /// s containing
/// details of the compilation failure.
public CompilationFailedException(
- [NotNull] ICompilationFailure compilationFailure)
- : base(FormatMessage(compilationFailure))
+ [NotNull] IEnumerable compilationFailures)
+ : base(FormatMessage(compilationFailures))
{
- CompilationFailures = new[] { compilationFailure };
+ CompilationFailures = compilationFailures;
}
///
public IEnumerable CompilationFailures { get; }
- private static string FormatMessage(ICompilationFailure compilationFailure)
+ private static string FormatMessage(IEnumerable compilationFailures)
{
return Resources.CompilationFailed + Environment.NewLine +
string.Join(
Environment.NewLine,
- compilationFailure.Messages.Select(message => message.FormattedMessage));
+ compilationFailures.SelectMany(f => f.Messages).Select(message => message.FormattedMessage));
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationResult.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationResult.cs
index a55a32fa63..58457ce423 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationResult.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationResult.cs
@@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using Microsoft.Framework.Runtime;
+using System.Collections.Generic;
+using System.IO;
using Microsoft.Framework.Internal;
+using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Mvc.Razor.Compilation
{
@@ -31,10 +33,11 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
public string CompiledContent { get; protected set; }
///
- /// Gets the produced from parsing or compiling the Razor file.
+ /// Gets the s produced from parsing or compiling the Razor file.
///
- /// This property is null when compilation succeeded.
- public ICompilationFailure CompilationFailure { get; private set; }
+ /// This property is null when compilation succeeded. An empty sequence
+ /// indicates a failed compilation.
+ public IEnumerable CompilationFailures { get; private set; }
///
/// Gets the .
@@ -43,9 +46,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
/// Thrown if compilation failed.
public CompilationResult EnsureSuccessful()
{
- if (CompilationFailure != null)
+ if (CompilationFailures != null)
{
- throw new CompilationFailedException(CompilationFailure);
+ throw new CompilationFailedException(CompilationFailures);
}
return this;
@@ -54,14 +57,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
///
/// Creates a for a failed compilation.
///
- /// The produced from parsing or
+ /// s produced from parsing or
/// compiling the Razor file.
/// A instance for a failed compilation.
- public static CompilationResult Failed([NotNull] ICompilationFailure compilationFailure)
+ public static CompilationResult Failed([NotNull] IEnumerable compilationFailures)
{
return new CompilationResult
{
- CompilationFailure = compilationFailure
+ CompilationFailures = compilationFailures
};
}
diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RazorCompilationService.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RazorCompilationService.cs
index 20493ddc90..74a6357608 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RazorCompilationService.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RazorCompilationService.cs
@@ -1,11 +1,14 @@
// 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;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Razor;
using Microsoft.Framework.Internal;
+using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Mvc.Razor.Compilation
{
@@ -16,12 +19,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
{
private readonly ICompilationService _compilationService;
private readonly IMvcRazorHost _razorHost;
+ private readonly IFileProvider _fileProvider;
public RazorCompilationService(ICompilationService compilationService,
- IMvcRazorHost razorHost)
+ IMvcRazorHost razorHost,
+ IOptions viewEngineOptions)
{
_compilationService = compilationService;
_razorHost = razorHost;
+ _fileProvider = viewEngineOptions.Options.FileProvider;
}
///
@@ -30,39 +36,61 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
GeneratorResults results;
using (var inputStream = file.FileInfo.CreateReadStream())
{
- results = _razorHost.GenerateCode(
- file.RelativePath, inputStream);
+ results = _razorHost.GenerateCode(file.RelativePath, inputStream);
}
if (!results.Success)
{
- var messages = results.ParserErrors
- .Select(parseError => new RazorCompilationMessage(parseError, file.RelativePath));
- var failure = new RazorCompilationFailure(
- file.RelativePath,
- ReadFileContentsSafely(file.FileInfo),
- messages);
-
- return CompilationResult.Failed(failure);
+ return GetCompilationFailedResult(file, results.ParserErrors);
}
return _compilationService.Compile(file, results.GeneratedCode);
}
- private static string ReadFileContentsSafely(IFileInfo fileInfo)
+ // Internal for unit testing
+ internal CompilationResult GetCompilationFailedResult(RelativeFileInfo file, IEnumerable 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();
+ 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
- {
- // Ignore any failures
- return null;
- }
+
+ return null;
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs
index b17e26b747..e08d4f47e7 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs
@@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.Framework.Internal;
+using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.Runtime.Compilation;
using Microsoft.Framework.Runtime.Roslyn;
@@ -34,9 +35,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
private readonly IApplicationEnvironment _environment;
private readonly IAssemblyLoadContext _loader;
private readonly ICompilerOptionsProvider _compilerOptionsProvider;
-
+ private readonly IFileProvider _fileProvider;
private readonly Lazy> _applicationReferences;
-
private readonly string _classPrefix;
///
@@ -50,30 +50,28 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
IAssemblyLoadContextAccessor loaderAccessor,
ILibraryManager libraryManager,
ICompilerOptionsProvider compilerOptionsProvider,
- IMvcRazorHost host)
+ IMvcRazorHost host,
+ IOptions optionsAccessor)
{
_environment = environment;
_loader = loaderAccessor.GetLoadContext(typeof(RoslynCompilationService).GetTypeInfo().Assembly);
_libraryManager = libraryManager;
_applicationReferences = new Lazy>(GetApplicationReferences);
_compilerOptionsProvider = compilerOptionsProvider;
+ _fileProvider = optionsAccessor.Options.FileProvider;
_classPrefix = host.MainClassNamePrefix;
}
///
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.FileInfo.PhysicalPath ?? fileInfo.RelativePath;
+ var assemblyName = Path.GetRandomFileName();
var compilationSettings = _compilerOptionsProvider.GetCompilationSettings(_environment);
var syntaxTree = SyntaxTreeGenerator.Generate(compilationContent,
- path,
+ assemblyName,
compilationSettings);
var references = _applicationReferences.Value;
- var assemblyName = Path.GetRandomFileName();
var compilationOptions = compilationSettings.CompilationOptions
.WithOutputKind(OutputKind.DynamicallyLinkedLibrary);
@@ -99,15 +97,11 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
if (!result.Success)
{
- var failures = result.Diagnostics.Where(IsError);
- var compilationFailure = new RoslynCompilationFailure(failures)
- {
- CompiledContent = compilationContent,
- SourceFileContent = ReadFileContentsSafely(fileInfo.FileInfo),
- SourceFilePath = fileInfo.RelativePath
- };
-
- return CompilationResult.Failed(compilationFailure);
+ return GetCompilationFailedResult(
+ fileInfo.RelativePath,
+ compilationContent,
+ assemblyName,
+ result.Diagnostics);
}
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 diagnostics)
+ {
+ var diagnosticGroups = diagnostics
+ .Where(IsError)
+ .GroupBy(diagnostic => GetFilePath(relativePath, diagnostic), StringComparer.Ordinal);
+
+ var failures = new List();
+ 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 GetApplicationReferences()
{
var references = new List();
@@ -222,20 +266,25 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
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
- {
- // Ignore any failures
- return null;
- }
+
+ return null;
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorErrorExtensions.cs b/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorErrorExtensions.cs
index abbd19b039..a08c5b916f 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorErrorExtensions.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorErrorExtensions.cs
@@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
isEnabledByDefault: true);
var location = error.Location;
- if (location == SourceLocation.Undefined)
+ if (location.Equals(SourceLocation.Undefined))
{
location = SourceLocation.Zero;
}
diff --git a/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs
index 5236fb203f..e57706a956 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs
@@ -394,6 +394,22 @@ namespace Microsoft.AspNet.Mvc.Razor
return string.Format(CultureInfo.CurrentCulture, GetString("RazorFileInfoCollection_ResourceCouldNotBeFound"), p0, p1);
}
+ ///
+ /// Generated Code
+ ///
+ internal static string GeneratedCodeFileName
+ {
+ get { return GetString("GeneratedCodeFileName"); }
+ }
+
+ ///
+ /// Generated Code
+ ///
+ internal static string FormatGeneratedCodeFileName()
+ {
+ return GetString("GeneratedCodeFileName");
+ }
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.AspNet.Mvc.Razor/Resources.resx b/src/Microsoft.AspNet.Mvc.Razor/Resources.resx
index 0d39f11535..c564e69697 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/Resources.resx
+++ b/src/Microsoft.AspNet.Mvc.Razor/Resources.resx
@@ -189,4 +189,7 @@
The resource '{0}' specified by '{1}' could not be found.
+
+ Generated Code
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ErrorPageTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ErrorPageTests.cs
index 258cc6469c..d18e826963 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ErrorPageTests.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ErrorPageTests.cs
@@ -47,5 +47,26 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Contains($"/Views/ErrorPageMiddleware/{action}.cshtml", 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);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs
index e6bfbd3478..e31676f283 100644
--- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs
@@ -38,33 +38,72 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
var codeTrees = utility.GetInheritedCodeTrees(@"Views\home\Index.cshtml");
// Assert
- Assert.Equal(2, codeTrees.Count);
- var viewStartChunks = codeTrees[0].Chunks;
- Assert.Equal(3, viewStartChunks.Count);
+ Assert.Collection(codeTrees,
+ codeTree =>
+ {
+ var globalImportPath = @"Views\home\_GlobalImport.cshtml";
+ Assert.Collection(codeTree.Chunks,
+ chunk =>
+ {
+ Assert.IsType(chunk);
+ Assert.Equal(globalImportPath, chunk.Start.FilePath);
+ },
+ chunk =>
+ {
+ var usingChunk = Assert.IsType(chunk);
+ Assert.Equal("MyNamespace", usingChunk.Namespace);
+ Assert.Equal(globalImportPath, chunk.Start.FilePath);
+ },
+ chunk =>
+ {
+ Assert.IsType(chunk);
+ Assert.Equal(globalImportPath, chunk.Start.FilePath);
+ });
+ },
+ codeTree =>
+ {
+ var globalImportPath = @"Views\_GlobalImport.cshtml";
+ Assert.Collection(codeTree.Chunks,
+ chunk =>
+ {
+ Assert.IsType(chunk);
+ Assert.Equal(globalImportPath, chunk.Start.FilePath);
+ },
+ chunk =>
+ {
+ var injectChunk = Assert.IsType(chunk);
+ Assert.Equal("MyHelper", injectChunk.TypeName);
+ Assert.Equal("Helper", injectChunk.MemberName);
+ Assert.Equal(globalImportPath, chunk.Start.FilePath);
+ },
+ chunk =>
+ {
+ Assert.IsType(chunk);
+ Assert.Equal(globalImportPath, chunk.Start.FilePath);
+ },
+ chunk =>
+ {
+ var setBaseTypeChunk = Assert.IsType(chunk);
+ Assert.Equal("MyBaseType", setBaseTypeChunk.TypeName);
+ Assert.Equal(globalImportPath, chunk.Start.FilePath);
- Assert.IsType(viewStartChunks[0]);
- var usingChunk = Assert.IsType(viewStartChunks[1]);
- Assert.Equal("MyNamespace", usingChunk.Namespace);
- Assert.IsType(viewStartChunks[2]);
-
- viewStartChunks = codeTrees[1].Chunks;
- Assert.Equal(7, viewStartChunks.Count);
-
- Assert.IsType(viewStartChunks[0]);
-
- var injectChunk = Assert.IsType(viewStartChunks[1]);
- Assert.Equal("MyHelper", injectChunk.TypeName);
- Assert.Equal("Helper", injectChunk.MemberName);
-
- Assert.IsType(viewStartChunks[2]);
-
- var setBaseTypeChunk = Assert.IsType(viewStartChunks[3]);
- Assert.Equal("MyBaseType", setBaseTypeChunk.TypeName);
-
- Assert.IsType(viewStartChunks[4]);
-
- Assert.IsType(viewStartChunks[5]);
- Assert.IsType(viewStartChunks[6]);
+ },
+ chunk =>
+ {
+ Assert.IsType(chunk);
+ Assert.Equal(globalImportPath, chunk.Start.FilePath);
+ },
+ chunk =>
+ {
+ Assert.IsType(chunk);
+ Assert.Equal(globalImportPath, chunk.Start.FilePath);
+ },
+ chunk =>
+ {
+ Assert.IsType(chunk);
+ Assert.Equal(globalImportPath, chunk.Start.FilePath);
+ });
+ });
}
[Fact]
diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorParserTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorParserTest.cs
index 19e76f1aa1..45b36b46c9 100644
--- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorParserTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorParserTest.cs
@@ -172,7 +172,7 @@ namespace Microsoft.AspNet.Mvc.Razor
var actual = descriptors[i];
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);
}
}
diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilationResultTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilationResultTest.cs
index 46a42549c1..962c7fa896 100644
--- a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilationResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilationResultTest.cs
@@ -14,14 +14,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
{
// Arrange
var compilationFailure = Mock.Of();
- var result = CompilationResult.Failed(compilationFailure);
+ var failures = new[] { compilationFailure };
+ var result = CompilationResult.Failed(failures);
// Act and Assert
Assert.Null(result.CompiledType);
- Assert.Same(compilationFailure, result.CompilationFailure);
+ Assert.Same(failures, result.CompilationFailures);
var exception = Assert.Throws(() => result.EnsureSuccessful());
- Assert.Collection(exception.CompilationFailures,
- failure => Assert.Same(compilationFailure, failure));
+ var failure = Assert.Single(exception.CompilationFailures);
+ Assert.Same(compilationFailure, failure);
}
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RazorCompilationServiceTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RazorCompilationServiceTest.cs
index 002eadb26a..7f92efd1ad 100644
--- a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RazorCompilationServiceTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RazorCompilationServiceTest.cs
@@ -8,6 +8,7 @@ using Microsoft.AspNet.Razor;
using Microsoft.AspNet.Razor.Generator.Compiler;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.TagHelpers;
+using Microsoft.Framework.OptionsModel;
using Moq;
using Xunit;
@@ -36,7 +37,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
compiler.Setup(c => c.Compile(relativeFileInfo, It.IsAny()))
.Returns(CompilationResult.Successful(typeof(RazorCompilationServiceTest)));
- var razorService = new RazorCompilationService(compiler.Object, host.Object);
+ var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions());
// Act
razorService.Compile(relativeFileInfo);
@@ -68,15 +69,19 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
var compiler = new Mock(MockBehavior.Strict);
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
var result = razorService.Compile(relativeFileInfo);
// Assert
- Assert.NotNull(result.CompilationFailure);
- var message = Assert.Single(result.CompilationFailure.Messages);
- Assert.Equal("some message", message.Message);
+ Assert.NotNull(result.CompilationFailures);
+ Assert.Collection(result.CompilationFailures,
+ failure =>
+ {
+ var message = Assert.Single(failure.Messages);
+ Assert.Equal("some message", message.Message);
+ });
host.Verify();
}
@@ -105,7 +110,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
compiler.Setup(c => c.Compile(relativeFileInfo, code))
.Returns(compilationResult)
.Verifiable();
- var razorService = new RazorCompilationService(compiler.Object, host.Object);
+ var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions());
// Act
var result = razorService.Compile(relativeFileInfo);
@@ -115,6 +120,86 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
compiler.Verify();
}
+ [Fact]
+ public void GetCompilationFailedResult_ReturnsCompilationResult_WithGroupedMessages()
+ {
+ // Arrange
+ var viewPath = @"views/index.razor";
+ var globalImportPath = @"views/global.import.cshtml";
+ var host = Mock.Of();
+
+ 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(),
+ Mock.Of(),
+ 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()
{
return new GeneratorResults(
@@ -124,5 +209,18 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
new CodeBuilderResult("", new LineMapping[0]),
new CodeTree());
}
+
+ private static IOptions GetOptions(IFileProvider fileProvider = null)
+ {
+ var razorViewEngineOptions = new RazorViewEngineOptions
+ {
+ FileProvider = fileProvider ?? new TestFileProvider()
+ };
+ var options = new Mock>();
+ options.SetupGet(o => o.Options)
+ .Returns(razorViewEngineOptions);
+
+ return options.Object;
+ }
}
}
diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs
index b4161006ad..424399c437 100644
--- a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs
@@ -6,6 +6,9 @@ using System.IO;
using System.Reflection;
using System.Runtime.Versioning;
using Microsoft.AspNet.FileProviders;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.Runtime.Compilation;
using Moq;
@@ -38,7 +41,8 @@ public class MyTestType {}";
accessor,
libraryManager,
compilerOptionsProvider.Object,
- mvcRazorHost.Object);
+ mvcRazorHost.Object,
+ GetOptions());
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path");
@@ -52,11 +56,14 @@ public class MyTestType {}";
}
[Fact]
- public void Compile_ReturnsCompilationFailureWithRelativePath()
+ public void Compile_ReturnsCompilationFailureWithPathsFromLinePragmas()
{
// Arrange
+ var viewPath = "some-relative-path";
var fileContent = "test file content";
- var content = @"this should fail";
+ var content = $@"
+#line 1 ""{viewPath}""
+this should fail";
var applicationEnvironment = GetApplicationEnvironment();
var accessor = GetLoadContextAccessor();
var libraryManager = GetLibraryManager();
@@ -67,17 +74,15 @@ public class MyTestType {}";
applicationEnvironment.Configuration))
.Returns(new CompilerOptions());
var mvcRazorHost = Mock.Of();
+ var fileProvider = new TestFileProvider();
+ var fileInfo = fileProvider.AddFile(viewPath, fileContent);
var compilationService = new RoslynCompilationService(applicationEnvironment,
accessor,
libraryManager,
compilerOptionsProvider.Object,
- mvcRazorHost);
- var fileInfo = new TestFileInfo
- {
- Content = fileContent,
- PhysicalPath = "physical path"
- };
+ mvcRazorHost,
+ GetOptions(fileProvider));
var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");
// Act
@@ -86,12 +91,13 @@ public class MyTestType {}";
// Assert
Assert.IsType(result);
Assert.Null(result.CompiledType);
- Assert.Equal(relativeFileInfo.RelativePath, result.CompilationFailure.SourceFilePath);
- Assert.Equal(fileContent, result.CompilationFailure.SourceFileContent);
+ var compilationFailure = Assert.Single(result.CompilationFailures);
+ Assert.Equal(relativeFileInfo.RelativePath, compilationFailure.SourceFilePath);
+ Assert.Equal(fileContent, compilationFailure.SourceFileContent);
}
[Fact]
- public void Compile_ReturnsApplicationRelativePath_IfPhyicalPathIsNotSpecified()
+ public void Compile_ReturnsGeneratedCodePath_IfLinePragmaIsNotAvailable()
{
// Arrange
var fileContent = "file content";
@@ -111,7 +117,8 @@ public class MyTestType {}";
accessor,
libraryManager,
compilerOptionsProvider.Object,
- mvcRazorHost);
+ mvcRazorHost,
+ GetOptions());
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { Content = fileContent },
"some-relative-path");
@@ -121,15 +128,20 @@ public class MyTestType {}";
// Assert
Assert.IsType(result);
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]
public void Compile_DoesNotThrow_IfFileCannotBeRead()
{
// Arrange
- var content = @"this should fail";
+ var path = "some-relative-path";
+ var content = $@"
+#line 1 ""{path}""
+this should fail";
var applicationEnvironment = GetApplicationEnvironment();
var accessor = GetLoadContextAccessor();
var libraryManager = GetLibraryManager();
@@ -141,15 +153,20 @@ public class MyTestType {}";
.Returns(new CompilerOptions());
var mvcRazorHost = Mock.Of();
+ var mockFileInfo = new Mock();
+ mockFileInfo.Setup(f => f.CreateReadStream())
+ .Throws(new Exception());
+ var fileProvider = new TestFileProvider();
+ fileProvider.AddFile(path, mockFileInfo.Object);
+
var compilationService = new RoslynCompilationService(applicationEnvironment,
accessor,
libraryManager,
compilerOptionsProvider.Object,
- mvcRazorHost);
- var mockFileInfo = new Mock();
- mockFileInfo.Setup(f => f.CreateReadStream())
- .Throws(new Exception());
- var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, "some-relative-path");
+ mvcRazorHost,
+ GetOptions(fileProvider));
+
+ var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path);
// Act
var result = compilationService.Compile(relativeFileInfo, content);
@@ -157,8 +174,9 @@ public class MyTestType {}";
// Assert
Assert.IsType(result);
Assert.Null(result.CompiledType);
- Assert.Equal("some-relative-path", result.CompilationFailure.SourceFilePath);
- Assert.Null(result.CompilationFailure.SourceFileContent);
+ var compilationFailure = Assert.Single(result.CompilationFailures);
+ Assert.Equal(path, compilationFailure.SourceFilePath);
+ Assert.Null(compilationFailure.SourceFileContent);
}
[Fact]
@@ -189,7 +207,8 @@ public class MyNonCustomDefinedClass {}
accessor,
libraryManager,
compilerOptionsProvider.Object,
- mvcRazorHost.Object);
+ mvcRazorHost.Object,
+ GetOptions());
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path");
@@ -225,7 +244,8 @@ public class NotRazorPrefixType {}";
accessor,
libraryManager,
compilerOptionsProvider.Object,
- mvcRazorHost.Object);
+ mvcRazorHost.Object,
+ GetOptions());
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path");
@@ -238,6 +258,113 @@ public class NotRazorPrefixType {}";
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>();
+ options.SetupGet(o => o.Options)
+ .Returns(new RazorViewEngineOptions
+ {
+ FileProvider = fileProvider
+ });
+ var compilationService = new RoslynCompilationService(
+ GetApplicationEnvironment(),
+ GetLoadContextAccessor(),
+ GetLibraryManager(),
+ Mock.Of(),
+ Mock.Of(),
+ 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()
{
var fileReference = new Mock();
@@ -285,5 +412,18 @@ public class NotRazorPrefixType {}";
return applicationEnvironment.Object;
}
+
+ private static IOptions GetOptions(IFileProvider fileProvider = null)
+ {
+ var razorViewEngineOptions = new RazorViewEngineOptions
+ {
+ FileProvider = fileProvider ?? new TestFileProvider()
+ };
+ var options = new Mock>();
+ options.SetupGet(o => o.Options)
+ .Returns(razorViewEngineOptions);
+
+ return options.Object;
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.TestCommon/TestFileProvider.cs b/test/Microsoft.AspNet.Mvc.TestCommon/TestFileProvider.cs
index a149075ad2..454f3af187 100644
--- a/test/Microsoft.AspNet.Mvc.TestCommon/TestFileProvider.cs
+++ b/test/Microsoft.AspNet.Mvc.TestCommon/TestFileProvider.cs
@@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.Razor
throw new NotSupportedException();
}
- public void AddFile(string path, string contents)
+ public TestFileInfo AddFile(string path, string contents)
{
var fileInfo = new TestFileInfo
{
@@ -32,6 +32,8 @@ namespace Microsoft.AspNet.Mvc.Razor
};
AddFile(path, fileInfo);
+
+ return fileInfo;
}
public void AddFile(string path, IFileInfo contents)
diff --git a/test/WebSites/ErrorPageMiddlewareWebSite/ErrorPageMiddlewareController.cs b/test/WebSites/ErrorPageMiddlewareWebSite/ErrorPageMiddlewareController.cs
index dda4e1f7a2..191f4aeb07 100644
--- a/test/WebSites/ErrorPageMiddlewareWebSite/ErrorPageMiddlewareController.cs
+++ b/test/WebSites/ErrorPageMiddlewareWebSite/ErrorPageMiddlewareController.cs
@@ -18,5 +18,11 @@ namespace ErrorPageMiddlewareWebSite
{
return View();
}
+
+ [HttpGet("/ErrorFromGlobalImport")]
+ public IActionResult GlobalImportError()
+ {
+ return View("~/Views/ErrorFromGlobalImport/Index");
+ }
}
}
diff --git a/test/WebSites/ErrorPageMiddlewareWebSite/Views/ErrorFromGlobalImport/Index.cshtml b/test/WebSites/ErrorPageMiddlewareWebSite/Views/ErrorFromGlobalImport/Index.cshtml
new file mode 100644
index 0000000000..6df88a66a1
--- /dev/null
+++ b/test/WebSites/ErrorPageMiddlewareWebSite/Views/ErrorFromGlobalImport/Index.cshtml
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file
diff --git a/test/WebSites/ErrorPageMiddlewareWebSite/Views/ErrorFromGlobalImport/_GlobalImport.cshtml b/test/WebSites/ErrorPageMiddlewareWebSite/Views/ErrorFromGlobalImport/_GlobalImport.cshtml
new file mode 100644
index 0000000000..c7ee89fafd
--- /dev/null
+++ b/test/WebSites/ErrorPageMiddlewareWebSite/Views/ErrorFromGlobalImport/_GlobalImport.cshtml
@@ -0,0 +1 @@
+@using NamespaceDoesNotExist
\ No newline at end of file