diff --git a/build/dependencies.props b/build/dependencies.props index 90660e53c..6f2f0187d 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -40,9 +40,7 @@ 3.0.0-alpha1-10742 3.0.0-alpha1-10742 3.0.0-alpha1-10742 - 3.0.0-alpha1-10742 3.0.0-alpha1-10742 - 3.0.0-alpha1-10742 3.0.0-alpha1-10742 3.0.0-alpha1-10742 3.0.0-alpha1-10742 diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/ApplicationAssembliesProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/ApplicationAssembliesProvider.cs index 9b222e72b..b1d414a8c 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/ApplicationAssembliesProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/ApplicationAssembliesProvider.cs @@ -28,7 +28,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts "Microsoft.AspNetCore.Mvc.Formatters.Xml", "Microsoft.AspNetCore.Mvc.Localization", "Microsoft.AspNetCore.Mvc.Razor", - "Microsoft.AspNetCore.Mvc.Razor.Extensions", "Microsoft.AspNetCore.Mvc.RazorPages", "Microsoft.AspNetCore.Mvc.TagHelpers", "Microsoft.AspNetCore.Mvc.ViewFeatures", diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CSharpCompiler.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CSharpCompiler.cs deleted file mode 100644 index c4f58943f..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CSharpCompiler.cs +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Emit; -using Microsoft.CodeAnalysis.Text; -using Microsoft.Extensions.DependencyModel; -using DependencyContextCompilationOptions = Microsoft.Extensions.DependencyModel.CompilationOptions; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - internal class CSharpCompiler - { -#pragma warning disable CS0618 // Type or member is obsolete - private readonly RazorReferenceManager _referenceManager; -#pragma warning restore CS0618 // Type or member is obsolete - private readonly IHostingEnvironment _hostingEnvironment; - private bool _optionsInitialized; - private CSharpParseOptions _parseOptions; - private CSharpCompilationOptions _compilationOptions; - private EmitOptions _emitOptions; - private bool _emitPdb; - -#pragma warning disable CS0618 // Type or member is obsolete - public CSharpCompiler(RazorReferenceManager manager, IHostingEnvironment hostingEnvironment) -#pragma warning restore CS0618 // Type or member is obsolete - { - _referenceManager = manager ?? throw new ArgumentNullException(nameof(manager)); - _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); - } - - public virtual CSharpParseOptions ParseOptions - { - get - { - EnsureOptions(); - return _parseOptions; - } - } - - public virtual CSharpCompilationOptions CSharpCompilationOptions - { - get - { - EnsureOptions(); - return _compilationOptions; - } - } - - public virtual bool EmitPdb - { - get - { - EnsureOptions(); - return _emitPdb; - } - } - - public virtual EmitOptions EmitOptions - { - get - { - EnsureOptions(); - return _emitOptions; - } - } - - public SyntaxTree CreateSyntaxTree(SourceText sourceText) - { - return CSharpSyntaxTree.ParseText( - sourceText, - options: ParseOptions); - } - - public CSharpCompilation CreateCompilation(string assemblyName) - { - return CSharpCompilation.Create( - assemblyName, - options: CSharpCompilationOptions, - references: _referenceManager.CompilationReferences); - } - - // Internal for unit testing. - protected internal virtual DependencyContextCompilationOptions GetDependencyContextCompilationOptions() - { - if (!string.IsNullOrEmpty(_hostingEnvironment.ApplicationName)) - { - var applicationAssembly = Assembly.Load(new AssemblyName(_hostingEnvironment.ApplicationName)); - var dependencyContext = DependencyContext.Load(applicationAssembly); - if (dependencyContext?.CompilationOptions != null) - { - return dependencyContext.CompilationOptions; - } - } - - return DependencyContextCompilationOptions.Default; - } - - private void EnsureOptions() - { - if (!_optionsInitialized) - { - var dependencyContextOptions = GetDependencyContextCompilationOptions(); - _parseOptions = GetParseOptions(_hostingEnvironment, dependencyContextOptions); - _compilationOptions = GetCompilationOptions(_hostingEnvironment, dependencyContextOptions); - _emitOptions = GetEmitOptions(dependencyContextOptions); - - _optionsInitialized = true; - } - } - - private EmitOptions GetEmitOptions(DependencyContextCompilationOptions dependencyContextOptions) - { - // Assume we're always producing pdbs unless DebugType = none - _emitPdb = true; - DebugInformationFormat debugInformationFormat; - if (string.IsNullOrEmpty(dependencyContextOptions.DebugType)) - { - debugInformationFormat = SymbolsUtility.SupportsFullPdbGeneration() ? - DebugInformationFormat.Pdb : - DebugInformationFormat.PortablePdb; - } - else - { - // Based on https://github.com/dotnet/roslyn/blob/1d28ff9ba248b332de3c84d23194a1d7bde07e4d/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs#L624-L640 - switch (dependencyContextOptions.DebugType.ToLower()) - { - case "none": - // There isn't a way to represent none in DebugInformationFormat. - // We'll set EmitPdb to false and let callers handle it by setting a null pdb-stream. - _emitPdb = false; - return new EmitOptions(); - case "portable": - debugInformationFormat = DebugInformationFormat.PortablePdb; - break; - case "embedded": - // Roslyn does not expose enough public APIs to produce a binary with embedded pdbs. - // We'll produce PortablePdb instead to continue providing a reasonable user experience. - debugInformationFormat = DebugInformationFormat.PortablePdb; - break; - case "full": - case "pdbonly": - debugInformationFormat = SymbolsUtility.SupportsFullPdbGeneration() ? - DebugInformationFormat.Pdb : - DebugInformationFormat.PortablePdb; - break; - default: - throw new InvalidOperationException(Resources.FormatUnsupportedDebugInformationFormat(dependencyContextOptions.DebugType)); - } - } - - var emitOptions = new EmitOptions(debugInformationFormat: debugInformationFormat); - return emitOptions; - } - - private static CSharpCompilationOptions GetCompilationOptions( - IHostingEnvironment hostingEnvironment, - DependencyContextCompilationOptions dependencyContextOptions) - { - var csharpCompilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - - // Disable 1702 until roslyn turns this off by default - csharpCompilationOptions = csharpCompilationOptions.WithSpecificDiagnosticOptions( - new Dictionary - { - {"CS1701", ReportDiagnostic.Suppress}, // Binding redirects - {"CS1702", ReportDiagnostic.Suppress}, - {"CS1705", ReportDiagnostic.Suppress} - }); - - if (dependencyContextOptions.AllowUnsafe.HasValue) - { - csharpCompilationOptions = csharpCompilationOptions.WithAllowUnsafe( - dependencyContextOptions.AllowUnsafe.Value); - } - - OptimizationLevel optimizationLevel; - if (dependencyContextOptions.Optimize.HasValue) - { - optimizationLevel = dependencyContextOptions.Optimize.Value ? - OptimizationLevel.Release : - OptimizationLevel.Debug; - } - else - { - optimizationLevel = hostingEnvironment.IsDevelopment() ? - OptimizationLevel.Debug : - OptimizationLevel.Release; - } - csharpCompilationOptions = csharpCompilationOptions.WithOptimizationLevel(optimizationLevel); - - if (dependencyContextOptions.WarningsAsErrors.HasValue) - { - var reportDiagnostic = dependencyContextOptions.WarningsAsErrors.Value ? - ReportDiagnostic.Error : - ReportDiagnostic.Default; - csharpCompilationOptions = csharpCompilationOptions.WithGeneralDiagnosticOption(reportDiagnostic); - } - - return csharpCompilationOptions; - } - - private static CSharpParseOptions GetParseOptions( - IHostingEnvironment hostingEnvironment, - DependencyContextCompilationOptions dependencyContextOptions) - { - var configurationSymbol = hostingEnvironment.IsDevelopment() ? "DEBUG" : "RELEASE"; - var defines = dependencyContextOptions.Defines.Concat(new[] { configurationSymbol }); - - var parseOptions = new CSharpParseOptions(preprocessorSymbols: defines); - - if (!string.IsNullOrEmpty(dependencyContextOptions.LanguageVersion)) - { - if (LanguageVersionFacts.TryParse(dependencyContextOptions.LanguageVersion, out var languageVersion)) - { - parseOptions = parseOptions.WithLanguageVersion(languageVersion); - } - else - { - Debug.Fail($"LanguageVersion {languageVersion} specified in the deps file could not be parsed."); - } - } - - return parseOptions; - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ChecksumValidator.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ChecksumValidator.cs deleted file mode 100644 index 2e5df4a3a..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ChecksumValidator.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using Microsoft.AspNetCore.Razor.Hosting; -using Microsoft.AspNetCore.Razor.Language; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - internal static class ChecksumValidator - { - public static bool IsRecompilationSupported(RazorCompiledItem item) - { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - - // A Razor item only supports recompilation if its primary source file has a checksum. - // - // Other files (view imports) may or may not have existed at the time of compilation, - // so we may not have checksums for them. - var checksums = item.GetChecksumMetadata(); - return checksums.Any(c => string.Equals(item.Identifier, c.Identifier, StringComparison.OrdinalIgnoreCase)); - } - - // Validates that we can use an existing precompiled view by comparing checksums with files on - // disk. - public static bool IsItemValid(RazorProjectFileSystem fileSystem, RazorCompiledItem item) - { - if (fileSystem == null) - { - throw new ArgumentNullException(nameof(fileSystem)); - } - - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - - var checksums = item.GetChecksumMetadata(); - - // The checksum that matches 'Item.Identity' in this list is significant. That represents the main file. - // - // We don't really care about the validation unless the main file exists. This is because we expect - // most sites to have some _ViewImports in common location. That means that in the case you're - // using views from a 3rd party library, you'll always have **some** conflicts. - // - // The presence of the main file with the same content is a very strong signal that you're in a - // development scenario. - var primaryChecksum = checksums - .FirstOrDefault(c => string.Equals(item.Identifier, c.Identifier, StringComparison.OrdinalIgnoreCase)); - if (primaryChecksum == null) - { - // No primary checksum, assume valid. - return true; - } - - var projectItem = fileSystem.GetItem(primaryChecksum.Identifier); - if (!projectItem.Exists) - { - // Main file doesn't exist - assume valid. - return true; - } - - var sourceDocument = RazorSourceDocument.ReadFrom(projectItem); - if (!string.Equals(sourceDocument.GetChecksumAlgorithm(), primaryChecksum.ChecksumAlgorithm) || - !ChecksumsEqual(primaryChecksum.Checksum, sourceDocument.GetChecksum())) - { - // Main file exists, but checksums not equal. - return false; - } - - for (var i = 0; i < checksums.Count; i++) - { - var checksum = checksums[i]; - if (string.Equals(item.Identifier, checksum.Identifier, StringComparison.OrdinalIgnoreCase)) - { - // Ignore primary checksum on this pass. - continue; - } - - var importItem = fileSystem.GetItem(checksum.Identifier); - if (!importItem.Exists) - { - // Import file doesn't exist - assume invalid. - return false; - } - - sourceDocument = RazorSourceDocument.ReadFrom(importItem); - if (!string.Equals(sourceDocument.GetChecksumAlgorithm(), checksum.ChecksumAlgorithm) || - !ChecksumsEqual(checksum.Checksum, sourceDocument.GetChecksum())) - { - // Import file exists, but checksums not equal. - return false; - } - } - - return true; - } - - private static bool ChecksumsEqual(string checksum, byte[] bytes) - { - if (bytes.Length * 2 != checksum.Length) - { - return false; - } - - for (var i = 0; i < bytes.Length; i++) - { - var text = bytes[i].ToString("x2"); - if (checksum[i * 2] != text[0] || checksum[i * 2 + 1] != text[1]) - { - return false; - } - } - - return true; - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CompilationFailedException.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CompilationFailedException.cs deleted file mode 100644 index 5e3899c0b..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CompilationFailedException.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Diagnostics; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - /// - /// An thrown when accessing the result of a failed compilation. - /// - public class CompilationFailedException : Exception, ICompilationException - { - /// - /// Instantiates a new instance of . - /// - /// s containing - /// details of the compilation failure. - public CompilationFailedException( - IEnumerable compilationFailures) - : base(FormatMessage(compilationFailures)) - { - if (compilationFailures == null) - { - throw new ArgumentNullException(nameof(compilationFailures)); - } - - CompilationFailures = compilationFailures; - } - - /// - public IEnumerable CompilationFailures { get; } - - private static string FormatMessage(IEnumerable compilationFailures) - { - return Resources.CompilationFailed + Environment.NewLine + - string.Join( - Environment.NewLine, - compilationFailures.SelectMany(f => f.Messages).Select(message => message.FormattedMessage)); - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CompilationFailedExceptionFactory.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CompilationFailedExceptionFactory.cs deleted file mode 100644 index 324b83572..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CompilationFailedExceptionFactory.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - internal static class CompilationFailedExceptionFactory - { - // error CS0234: The type or namespace name 'C' does not exist in the namespace 'N' (are you missing - // an assembly reference?) - private const string CS0234 = nameof(CS0234); - // error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive - // or an assembly reference?) - private const string CS0246 = nameof(CS0246); - - public static CompilationFailedException Create( - RazorCodeDocument codeDocument, - IEnumerable diagnostics) - { - // If a SourceLocation does not specify a file path, assume it is produced from parsing the current file. - var messageGroups = diagnostics.GroupBy( - razorError => razorError.Span.FilePath ?? codeDocument.Source.FilePath, - StringComparer.Ordinal); - - var failures = new List(); - foreach (var group in messageGroups) - { - var filePath = group.Key; - var fileContent = ReadContent(codeDocument, filePath); - var compilationFailure = new CompilationFailure( - filePath, - fileContent, - compiledContent: string.Empty, - messages: group.Select(parserError => CreateDiagnosticMessage(parserError, filePath))); - failures.Add(compilationFailure); - } - - return new CompilationFailedException(failures); - } - - public static CompilationFailedException Create( - RazorCodeDocument codeDocument, - string compilationContent, - string assemblyName, - IEnumerable diagnostics) - { - var diagnosticGroups = diagnostics - .Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error) - .GroupBy(diagnostic => GetFilePath(codeDocument, 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 = ReadContent(codeDocument, sourceFilePath); - } - - string additionalMessage = null; - if (group.Any(g => - string.Equals(CS0234, g.Id, StringComparison.OrdinalIgnoreCase) || - string.Equals(CS0246, g.Id, StringComparison.OrdinalIgnoreCase))) - { - additionalMessage = Resources.FormatCompilation_MissingReferences( - "CopyRefAssembliesToPublishDirectory"); - } - - var compilationFailure = new CompilationFailure( - sourceFilePath, - sourceFileContent, - compilationContent, - group.Select(GetDiagnosticMessage), - additionalMessage); - - failures.Add(compilationFailure); - } - - return new CompilationFailedException(failures); - } - - private static string ReadContent(RazorCodeDocument codeDocument, string filePath) - { - RazorSourceDocument sourceDocument; - if (string.IsNullOrEmpty(filePath) || string.Equals(codeDocument.Source.FilePath, filePath, StringComparison.Ordinal)) - { - sourceDocument = codeDocument.Source; - } - else - { - sourceDocument = codeDocument.Imports.FirstOrDefault(f => string.Equals(f.FilePath, filePath, StringComparison.Ordinal)); - } - - if (sourceDocument != null) - { - var contentChars = new char[sourceDocument.Length]; - sourceDocument.CopyTo(0, contentChars, 0, sourceDocument.Length); - return new string(contentChars); - } - - return string.Empty; - } - - private static DiagnosticMessage GetDiagnosticMessage(Diagnostic diagnostic) - { - var mappedLineSpan = diagnostic.Location.GetMappedLineSpan(); - return new DiagnosticMessage( - diagnostic.GetMessage(), - CSharpDiagnosticFormatter.Instance.Format(diagnostic), - mappedLineSpan.Path, - mappedLineSpan.StartLinePosition.Line + 1, - mappedLineSpan.StartLinePosition.Character + 1, - mappedLineSpan.EndLinePosition.Line + 1, - mappedLineSpan.EndLinePosition.Character + 1); - } - - private static DiagnosticMessage CreateDiagnosticMessage( - RazorDiagnostic razorDiagnostic, - string filePath) - { - var sourceSpan = razorDiagnostic.Span; - var message = razorDiagnostic.GetMessage(); - return new DiagnosticMessage( - message: message, - formattedMessage: razorDiagnostic.ToString(), - filePath: filePath, - startLine: sourceSpan.LineIndex + 1, - startColumn: sourceSpan.CharacterIndex, - endLine: sourceSpan.LineIndex + 1, - endColumn: sourceSpan.CharacterIndex + sourceSpan.Length); - } - - private static string GetFilePath(RazorCodeDocument codeDocument, Diagnostic diagnostic) - { - if (diagnostic.Location == Location.None) - { - return codeDocument.Source.FilePath; - } - - return diagnostic.Location.GetMappedLineSpan().Path; - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CompiledViewDescriptor.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CompiledViewDescriptor.cs index 1e5bbb0c4..213218c5c 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CompiledViewDescriptor.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/CompiledViewDescriptor.cs @@ -51,7 +51,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation // later. ExpirationTokens = Array.Empty(); RelativePath = ViewPath.NormalizePath(item?.Identifier ?? attribute.Path); - IsPrecompiled = true; } /// @@ -72,11 +71,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation /// public IList ExpirationTokens { get; set; } - /// - /// Gets a value that determines if the view is precompiled. - /// - public bool IsPrecompiled { get; set; } - /// /// Gets the descriptor for this view. /// diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/DefaultRazorPageFactoryProvider.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/DefaultRazorPageFactoryProvider.cs index a96874988..fc0d03cf0 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/DefaultRazorPageFactoryProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/DefaultRazorPageFactoryProvider.cs @@ -4,7 +4,6 @@ using System; using System.Linq.Expressions; using System.Reflection; -using Microsoft.AspNetCore.Mvc.Razor.Compilation; namespace Microsoft.AspNetCore.Mvc.Razor.Compilation { diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/DefaultRazorReferenceManager.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/DefaultRazorReferenceManager.cs deleted file mode 100644 index b2a8e8012..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/DefaultRazorReferenceManager.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.CodeAnalysis; -using Microsoft.Extensions.Options; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ -#pragma warning disable CS0618 // Type or member is obsolete - internal class DefaultRazorReferenceManager : RazorReferenceManager -#pragma warning restore CS0618 // Type or member is obsolete - { - private readonly ApplicationPartManager _partManager; - private readonly IList _additionalMetadataReferences; - private object _compilationReferencesLock = new object(); - private bool _compilationReferencesInitialized; - private IReadOnlyList _compilationReferences; - - public DefaultRazorReferenceManager( - ApplicationPartManager partManager, - IOptions optionsAccessor) - { - _partManager = partManager; -#pragma warning disable CS0618 // Type or member is obsolete - _additionalMetadataReferences = optionsAccessor.Value.AdditionalCompilationReferences; -#pragma warning restore CS0618 // Type or member is obsolete - } - - public override IReadOnlyList CompilationReferences - { - get - { - return LazyInitializer.EnsureInitialized( - ref _compilationReferences, - ref _compilationReferencesInitialized, - ref _compilationReferencesLock, - GetCompilationReferences); - } - } - - private IReadOnlyList GetCompilationReferences() - { -#pragma warning disable CS0618 // Type or member is obsolete - var feature = new MetadataReferenceFeature(); -#pragma warning restore CS0618 // Type or member is obsolete - _partManager.PopulateFeature(feature); - var applicationReferences = feature.MetadataReferences; - - if (_additionalMetadataReferences.Count == 0) - { - return applicationReferences.ToArray(); - } - - var compilationReferences = new List(applicationReferences.Count + _additionalMetadataReferences.Count); - compilationReferences.AddRange(applicationReferences); - compilationReferences.AddRange(_additionalMetadataReferences); - - return compilationReferences; - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ExpressionRewriter.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ExpressionRewriter.cs deleted file mode 100644 index d76b635d3..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ExpressionRewriter.cs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq.Expressions; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - /// - /// An expression rewriter which can hoist a simple expression lambda into a private field. - /// - internal class ExpressionRewriter : CSharpSyntaxRewriter - { - private static readonly string FieldNameTemplate = "__h{0}"; - - public ExpressionRewriter(SemanticModel semanticModel) - { - SemanticModel = semanticModel; - - Expressions = new List>(); - } - - // We only want to rewrite expressions for the top-level class definition. - private bool IsInsideClass { get; set; } - - private SemanticModel SemanticModel { get; } - - private List> Expressions { get; } - - public static CSharpCompilation Rewrite(CSharpCompilation compilation) - { - var rewrittenTrees = new List(); - foreach (var tree in compilation.SyntaxTrees) - { - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - var rewriter = new ExpressionRewriter(semanticModel); - - var rewrittenTree = tree.WithRootAndOptions(rewriter.Visit(tree.GetRoot()), tree.Options); - rewrittenTrees.Add(rewrittenTree); - } - - return compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(rewrittenTrees); - } - - public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) - { - if (IsInsideClass) - { - // Avoid recursing into nested classes. - return node; - } - - Expressions.Clear(); - - IsInsideClass = true; - - // Call base first to visit all the children and populate Expressions. - var classDeclaration = (ClassDeclarationSyntax)base.VisitClassDeclaration(node); - - IsInsideClass = false; - - var memberDeclarations = new List(); - foreach (var kvp in Expressions) - { - var expression = kvp.Key; - var memberName = kvp.Value.GetFirstToken(); - - var expressionType = SemanticModel.GetTypeInfo(expression).ConvertedType; - var declaration = SyntaxFactory.FieldDeclaration( - SyntaxFactory.List(), - SyntaxFactory.TokenList( - SyntaxFactory.Token(SyntaxKind.PrivateKeyword), - SyntaxFactory.Token(SyntaxKind.StaticKeyword), - SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)), - SyntaxFactory.VariableDeclaration( - SyntaxFactory.ParseTypeName(expressionType.ToDisplayString( - SymbolDisplayFormat.FullyQualifiedFormat)), - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.VariableDeclarator( - memberName, - SyntaxFactory.BracketedArgumentList(), - SyntaxFactory.EqualsValueClause(expression))))) - .WithTriviaFrom(expression); - memberDeclarations.Add(declaration); - } - - return classDeclaration.AddMembers(memberDeclarations.ToArray()); - } - - public override SyntaxNode VisitSimpleLambdaExpression(SimpleLambdaExpressionSyntax node) - { - Debug.Assert(IsInsideClass); - - // If this lambda is an Expression and is suitable for hoisting, we rewrite this into a field access. - // - // Before: - // public Task ExecuteAsync(...) - // { - // ... - // Html.EditorFor(m => m.Price); - // ... - // } - // - // - // After: - // private static readonly Expression> __h0 = m => m.Price; - // public Task ExecuteAsync(...) - // { - // ... - // Html.EditorFor(__h0); - // ... - // } - // - var type = SemanticModel.GetTypeInfo(node); - - // Due to an anomaly where Roslyn (depending on code sample) may finish compilation without diagnostic - // errors (this code path does not execute when diagnostic errors are present) we need to validate that - // the ConvertedType was determined/is not null. - if (type.ConvertedType == null || - type.ConvertedType.Name != typeof(Expression).Name && - type.ConvertedType.ContainingNamespace.Name != typeof(Expression).Namespace) - { - return node; - } - - if (!node.Parent.IsKind(SyntaxKind.Argument)) - { - return node; - } - - var parameter = node.Parameter; - if (IsValidForHoisting(parameter, node.Body)) - { - // Replace with a MemberAccess - var memberName = string.Format(FieldNameTemplate, Expressions.Count); - var memberAccess = PadMemberAccess(node, SyntaxFactory.IdentifierName(memberName)); - Expressions.Add(new KeyValuePair(node, memberAccess)); - return memberAccess; - } - - return node; - } - - private static IdentifierNameSyntax PadMemberAccess( - SimpleLambdaExpressionSyntax node, - IdentifierNameSyntax memberAccess) - { - var charactersToExclude = memberAccess.Identifier.Text.Length; - var triviaList = new SyntaxTriviaList(); - - // Go through each token and - // 1. Append leading trivia - // 2. Append the same number of whitespace as the length of the token text - // 3. Append trailing trivia - foreach (var token in node.DescendantTokens()) - { - if (token.HasLeadingTrivia) - { - triviaList = triviaList.AddRange(token.LeadingTrivia); - } - - // Need to exclude the length of the member name from the padding. - var padding = token.Text.Length; - if (padding > charactersToExclude) - { - padding -= charactersToExclude; - charactersToExclude = 0; - } - else - { - charactersToExclude -= padding; - padding = 0; - } - - if (padding > 0) - { - triviaList = triviaList.Add(SyntaxFactory.Whitespace(new string(' ', padding))); - } - - if (token.HasTrailingTrivia) - { - triviaList = triviaList.AddRange(token.TrailingTrivia); - } - } - - return memberAccess - .WithLeadingTrivia(node.GetLeadingTrivia()) - .WithTrailingTrivia(triviaList); - } - - private static bool IsValidForHoisting(ParameterSyntax parameter, CSharpSyntaxNode node) - { - if (node.IsKind(SyntaxKind.IdentifierName)) - { - var identifier = (IdentifierNameSyntax)node; - if (identifier.Identifier.Text == parameter.Identifier.Text) - { - return true; - } - } - else if (node.IsKind(SyntaxKind.SimpleMemberAccessExpression)) - { - var memberAccess = (MemberAccessExpressionSyntax)node; - var lhs = memberAccess.Expression; - return IsValidForHoisting(parameter, lhs); - } - - return false; - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/IViewCompilationMemoryCacheProvider.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/IViewCompilationMemoryCacheProvider.cs deleted file mode 100644 index 5abbd550f..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/IViewCompilationMemoryCacheProvider.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.Extensions.Caching.Memory; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - /// - /// Provides an instance of that is used to store compiled Razor views. - /// - public interface IViewCompilationMemoryCacheProvider - { - IMemoryCache CompilationMemoryCache { get; } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/MetadataReferenceFeature.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/MetadataReferenceFeature.cs deleted file mode 100644 index 478d21eed..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/MetadataReferenceFeature.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using Microsoft.CodeAnalysis; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - /// - /// Specifies the list of used in Razor compilation. - /// - [Obsolete("This type is obsolete and will be removed in a future version. See https://aka.ms/AA1x4gg for details.")] - public class MetadataReferenceFeature - { - /// - /// Gets the instances. - /// - public IList MetadataReferences { get; } = new List(); - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/MetadataReferenceFeatureProvider.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/MetadataReferenceFeatureProvider.cs deleted file mode 100644 index 8050671a1..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/MetadataReferenceFeatureProvider.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection.PortableExecutable; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.CodeAnalysis; -using Microsoft.Extensions.DependencyModel; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - /// - /// An for that - /// uses for registered instances to create - /// . - /// - [Obsolete("This type is obsolete and will be removed in a future version. See https://aka.ms/AA1x4gg for details.")] - public class MetadataReferenceFeatureProvider : IApplicationFeatureProvider - { - /// - public void PopulateFeature(IEnumerable parts, MetadataReferenceFeature feature) - { - if (parts == null) - { - throw new ArgumentNullException(nameof(parts)); - } - - if (feature == null) - { - throw new ArgumentNullException(nameof(feature)); - } - - var libraryPaths = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (var providerPart in parts.OfType()) - { - var referencePaths = providerPart.GetReferencePaths(); - foreach (var path in referencePaths) - { - if (libraryPaths.Add(path)) - { - var metadataReference = CreateMetadataReference(path); - feature.MetadataReferences.Add(metadataReference); - } - } - } - } - - private static MetadataReference CreateMetadataReference(string path) - { - using (var stream = File.OpenRead(path)) - { - var moduleMetadata = ModuleMetadata.CreateFromStream(stream, PEStreamOptions.PrefetchMetadata); - var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata); - - return assemblyMetadata.GetReference(filePath: path); - } - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorReferenceManager.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorReferenceManager.cs deleted file mode 100644 index df0962316..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorReferenceManager.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using Microsoft.CodeAnalysis; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - /// - /// Manages compilation references for Razor compilation. - /// - [Obsolete("This type is obsolete and will be removed in a future version. See https://aka.ms/AA1x4gg for details.")] - public abstract class RazorReferenceManager - { - /// - /// Gets the set of compilation references to be used for Razor compilation. - /// - public abstract IReadOnlyList CompilationReferences { get; } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompilationMemoryCacheProvider.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompilationMemoryCacheProvider.cs deleted file mode 100644 index 9a11db0b3..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompilationMemoryCacheProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.Extensions.Caching.Memory; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - internal class RazorViewCompilationMemoryCacheProvider : IViewCompilationMemoryCacheProvider - { - IMemoryCache IViewCompilationMemoryCacheProvider.CompilationMemoryCache { get; } = new MemoryCache(new MemoryCacheOptions()); - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompiler.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompiler.cs index 8dbc2012f..7afc53eda 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompiler.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompiler.cs @@ -5,18 +5,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Hosting; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Emit; -using Microsoft.CodeAnalysis.Text; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; @@ -27,48 +16,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation /// internal class RazorViewCompiler : IViewCompiler { - private readonly object _cacheLock = new object(); - private readonly Dictionary _precompiledViews; + private readonly Dictionary> _compiledViews; private readonly ConcurrentDictionary _normalizedPathCache; - private readonly IFileProvider _fileProvider; - private readonly RazorProjectEngine _projectEngine; - private readonly Action _compilationCallback; - private readonly IMemoryCache _cache; private readonly ILogger _logger; - private readonly CSharpCompiler _csharpCompiler; public RazorViewCompiler( - IFileProvider fileProvider, - RazorProjectEngine projectEngine, - CSharpCompiler csharpCompiler, - Action compilationCallback, - IList precompiledViews, - IMemoryCache cache, + IList compiledViews, ILogger logger) { - if (fileProvider == null) + if (compiledViews == null) { - throw new ArgumentNullException(nameof(fileProvider)); - } - - if (projectEngine == null) - { - throw new ArgumentNullException(nameof(projectEngine)); - } - - if (csharpCompiler == null) - { - throw new ArgumentNullException(nameof(csharpCompiler)); - } - - if (compilationCallback == null) - { - throw new ArgumentNullException(nameof(compilationCallback)); - } - - if (precompiledViews == null) - { - throw new ArgumentNullException(nameof(precompiledViews)); + throw new ArgumentNullException(nameof(compiledViews)); } if (logger == null) @@ -76,47 +34,35 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation throw new ArgumentNullException(nameof(logger)); } - _fileProvider = fileProvider; - _projectEngine = projectEngine; - _csharpCompiler = csharpCompiler; - _compilationCallback = compilationCallback; _logger = logger; - - _normalizedPathCache = new ConcurrentDictionary(StringComparer.Ordinal); - // This is our L0 cache, and is a durable store. Views migrate into the cache as they are requested - // from either the set of known precompiled views, or by being compiled. - _cache = cache; - // We need to validate that the all of the precompiled views are unique by path (case-insensitive). // We do this because there's no good way to canonicalize paths on windows, and it will create // problems when deploying to linux. Rather than deal with these issues, we just don't support // views that differ only by case. - _precompiledViews = new Dictionary( - precompiledViews.Count, + _compiledViews = new Dictionary>( + compiledViews.Count, StringComparer.OrdinalIgnoreCase); - foreach (var precompiledView in precompiledViews) + foreach (var compiledView in compiledViews) { - logger.ViewCompilerLocatedCompiledView(precompiledView.RelativePath); + logger.ViewCompilerLocatedCompiledView(compiledView.RelativePath); - if (!_precompiledViews.ContainsKey(precompiledView.RelativePath)) + if (!_compiledViews.ContainsKey(compiledView.RelativePath)) { - // View ordering has precedence semantics, a view with a higher precedence was + // View ordering has precedence semantics, a view with a higher precedence was not // already added to the list. - _precompiledViews.Add(precompiledView.RelativePath, precompiledView); + _compiledViews.Add(compiledView.RelativePath, Task.FromResult(compiledView)); } } - if (_precompiledViews.Count == 0) + if (_compiledViews.Count == 0) { logger.ViewCompilerNoCompiledViewsFound(); } } - public bool AllowRecompilingViewsOnFileChange { get; set; } - /// public Task CompileAsync(string relativePath) { @@ -127,320 +73,26 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation // Attempt to lookup the cache entry using the passed in path. This will succeed if the path is already // normalized and a cache entry exists. - if (_cache.TryGetValue(relativePath, out Task cachedResult)) + if (_compiledViews.TryGetValue(relativePath, out var cachedResult)) { + _logger.ViewCompilerLocatedCompiledViewForPath(relativePath); return cachedResult; } var normalizedPath = GetNormalizedPath(relativePath); - if (_cache.TryGetValue(normalizedPath, out cachedResult)) + if (_compiledViews.TryGetValue(normalizedPath, out cachedResult)) { + _logger.ViewCompilerLocatedCompiledViewForPath(normalizedPath); return cachedResult; } // Entry does not exist. Attempt to create one. - cachedResult = OnCacheMiss(normalizedPath); - return cachedResult; - } - - private Task OnCacheMiss(string normalizedPath) - { - ViewCompilerWorkItem item; - TaskCompletionSource taskSource; - MemoryCacheEntryOptions cacheEntryOptions; - - // Safe races cannot be allowed when compiling Razor pages. To ensure only one compilation request succeeds - // per file, we'll lock the creation of a cache entry. Creating the cache entry should be very quick. The - // actual work for compiling files happens outside the critical section. - lock (_cacheLock) + _logger.ViewCompilerCouldNotFindFileAtPath(relativePath); + return Task.FromResult(new CompiledViewDescriptor { - // Double-checked locking to handle a possible race. - if (_cache.TryGetValue(normalizedPath, out Task result)) - { - return result; - } - - if (_precompiledViews.TryGetValue(normalizedPath, out var precompiledView)) - { - _logger.ViewCompilerLocatedCompiledViewForPath(normalizedPath); - item = CreatePrecompiledWorkItem(normalizedPath, precompiledView); - } - else - { - item = CreateRuntimeCompilationWorkItem(normalizedPath); - } - - // At this point, we've decided what to do - but we should create the cache entry and - // release the lock first. - cacheEntryOptions = new MemoryCacheEntryOptions(); - - Debug.Assert(item.ExpirationTokens != null); - for (var i = 0; i < item.ExpirationTokens.Count; i++) - { - cacheEntryOptions.ExpirationTokens.Add(item.ExpirationTokens[i]); - } - - taskSource = new TaskCompletionSource(creationOptions: TaskCreationOptions.RunContinuationsAsynchronously); - if (item.SupportsCompilation) - { - // We'll compile in just a sec, be patient. - } - else - { - // If we can't compile, we should have already created the descriptor - Debug.Assert(item.Descriptor != null); - taskSource.SetResult(item.Descriptor); - } - - _cache.Set(normalizedPath, taskSource.Task, cacheEntryOptions); - } - - // Now the lock has been released so we can do more expensive processing. - if (item.SupportsCompilation) - { - Debug.Assert(taskSource != null); - - if (item.Descriptor?.Item != null && - ChecksumValidator.IsItemValid(_projectEngine.FileSystem, item.Descriptor.Item)) - { - // If the item has checksums to validate, we should also have a precompiled view. - Debug.Assert(item.Descriptor != null); - - taskSource.SetResult(item.Descriptor); - return taskSource.Task; - } - - _logger.ViewCompilerInvalidingCompiledFile(item.NormalizedPath); - try - { - var descriptor = CompileAndEmit(normalizedPath); - descriptor.ExpirationTokens = cacheEntryOptions.ExpirationTokens; - taskSource.SetResult(descriptor); - } - catch (Exception ex) - { - taskSource.SetException(ex); - } - } - - return taskSource.Task; - } - - private ViewCompilerWorkItem CreatePrecompiledWorkItem(string normalizedPath, CompiledViewDescriptor precompiledView) - { - // We have a precompiled view - but we're not sure that we can use it yet. - // - // We need to determine first if we have enough information to 'recompile' this view. If that's the case - // we'll create change tokens for all of the files. - // - // Then we'll attempt to validate if any of those files have different content than the original sources - // based on checksums. - if (precompiledView.Item == null || !ChecksumValidator.IsRecompilationSupported(precompiledView.Item)) - { - return new ViewCompilerWorkItem() - { - // If we don't have a checksum for the primary source file we can't recompile. - SupportsCompilation = false, - - ExpirationTokens = Array.Empty(), // Never expire because we can't recompile. - Descriptor = precompiledView, // This will be used as-is. - }; - } - - var item = new ViewCompilerWorkItem() - { - SupportsCompilation = true, - - Descriptor = precompiledView, // This might be used, if the checksums match. - - // Used to validate and recompile - NormalizedPath = normalizedPath, - - ExpirationTokens = GetExpirationTokens(precompiledView), - }; - - // We also need to create a new descriptor, because the original one doesn't have expiration tokens on - // it. These will be used by the view location cache, which is like an L1 cache for views (this class is - // the L2 cache). - item.Descriptor = new CompiledViewDescriptor() - { - ExpirationTokens = item.ExpirationTokens, - IsPrecompiled = true, - Item = precompiledView.Item, - RelativePath = precompiledView.RelativePath, - ViewAttribute = precompiledView.ViewAttribute, - }; - - return item; - } - - private ViewCompilerWorkItem CreateRuntimeCompilationWorkItem(string normalizedPath) - { - IList expirationTokens = Array.Empty(); - - if (AllowRecompilingViewsOnFileChange) - { - var changeToken = _fileProvider.Watch(normalizedPath); - expirationTokens = new List { changeToken }; - } - - var projectItem = _projectEngine.FileSystem.GetItem(normalizedPath); - if (!projectItem.Exists) - { - _logger.ViewCompilerCouldNotFindFileAtPath(normalizedPath); - - // If the file doesn't exist, we can't do compilation right now - we still want to cache - // the fact that we tried. This will allow us to re-trigger compilation if the view file - // is added. - return new ViewCompilerWorkItem() - { - // We don't have enough information to compile - SupportsCompilation = false, - - Descriptor = new CompiledViewDescriptor() - { - RelativePath = normalizedPath, - ExpirationTokens = expirationTokens, - }, - - // We can try again if the file gets created. - ExpirationTokens = expirationTokens, - }; - } - - _logger.ViewCompilerFoundFileToCompile(normalizedPath); - - GetChangeTokensFromImports(expirationTokens, projectItem); - - return new ViewCompilerWorkItem() - { - SupportsCompilation = true, - - NormalizedPath = normalizedPath, - ExpirationTokens = expirationTokens, - }; - } - - private IList GetExpirationTokens(CompiledViewDescriptor precompiledView) - { - if (!AllowRecompilingViewsOnFileChange) - { - return Array.Empty(); - } - - var checksums = precompiledView.Item.GetChecksumMetadata(); - var expirationTokens = new List(checksums.Count); - - for (var i = 0; i < checksums.Count; i++) - { - // We rely on Razor to provide the right set of checksums. Trust the compiler, it has to do a good job, - // so it probably will. - expirationTokens.Add(_fileProvider.Watch(checksums[i].Identifier)); - } - - return expirationTokens; - } - - private void GetChangeTokensFromImports(IList expirationTokens, RazorProjectItem projectItem) - { - if (!AllowRecompilingViewsOnFileChange) - { - return; - } - - // OK this means we can do compilation. For now let's just identify the other files we need to watch - // so we can create the cache entry. Compilation will happen after we release the lock. - var importFeature = _projectEngine.ProjectFeatures.OfType().FirstOrDefault(); - - // There should always be an import feature unless someone has misconfigured their RazorProjectEngine. - // In that case once we attempt to parse the Razor file we'll explode and give the a user a decent - // error message; for now, lets just be extra protective and assume 0 imports to not give a bad error. - var imports = importFeature?.GetImports(projectItem) ?? Enumerable.Empty(); - var physicalImports = imports.Where(import => import.FilePath != null); - - // Now that we have non-dynamic imports we need to get their RazorProjectItem equivalents so we have their - // physical file paths (according to the FileSystem). - foreach (var physicalImport in physicalImports) - { - expirationTokens.Add(_fileProvider.Watch(physicalImport.FilePath)); - } - } - - protected virtual CompiledViewDescriptor CompileAndEmit(string relativePath) - { - var projectItem = _projectEngine.FileSystem.GetItem(relativePath); - var codeDocument = _projectEngine.Process(projectItem); - var cSharpDocument = codeDocument.GetCSharpDocument(); - - if (cSharpDocument.Diagnostics.Count > 0) - { - throw CompilationFailedExceptionFactory.Create( - codeDocument, - cSharpDocument.Diagnostics); - } - - var assembly = CompileAndEmit(codeDocument, cSharpDocument.GeneratedCode); - - // Anything we compile from source will use Razor 2.1 and so should have the new metadata. - var loader = new RazorCompiledItemLoader(); - var item = loader.LoadItems(assembly).SingleOrDefault(); - var attribute = assembly.GetCustomAttribute(); - - return new CompiledViewDescriptor(item, attribute); - } - - internal Assembly CompileAndEmit(RazorCodeDocument codeDocument, string generatedCode) - { - _logger.GeneratedCodeToAssemblyCompilationStart(codeDocument.Source.FilePath); - - var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0; - - var assemblyName = Path.GetRandomFileName(); - var compilation = CreateCompilation(generatedCode, assemblyName); - - var emitOptions = _csharpCompiler.EmitOptions; - var emitPdbFile = _csharpCompiler.EmitPdb && emitOptions.DebugInformationFormat != DebugInformationFormat.Embedded; - - using (var assemblyStream = new MemoryStream()) - using (var pdbStream = emitPdbFile ? new MemoryStream() : null) - { - var result = compilation.Emit( - assemblyStream, - pdbStream, - options: emitOptions); - - if (!result.Success) - { - throw CompilationFailedExceptionFactory.Create( - codeDocument, - generatedCode, - assemblyName, - result.Diagnostics); - } - - assemblyStream.Seek(0, SeekOrigin.Begin); - pdbStream?.Seek(0, SeekOrigin.Begin); - - var assembly = Assembly.Load(assemblyStream.ToArray(), pdbStream?.ToArray()); - _logger.GeneratedCodeToAssemblyCompilationEnd(codeDocument.Source.FilePath, startTimestamp); - - return assembly; - } - } - - private CSharpCompilation CreateCompilation(string compilationContent, string assemblyName) - { - var sourceText = SourceText.From(compilationContent, Encoding.UTF8); - var syntaxTree = _csharpCompiler.CreateSyntaxTree(sourceText).WithFilePath(assemblyName); - var compilation = _csharpCompiler - .CreateCompilation(assemblyName) - .AddSyntaxTrees(syntaxTree); - compilation = ExpressionRewriter.Rewrite(compilation); - - var compilationContext = new RoslynCompilationContext(compilation); - _compilationCallback(compilationContext); - compilation = compilationContext.Compilation; - return compilation; + RelativePath = normalizedPath, + ExpirationTokens = Array.Empty(), + }); } private string GetNormalizedPath(string relativePath) @@ -459,16 +111,5 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation return normalizedPath; } - - private class ViewCompilerWorkItem - { - public bool SupportsCompilation { get; set; } - - public string NormalizedPath { get; set; } - - public IList ExpirationTokens { get; set; } - - public CompiledViewDescriptor Descriptor { get; set; } - } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompilerProvider.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompilerProvider.cs index 44fb8a0ef..64965e6f7 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompilerProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RazorViewCompilerProvider.cs @@ -1,89 +1,25 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Threading; using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.AspNetCore.Mvc.Razor.Compilation; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Mvc.Razor.Compilation { internal class RazorViewCompilerProvider : IViewCompilerProvider { - private readonly RazorProjectEngine _razorProjectEngine; - private readonly ApplicationPartManager _applicationPartManager; - private readonly IRazorViewEngineFileProviderAccessor _fileProviderAccessor; - private readonly CSharpCompiler _csharpCompiler; - private readonly IViewCompilationMemoryCacheProvider _compilationMemoryCacheProvider; - private readonly RazorViewEngineOptions _viewEngineOptions; - private readonly ILogger _logger; - private readonly Func _createCompiler; - - private object _initializeLock = new object(); - private bool _initialized; - private IViewCompiler _compiler; + private readonly RazorViewCompiler _compiler; public RazorViewCompilerProvider( ApplicationPartManager applicationPartManager, - RazorProjectEngine razorProjectEngine, - IRazorViewEngineFileProviderAccessor fileProviderAccessor, - CSharpCompiler csharpCompiler, - IOptions viewEngineOptionsAccessor, - IViewCompilationMemoryCacheProvider compilationMemoryCacheProvider, ILoggerFactory loggerFactory) - { - _applicationPartManager = applicationPartManager; - _razorProjectEngine = razorProjectEngine; - _fileProviderAccessor = fileProviderAccessor; - _csharpCompiler = csharpCompiler; - _compilationMemoryCacheProvider = compilationMemoryCacheProvider; - _viewEngineOptions = viewEngineOptionsAccessor.Value; - - _logger = loggerFactory.CreateLogger(); - _createCompiler = CreateCompiler; - } - - public IViewCompiler GetCompiler() - { - var fileProvider = _fileProviderAccessor.FileProvider; - if (fileProvider is NullFileProvider) - { - var message = Resources.FormatFileProvidersAreRequired( - typeof(RazorViewEngineOptions).FullName, - nameof(RazorViewEngineOptions.FileProviders), - typeof(IFileProvider).FullName); - throw new InvalidOperationException(message); - } - - return LazyInitializer.EnsureInitialized( - ref _compiler, - ref _initialized, - ref _initializeLock, - _createCompiler); - } - - private IViewCompiler CreateCompiler() { var feature = new ViewsFeature(); - _applicationPartManager.PopulateFeature(feature); + applicationPartManager.PopulateFeature(feature); - return new RazorViewCompiler( - _fileProviderAccessor.FileProvider, - _razorProjectEngine, - _csharpCompiler, -#pragma warning disable CS0618 // Type or member is obsolete - _viewEngineOptions.CompilationCallback, -#pragma warning restore CS0618 // Type or member is obsolete - feature.ViewDescriptors, - _compilationMemoryCacheProvider.CompilationMemoryCache, - _logger) - { - AllowRecompilingViewsOnFileChange = _viewEngineOptions.AllowRecompilingViewsOnFileChange, - }; + _compiler = new RazorViewCompiler(feature.ViewDescriptors, loggerFactory.CreateLogger()); } + + public IViewCompiler GetCompiler() => _compiler; } } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RoslynCompilationContext.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RoslynCompilationContext.cs deleted file mode 100644 index 553696972..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RoslynCompilationContext.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.CodeAnalysis.CSharp; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - /// - /// Context object used to pass information about the current Razor page compilation. - /// - public class RoslynCompilationContext - { - /// - /// Constructs a new instance of the type. - /// - /// to be set to property. - public RoslynCompilationContext(CSharpCompilation compilation) - { - if (compilation == null) - { - throw new ArgumentNullException(nameof(compilation)); - } - - Compilation = compilation; - } - - /// - /// Gets or sets the used for current source file compilation. - /// - public CSharpCompilation Compilation { get; set; } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/SymbolsUtility.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/SymbolsUtility.cs deleted file mode 100644 index bfe2b8841..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/SymbolsUtility.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Runtime.InteropServices; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - /// - /// Utility type for determining if a platform supports full pdb file generation. - /// - internal static class SymbolsUtility - { - // Native pdb writer's CLSID - private const string SymWriterGuid = "0AE2DEB0-F901-478b-BB9F-881EE8066788"; - - /// - /// Determines if the current platform supports full pdb generation. - /// - /// true if full pdb generation is supported; false otherwise. - public static bool SupportsFullPdbGeneration() - { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // Cross-plat always produce portable pdbs. - return false; - } - - if (Type.GetType("Mono.Runtime") != null) - { - return false; - } - - try - { - // Check for the pdb writer component that roslyn uses to generate pdbs - var type = Marshal.GetTypeFromCLSID(new Guid(SymWriterGuid)); - if (type != null) - { - // This line will throw if pdb generation is not supported. - Activator.CreateInstance(type); - return true; - } - } - catch - { - } - - return false; - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ViewsFeatureProvider.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ViewsFeatureProvider.cs index aebfe0008..911b01026 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ViewsFeatureProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/ViewsFeatureProvider.cs @@ -50,7 +50,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation ExpirationTokens = Array.Empty(), RelativePath = relativePath, ViewAttribute = attribute, - IsPrecompiled = true, }; feature.ViewDescriptors.Add(viewDescriptor); diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/DefaultRazorViewEngineFileProviderAccessor.cs b/src/Microsoft.AspNetCore.Mvc.Razor/DefaultRazorViewEngineFileProviderAccessor.cs deleted file mode 100644 index 449527376..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/DefaultRazorViewEngineFileProviderAccessor.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Options; - -namespace Microsoft.AspNetCore.Mvc.Razor -{ - /// - /// Default implementation of . - /// - internal class DefaultRazorViewEngineFileProviderAccessor : IRazorViewEngineFileProviderAccessor - { - /// - /// Initializes a new instance of . - /// - /// Accessor to . - public DefaultRazorViewEngineFileProviderAccessor(IOptions optionsAccessor) - { - var fileProviders = optionsAccessor.Value.FileProviders; - if (fileProviders.Count == 0) - { - FileProvider = new NullFileProvider(); - } - else if (fileProviders.Count == 1) - { - FileProvider = fileProviders[0]; - } - else - { - FileProvider = new CompositeFileProvider(fileProviders); - } - } - - /// - /// Gets the used to look up Razor files. - /// - public IFileProvider FileProvider { get; } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs index c3f638825..46139a28a 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs @@ -2,26 +2,19 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Diagnostics; using System.Linq; -using System.Text.Encodings.Web; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor.Compilation; -using Microsoft.AspNetCore.Mvc.Razor.Extensions; using Microsoft.AspNetCore.Mvc.Razor.Infrastructure; using Microsoft.AspNetCore.Mvc.Razor.TagHelpers; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using CompilationTagHelperFeature = Microsoft.CodeAnalysis.Razor.CompilationTagHelperFeature; -using DefaultTagHelperDescriptorProvider = Microsoft.CodeAnalysis.Razor.DefaultTagHelperDescriptorProvider; namespace Microsoft.Extensions.DependencyInjection { @@ -66,13 +59,6 @@ namespace Microsoft.Extensions.DependencyInjection private static void AddRazorViewEngineFeatureProviders(IMvcCoreBuilder builder) { -#pragma warning disable CS0618 // Type or member is obsolete - if (!builder.PartManager.FeatureProviders.OfType().Any()) - { - builder.PartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider()); - } -#pragma warning restore CS0618 // Type or member is obsolete - if (!builder.PartManager.FeatureProviders.OfType().Any()) { builder.PartManager.FeatureProviders.Add(new TagHelperFeatureProvider()); @@ -148,74 +134,19 @@ namespace Microsoft.Extensions.DependencyInjection // Internal for testing. internal static void AddRazorViewEngineServices(IServiceCollection services) { - services.TryAddSingleton(); -#pragma warning disable CS0618 // Type or member is obsolete - services.TryAddSingleton(); -#pragma warning restore CS0618 // Type or member is obsolete - services.TryAddEnumerable( ServiceDescriptor.Transient, MvcRazorMvcViewOptionsSetup>()); services.TryAddEnumerable( ServiceDescriptor.Transient, RazorViewEngineOptionsSetup>()); - services.TryAddEnumerable( - ServiceDescriptor.Transient, RazorViewEngineOptionsSetup>()); - - services.TryAddSingleton< - IRazorViewEngineFileProviderAccessor, - DefaultRazorViewEngineFileProviderAccessor>(); - - services.TryAddSingleton(s => - { - var pageFactory = s.GetRequiredService(); - var pageActivator = s.GetRequiredService(); - var htmlEncoder = s.GetRequiredService(); - var optionsAccessor = s.GetRequiredService>(); - var razorFileSystem = s.GetRequiredService(); - var loggerFactory = s.GetRequiredService(); - var diagnosticListener = s.GetRequiredService(); - - var viewEngine = new RazorViewEngine(pageFactory, pageActivator, htmlEncoder, optionsAccessor, razorFileSystem, loggerFactory, diagnosticListener); - return viewEngine; - }); + services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(); // In the default scenario the following services are singleton by virtue of being initialized as part of // creating the singleton RazorViewEngine instance. services.TryAddTransient(); - // - // Razor compilation infrastructure - // - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(s => - { - var fileSystem = s.GetRequiredService(); - var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, builder => - { - RazorExtensions.Register(builder); - - // Roslyn + TagHelpers infrastructure - var metadataReferenceFeature = s.GetRequiredService(); - builder.Features.Add(metadataReferenceFeature); - builder.Features.Add(new CompilationTagHelperFeature()); - - // TagHelperDescriptorProviders (actually do tag helper discovery) - builder.Features.Add(new DefaultTagHelperDescriptorProvider()); - builder.Features.Add(new ViewComponentTagHelperDescriptorProvider()); - }); - - return projectEngine; - }); - - // Legacy Razor compilation services - services.TryAddSingleton(s => s.GetRequiredService().FileSystem); - services.TryAddSingleton(); - services.TryAddSingleton(s => s.GetRequiredService().Engine); - // This caches Razor page activation details that are valid for the lifetime of the application. services.TryAddSingleton(); diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/FileProviderRazorProject.cs b/src/Microsoft.AspNetCore.Mvc.Razor/FileProviderRazorProject.cs deleted file mode 100644 index cde9cf1da..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/FileProviderRazorProject.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.Extensions.FileProviders; - -namespace Microsoft.AspNetCore.Mvc.Razor -{ - internal class FileProviderRazorProjectFileSystem : RazorProjectFileSystem - { - private const string RazorFileExtension = ".cshtml"; - private readonly IFileProvider _provider; - private readonly IHostingEnvironment _hostingEnvironment; - - public FileProviderRazorProjectFileSystem(IRazorViewEngineFileProviderAccessor accessor, IHostingEnvironment hostingEnvironment) - { - if (accessor == null) - { - throw new ArgumentNullException(nameof(accessor)); - } - - if (hostingEnvironment == null) - { - throw new ArgumentNullException(nameof(hostingEnvironment)); - } - - _provider = accessor.FileProvider; - _hostingEnvironment = hostingEnvironment; - } - - public override RazorProjectItem GetItem(string path) - { - path = NormalizeAndEnsureValidPath(path); - var fileInfo = _provider.GetFileInfo(path); - - return new FileProviderRazorProjectItem(fileInfo, basePath: string.Empty, filePath: path, root: _hostingEnvironment.ContentRootPath); - } - - public override IEnumerable EnumerateItems(string path) - { - path = NormalizeAndEnsureValidPath(path); - return EnumerateFiles(_provider.GetDirectoryContents(path), path, prefix: string.Empty); - } - - private IEnumerable EnumerateFiles(IDirectoryContents directory, string basePath, string prefix) - { - if (directory.Exists) - { - foreach (var fileInfo in directory) - { - if (fileInfo.IsDirectory) - { - var relativePath = prefix + "/" + fileInfo.Name; - var subDirectory = _provider.GetDirectoryContents(JoinPath(basePath, relativePath)); - var children = EnumerateFiles(subDirectory, basePath, relativePath); - foreach (var child in children) - { - yield return child; - } - } - else if (string.Equals(RazorFileExtension, Path.GetExtension(fileInfo.Name), StringComparison.OrdinalIgnoreCase)) - { - var filePath = prefix + "/" + fileInfo.Name; - - yield return new FileProviderRazorProjectItem(fileInfo, basePath, filePath: filePath, root: _hostingEnvironment.ContentRootPath); - } - } - } - } - - private static string JoinPath(string path1, string path2) - { - var hasTrailingSlash = path1.EndsWith("/", StringComparison.Ordinal); - var hasLeadingSlash = path2.StartsWith("/", StringComparison.Ordinal); - if (hasLeadingSlash && hasTrailingSlash) - { - return path1 + path2.Substring(1); - } - else if (hasLeadingSlash || hasTrailingSlash) - { - return path1 + path2; - } - - return path1 + "/" + path2; - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/FileProviderRazorProjectItem.cs b/src/Microsoft.AspNetCore.Mvc.Razor/FileProviderRazorProjectItem.cs deleted file mode 100644 index 5eb9f24b3..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/FileProviderRazorProjectItem.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.IO; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.Extensions.FileProviders; - -namespace Microsoft.AspNetCore.Mvc.Razor -{ - internal class FileProviderRazorProjectItem : RazorProjectItem - { - private string _root; - private string _relativePhysicalPath; - private bool _isRelativePhysicalPathSet; - - public FileProviderRazorProjectItem(IFileInfo fileInfo, string basePath, string filePath, string root) - { - FileInfo = fileInfo; - BasePath = basePath; - FilePath = filePath; - _root = root; - } - - public IFileInfo FileInfo { get; } - - public override string BasePath { get; } - - public override string FilePath { get; } - - public override bool Exists => FileInfo.Exists; - - public override string PhysicalPath => FileInfo.PhysicalPath; - - public override string RelativePhysicalPath - { - get - { - if (!_isRelativePhysicalPathSet) - { - _isRelativePhysicalPathSet = true; - - if (Exists) - { - if (_root != null && - !string.IsNullOrEmpty(PhysicalPath) && - PhysicalPath.StartsWith(_root, StringComparison.OrdinalIgnoreCase) && - PhysicalPath.Length > _root.Length && - (PhysicalPath[_root.Length] == Path.DirectorySeparatorChar || PhysicalPath[_root.Length] == Path.AltDirectorySeparatorChar)) - { - _relativePhysicalPath = PhysicalPath.Substring(_root.Length + 1); // Include leading separator - } - } - } - - return _relativePhysicalPath; - } - } - - public override Stream Read() - { - return FileInfo.CreateReadStream(); - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/IRazorViewEngineFileProviderAccessor.cs b/src/Microsoft.AspNetCore.Mvc.Razor/IRazorViewEngineFileProviderAccessor.cs deleted file mode 100644 index 19c5fde0c..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/IRazorViewEngineFileProviderAccessor.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.Extensions.FileProviders; - -namespace Microsoft.AspNetCore.Mvc.Razor -{ - /// - /// Accessor to the used by . - /// - internal interface IRazorViewEngineFileProviderAccessor - { - /// - /// Gets the used to look up Razor files. - /// - IFileProvider FileProvider { get; } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/LazyMetadataReferenceFeature.cs b/src/Microsoft.AspNetCore.Mvc.Razor/LazyMetadataReferenceFeature.cs deleted file mode 100644 index a7b86b00f..000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor/LazyMetadataReferenceFeature.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Mvc.Razor.Compilation; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Razor; - -namespace Microsoft.AspNetCore.Mvc.Razor -{ - internal class LazyMetadataReferenceFeature : IMetadataReferenceFeature - { -#pragma warning disable CS0618 // Type or member is obsolete - private readonly RazorReferenceManager _referenceManager; -#pragma warning restore CS0618 // Type or member is obsolete - -#pragma warning disable CS0618 // Type or member is obsolete - public LazyMetadataReferenceFeature(RazorReferenceManager referenceManager) -#pragma warning restore CS0618 // Type or member is obsolete - { - _referenceManager = referenceManager; - } - - /// - /// Invoking ensures that compilation - /// references are lazily evaluated. - /// - public IReadOnlyList References => _referenceManager.CompilationReferences; - - public RazorEngine Engine { get; set; } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.csproj b/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.csproj index 52c7d6814..4296dbb5c 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.csproj +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.csproj @@ -13,10 +13,7 @@ - - - @@ -44,7 +41,6 @@ repositoryUrl=$(RepositoryUrl); repositoryCommit=$(RepositoryCommit); targetframework=$(TargetFramework); - MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion=$(MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion); MicrosoftAspNetCoreRazorRuntimePackageVersion=$(MicrosoftAspNetCoreRazorRuntimePackageVersion); MicrosoftCodeAnalysisRazorPackageVersion=$(MicrosoftCodeAnalysisRazorPackageVersion); MicrosoftCodeAnalysisCSharpPackageVersion=$(MicrosoftCodeAnalysisCSharpPackageVersion); diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.nuspec b/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.nuspec index 9ff2439b9..dc898bf15 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.nuspec +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.nuspec @@ -16,7 +16,6 @@ - @@ -25,7 +24,6 @@ - diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/RazorFileHierarchy.cs b/src/Microsoft.AspNetCore.Mvc.Razor/RazorFileHierarchy.cs new file mode 100644 index 000000000..89f7b43dd --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Razor/RazorFileHierarchy.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.AspNetCore.Mvc.Razor +{ + internal static class RazorFileHierarchy + { + private const string ViewStartFileName = "_ViewStart.cshtml"; + + public static IEnumerable GetViewStartPaths(string path) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(path)); + } + + if (path[0] != '/') + { + throw new ArgumentException(Resources.RazorProject_PathMustStartWithForwardSlash, nameof(path)); + } + + if (path.Length == 1) + { + yield break; + } + + var builder = new StringBuilder(path); + var maxIterations = 255; + var index = path.Length; + while (maxIterations-- > 0 && index > 1 && (index = path.LastIndexOf('/', index - 1)) != -1) + { + builder.Length = index + 1; + builder.Append(ViewStartFileName); + + var itemPath = builder.ToString(); + yield return itemPath; + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs index 026d8c88a..ad87fbb64 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Text.Encodings.Web; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.ViewEngines; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -29,7 +28,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor public class RazorViewEngine : IRazorViewEngine { public static readonly string ViewExtension = ".cshtml"; - private const string ViewStartFileName = "_ViewStart.cshtml"; private const string AreaKey = "area"; private const string ControllerKey = "controller"; @@ -42,19 +40,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor private readonly HtmlEncoder _htmlEncoder; private readonly ILogger _logger; private readonly RazorViewEngineOptions _options; - private readonly RazorProject _razorFileSystem; private readonly DiagnosticListener _diagnosticListener; /// /// Initializes a new instance of the . /// - [Obsolete("This constructor is obsolete and will be removed in a future version.")] public RazorViewEngine( IRazorPageFactoryProvider pageFactory, IRazorPageActivator pageActivator, HtmlEncoder htmlEncoder, IOptions optionsAccessor, - RazorProject razorProject, ILoggerFactory loggerFactory, DiagnosticListener diagnosticListener) { @@ -78,28 +73,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor _pageActivator = pageActivator; _htmlEncoder = htmlEncoder; _logger = loggerFactory.CreateLogger(); - _razorFileSystem = razorProject; _diagnosticListener = diagnosticListener; ViewLookupCache = new MemoryCache(new MemoryCacheOptions()); } - /// - /// Initializes a new instance of the RazorViewEngine - /// - public RazorViewEngine( - IRazorPageFactoryProvider pageFactory, - IRazorPageActivator pageActivator, - HtmlEncoder htmlEncoder, - IOptions optionsAccessor, - RazorProjectFileSystem razorFileSystem, - ILoggerFactory loggerFactory, - DiagnosticListener diagnosticListener) -#pragma warning disable CS0618 // Type or member is obsolete - : this (pageFactory, pageActivator, htmlEncoder, optionsAccessor, (RazorProject)razorFileSystem, loggerFactory, diagnosticListener) -#pragma warning restore CS0618 // Type or member is obsolete - { - } - /// /// A cache for results of view lookups. /// @@ -439,10 +416,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor var viewStartPages = isMainPage ? GetViewStartPages(viewDescriptor.RelativePath, expirationTokens) : Array.Empty(); - if (viewDescriptor.IsPrecompiled) - { - _logger.PrecompiledViewFound(relativePath); - } return new ViewLocationCacheResult( new ViewLocationCacheItem(factoryResult.RazorPageFactory, relativePath), @@ -458,9 +431,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor { var viewStartPages = new List(); - foreach (var viewStartProjectItem in _razorFileSystem.FindHierarchicalItems(path, ViewStartFileName)) + foreach (var filePath in RazorFileHierarchy.GetViewStartPaths(path)) { - var result = _pageFactory.CreateFactory(viewStartProjectItem.FilePath); + var result = _pageFactory.CreateFactory(filePath); var viewDescriptor = result.ViewDescriptor; if (viewDescriptor?.ExpirationTokens != null) { @@ -475,7 +448,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor // Populate the viewStartPages list so that _ViewStarts appear in the order the need to be // executed (closest last, furthest first). This is the reverse order in which // ViewHierarchyUtility.GetViewStartLocations returns _ViewStarts. - viewStartPages.Insert(0, new ViewLocationCacheItem(result.RazorPageFactory, viewStartProjectItem.FilePath)); + viewStartPages.Insert(0, new ViewLocationCacheItem(result.RazorPageFactory, filePath)); } } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptions.cs b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptions.cs index 6c6148cb2..d6100bc4e 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptions.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptions.cs @@ -1,50 +1,20 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Collections; using System.Collections.Generic; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.AspNetCore.Mvc.Razor.Compilation; -using Microsoft.CodeAnalysis; -using Microsoft.Extensions.FileProviders; namespace Microsoft.AspNetCore.Mvc.Razor { /// /// Provides programmatic configuration for the . /// - public class RazorViewEngineOptions : IEnumerable + public class RazorViewEngineOptions { - private readonly ICompatibilitySwitch[] _switches; - private readonly CompatibilitySwitch _allowRecompilingViewsOnFileChange; - private Action _compilationCallback = c => { }; - - public RazorViewEngineOptions() - { - _allowRecompilingViewsOnFileChange = new CompatibilitySwitch(nameof(AllowRecompilingViewsOnFileChange)); - _switches = new[] - { - _allowRecompilingViewsOnFileChange, - }; - } - /// /// Gets a used by the . /// public IList ViewLocationExpanders { get; } = new List(); - /// - /// Gets the sequence of instances used by to - /// locate Razor files. - /// - /// - /// At startup, this is initialized to include an instance of - /// that is rooted at the application root. - /// - public IList FileProviders { get; } = new List(); - /// /// Gets the locations where will search for views. /// @@ -165,81 +135,5 @@ namespace Microsoft.AspNetCore.Mvc.Razor /// /// public IList AreaPageViewLocationFormats { get; } = new List(); - - /// - /// Gets the instances that should be included in Razor compilation, along with - /// those discovered by s. - /// - [Obsolete("This property is obsolete and will be removed in a future version. See https://aka.ms/AA1x4gg for details.")] - public IList AdditionalCompilationReferences { get; } = new List(); - - /// - /// Gets or sets the callback that is used to customize Razor compilation - /// to change compilation settings you can update property. - /// - /// - /// Customizations made here would not reflect in tooling (Intellisense). - /// - [Obsolete("This property is obsolete and will be removed in a future version. See https://aka.ms/AA1x4gg for details.")] - public Action CompilationCallback - { - get => _compilationCallback; - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - _compilationCallback = value; - } - } - - /// - /// Gets or sets a value that determines if Razor files (Razor Views and Razor Pages) are recompiled and updated - /// if files change on disk. - /// - /// When , MVC will use to watch for changes to - /// Razor files in configured instances. - /// - /// - /// - /// The default value is if the version is - /// or earlier. If the version is later and is Development, - /// the default value is . Otherwise, the default value is . - /// - /// - /// - /// This property is associated with a compatibility switch and can provide a different behavior depending on - /// the configured compatibility version for the application. See for - /// guidance and examples of setting the application's compatibility version. - /// - /// - /// Configuring the desired value of the compatibility switch by calling this property's setter will take - /// precedence over the value implied by the application's . - /// - /// - /// If the application's compatibility version is set to or - /// lower then this setting will have the value unless explicitly configured. - /// - /// - /// If the application's compatibility version is set to or - /// higher then this setting will have the value unless - /// is Development or the value is explicitly configured. - /// - /// - public bool AllowRecompilingViewsOnFileChange - { - // Note: When compatibility switches are removed in 3.0, this property should be retained as a regular boolean property. - get => _allowRecompilingViewsOnFileChange.Value; - set => _allowRecompilingViewsOnFileChange.Value = value; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable)_switches).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator(); } } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptionsSetup.cs b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptionsSetup.cs index 6747d8772..d6ffc66da 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptionsSetup.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptionsSetup.cs @@ -2,45 +2,20 @@ // 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 Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Mvc.Razor { - internal class RazorViewEngineOptionsSetup : - ConfigureCompatibilityOptions, - IConfigureOptions + internal class RazorViewEngineOptionsSetup : IConfigureOptions { private readonly IHostingEnvironment _hostingEnvironment; - public RazorViewEngineOptionsSetup( - IHostingEnvironment hostingEnvironment, - ILoggerFactory loggerFactory, - IOptions compatibilityOptions) - : base(loggerFactory, compatibilityOptions) + public RazorViewEngineOptionsSetup(IHostingEnvironment hostingEnvironment) { _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); } - protected override IReadOnlyDictionary DefaultValues - { - get - { - var values = new Dictionary(); - if (Version < CompatibilityVersion.Version_2_2) - { - // Default to true in 2.1 or earlier. In 2.2, we have to conditionally enable this - // and consequently this switch has no default value. - values[nameof(RazorViewEngineOptions.AllowRecompilingViewsOnFileChange)] = true; - } - - return values; - } - } - public void Configure(RazorViewEngineOptions options) { if (options == null) @@ -48,22 +23,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor throw new ArgumentNullException(nameof(options)); } - if (_hostingEnvironment.ContentRootFileProvider != null) - { - options.FileProviders.Add(_hostingEnvironment.ContentRootFileProvider); - } - options.ViewLocationFormats.Add("/Views/{1}/{0}" + RazorViewEngine.ViewExtension); options.ViewLocationFormats.Add("/Views/Shared/{0}" + RazorViewEngine.ViewExtension); options.AreaViewLocationFormats.Add("/Areas/{2}/Views/{1}/{0}" + RazorViewEngine.ViewExtension); options.AreaViewLocationFormats.Add("/Areas/{2}/Views/Shared/{0}" + RazorViewEngine.ViewExtension); options.AreaViewLocationFormats.Add("/Views/Shared/{0}" + RazorViewEngine.ViewExtension); - - if (_hostingEnvironment.IsDevelopment()) - { - options.AllowRecompilingViewsOnFileChange = true; - } } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Mvc.Razor/breakingchanges.netcore.json index ee5093114..c23845969 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/breakingchanges.netcore.json +++ b/src/Microsoft.AspNetCore.Mvc.Razor/breakingchanges.netcore.json @@ -1,4 +1,24 @@ [ + { + "TypeId": "public abstract class Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorReferenceManager", + "Kind": "Removal" + }, + { + "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.Compilation.CompilationFailedException : System.Exception, Microsoft.AspNetCore.Diagnostics.ICompilationException", + "Kind": "Removal" + }, + { + "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.Compilation.MetadataReferenceFeature", + "Kind": "Removal" + }, + { + "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.Compilation.MetadataReferenceFeatureProvider : Microsoft.AspNetCore.Mvc.ApplicationParts.IApplicationFeatureProvider", + "Kind": "Removal" + }, + { + "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.Compilation.RoslynCompilationContext", + "Kind": "Removal" + }, { "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine : Microsoft.AspNetCore.Mvc.Razor.IRazorViewEngine", "MemberId": "public .ctor(Microsoft.AspNetCore.Mvc.Razor.IRazorPageFactoryProvider pageFactory, Microsoft.AspNetCore.Mvc.Razor.IRazorPageActivator pageActivator, System.Text.Encodings.Web.HtmlEncoder htmlEncoder, Microsoft.Extensions.Options.IOptions optionsAccessor, Microsoft.AspNetCore.Razor.Language.RazorProject razorProject, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, System.Diagnostics.DiagnosticSource diagnosticSource)", @@ -13,5 +33,35 @@ "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.RazorView : Microsoft.AspNetCore.Mvc.ViewEngines.IView", "MemberId": "public .ctor(Microsoft.AspNetCore.Mvc.Razor.IRazorViewEngine viewEngine, Microsoft.AspNetCore.Mvc.Razor.IRazorPageActivator pageActivator, System.Collections.Generic.IReadOnlyList viewStartPages, Microsoft.AspNetCore.Mvc.Razor.IRazorPage razorPage, System.Text.Encodings.Web.HtmlEncoder htmlEncoder, System.Diagnostics.DiagnosticSource diagnosticSource)", "Kind": "Removal" + }, + { + "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions", + "MemberId": "public System.Action get_CompilationCallback()", + "Kind": "Removal" + }, + { + "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions", + "MemberId": "public System.Collections.Generic.IList get_AdditionalCompilationReferences()", + "Kind": "Removal" + }, + { + "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions", + "MemberId": "public System.Void set_CompilationCallback(System.Action value)", + "Kind": "Removal" + }, + { + "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions", + "MemberId": "public System.Collections.Generic.IList get_FileProviders()", + "Kind": "Removal" + }, + { + "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.Compilation.CompiledViewDescriptor", + "MemberId": "public System.Boolean get_IsPrecompiled()", + "Kind": "Removal" + }, + { + "TypeId": "public class Microsoft.AspNetCore.Mvc.Razor.Compilation.CompiledViewDescriptor", + "MemberId": "public System.Void set_IsPrecompiled(System.Boolean value)", + "Kind": "Removal" } -] +] \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/CompiledPageRouteModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/CompiledPageRouteModelProvider.cs index 9660bffa2..7471af285 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/CompiledPageRouteModelProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/CompiledPageRouteModelProvider.cs @@ -6,11 +6,9 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Razor.Compilation; -using Microsoft.AspNetCore.Mvc.Razor.Extensions; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Razor.Hosting; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -18,23 +16,20 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels { internal class CompiledPageRouteModelProvider : IPageRouteModelProvider { + private static readonly string RazorPageDocumentKind = "mvc.1.0.razor-page"; + private static readonly string RouteTemplateKey = "RouteTemplate"; private readonly ApplicationPartManager _applicationManager; private readonly RazorPagesOptions _pagesOptions; - private readonly RazorProjectEngine _razorProjectEngine; - private readonly ILogger _logger; private readonly PageRouteModelFactory _routeModelFactory; public CompiledPageRouteModelProvider( ApplicationPartManager applicationManager, IOptions pagesOptionsAccessor, - RazorProjectEngine razorProjectEngine, ILogger logger) { _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager)); _pagesOptions = pagesOptionsAccessor?.Value ?? throw new ArgumentNullException(nameof(pagesOptionsAccessor)); - _razorProjectEngine = razorProjectEngine ?? throw new ArgumentNullException(nameof(razorProjectEngine)); - _logger = logger ?? throw new ArgumentNullException(nameof(razorProjectEngine)); - _routeModelFactory = new PageRouteModelFactory(_pagesOptions, _logger); + _routeModelFactory = new PageRouteModelFactory(_pagesOptions, logger); } public int Order => -1000; @@ -75,11 +70,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels continue; } - if (!viewDescriptor.IsPrecompiled) - { - continue; - } - if (IsRazorPage(viewDescriptor)) { yield return viewDescriptor; @@ -90,7 +80,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels { if (viewDescriptor.Item != null) { - return viewDescriptor.Item.Kind == RazorPageDocumentClassifierPass.RazorPageDocumentKind; + return viewDescriptor.Item.Kind == RazorPageDocumentKind; } else if (viewDescriptor.ViewAttribute != null) { @@ -119,12 +109,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels var areaRootDirectory = "/Areas/"; foreach (var viewDescriptor in GetViewDescriptors(_applicationManager)) { - if (viewDescriptor.Item != null && !ChecksumValidator.IsItemValid(_razorProjectEngine.FileSystem, viewDescriptor.Item)) - { - // If we get here, this compiled Page has different local content, so ignore it. - continue; - } - var relativePath = viewDescriptor.RelativePath; var routeTemplate = GetRouteTemplate(viewDescriptor); PageRouteModel routeModel = null; @@ -158,7 +142,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels { return viewDescriptor.Item.Metadata .OfType() - .FirstOrDefault(f => f.Key == RazorPageDocumentClassifierPass.RouteTemplateKey) + .FirstOrDefault(f => f.Key == RouteTemplateKey) ?.Value; } diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/RazorProjectPageRouteModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/RazorProjectPageRouteModelProvider.cs deleted file mode 100644 index 4f2dcc65c..000000000 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/RazorProjectPageRouteModelProvider.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace Microsoft.AspNetCore.Mvc.ApplicationModels -{ - internal class RazorProjectPageRouteModelProvider : IPageRouteModelProvider - { - private const string AreaRootDirectory = "/Areas"; - private readonly RazorProjectFileSystem _razorFileSystem; - private readonly RazorPagesOptions _pagesOptions; - private readonly PageRouteModelFactory _routeModelFactory; - private readonly ILogger _logger; - - public RazorProjectPageRouteModelProvider( - RazorProjectFileSystem razorFileSystem, - IOptions pagesOptionsAccessor, - ILoggerFactory loggerFactory) - { - _razorFileSystem = razorFileSystem; - _pagesOptions = pagesOptionsAccessor.Value; - _logger = loggerFactory.CreateLogger(); - _routeModelFactory = new PageRouteModelFactory(_pagesOptions, _logger); - } - - /// - /// Ordered to execute after . - /// - public int Order => -1000 + 10; - - public void OnProvidersExecuted(PageRouteModelProviderContext context) - { - } - - public void OnProvidersExecuting(PageRouteModelProviderContext context) - { - // When RootDirectory and AreaRootDirectory overlap, e.g. RootDirectory = /, AreaRootDirectory = /Areas; - // we need to ensure that the page is only route-able via the area route. By adding area routes first, - // we'll ensure non area routes get skipped when it encounters an IsAlreadyRegistered check. - - if (_pagesOptions.AllowAreas) - { - AddAreaPageModels(context); - } - - AddPageModels(context); - } - - private void AddPageModels(PageRouteModelProviderContext context) - { - foreach (var item in _razorFileSystem.EnumerateItems(_pagesOptions.RootDirectory)) - { - var relativePath = item.CombinedPath; - if (context.RouteModels.Any(m => string.Equals(relativePath, m.RelativePath, StringComparison.OrdinalIgnoreCase))) - { - // A route for this file was already registered either by the CompiledPageRouteModel or as an area route. - // by this provider. Skip registering an additional entry. - - // Note: We're comparing duplicates based on root-relative paths. This eliminates a page from being discovered - // by overlapping area and non-area routes where ViewEnginePath would be different. - continue; - } - - if (!PageDirectiveFeature.TryGetPageDirective(_logger, item, out var routeTemplate)) - { - // .cshtml pages without @page are not RazorPages. - continue; - } - - if (_pagesOptions.AllowAreas && relativePath.StartsWith(AreaRootDirectory, StringComparison.OrdinalIgnoreCase)) - { - // Ignore Razor pages that are under the area root directory when AllowAreas is enabled. - // Conforming page paths will be added by AddAreaPageModels. - _logger.UnsupportedAreaPath(relativePath); - continue; - } - - var routeModel = _routeModelFactory.CreateRouteModel(relativePath, routeTemplate); - if (routeModel != null) - { - context.RouteModels.Add(routeModel); - } - } - } - - private void AddAreaPageModels(PageRouteModelProviderContext context) - { - foreach (var item in _razorFileSystem.EnumerateItems(AreaRootDirectory)) - { - var relativePath = item.CombinedPath; - if (context.RouteModels.Any(m => string.Equals(relativePath, m.RelativePath, StringComparison.OrdinalIgnoreCase))) - { - // A route for this file was already registered either by the CompiledPageRouteModel. - // Skip registering an additional entry. - continue; - } - - if (!PageDirectiveFeature.TryGetPageDirective(_logger, item, out var routeTemplate)) - { - // .cshtml pages without @page are not RazorPages. - continue; - } - - var routeModel = _routeModelFactory.CreateAreaRouteModel(relativePath, routeTemplate); - if (routeModel != null) - { - context.RouteModels.Add(routeModel); - } - } - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs index 18a88f803..4de29ac56 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcCoreBuilderExtensions.cs @@ -88,9 +88,6 @@ namespace Microsoft.Extensions.DependencyInjection // Action description and invocation services.TryAddEnumerable( ServiceDescriptor.Singleton()); - services.TryAddSingleton(); - services.TryAddEnumerable( - ServiceDescriptor.Singleton()); services.TryAddEnumerable( ServiceDescriptor.Singleton()); diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorChangeProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorChangeProvider.cs deleted file mode 100644 index 8ede4ccf1..000000000 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorChangeProvider.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Diagnostics; -using System.Linq; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.AspNetCore.Mvc.Razor; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Options; -using Microsoft.Extensions.Primitives; - -namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure -{ - internal class PageActionDescriptorChangeProvider : IActionDescriptorChangeProvider - { - private readonly IFileProvider _fileProvider; - private readonly string[] _searchPatterns; - private readonly string[] _additionalFilesToTrack; - private readonly bool _watchForChanges; - - public PageActionDescriptorChangeProvider( - RazorTemplateEngine templateEngine, - IRazorViewEngineFileProviderAccessor fileProviderAccessor, - IOptions razorPagesOptions, - IOptions razorViewEngineOptions) - { - if (templateEngine == null) - { - throw new ArgumentNullException(nameof(templateEngine)); - } - - if (fileProviderAccessor == null) - { - throw new ArgumentNullException(nameof(fileProviderAccessor)); - } - - if (razorPagesOptions == null) - { - throw new ArgumentNullException(nameof(razorPagesOptions)); - } - - _watchForChanges = razorViewEngineOptions.Value.AllowRecompilingViewsOnFileChange; - if (!_watchForChanges) - { - // No need to do any additional work if we aren't going to be watching for file changes. - return; - } - - _fileProvider = fileProviderAccessor.FileProvider; - - var rootDirectory = razorPagesOptions.Value.RootDirectory; - Debug.Assert(!string.IsNullOrEmpty(rootDirectory)); - rootDirectory = rootDirectory.TrimEnd('/'); - - // Search pattern that matches all cshtml files under the Pages RootDirectory - var pagesRootSearchPattern = rootDirectory + "/**/*.cshtml"; - - // pagesRootSearchPattern will miss _ViewImports outside the RootDirectory despite these influencing - // compilation. e.g. when RootDirectory = /Dir1/Dir2, the search pattern will ignore changes to - // [/_ViewImports.cshtml, /Dir1/_ViewImports.cshtml]. We need to additionally account for these. - var importFileAtPagesRoot = rootDirectory + "/" + templateEngine.Options.ImportsFileName; - var additionalImportFilePaths = templateEngine.GetImportItems(importFileAtPagesRoot) - .Select(item => item.FilePath); - - if (razorPagesOptions.Value.AllowAreas) - { - // Search pattern that matches all cshtml files under the Pages AreaRootDirectory - var areaRootSearchPattern = "/Areas/**/*.cshtml"; - - var importFileAtAreaPagesRoot = $"/Areas/{templateEngine.Options.ImportsFileName}"; - var importPathsOutsideAreaPagesRoot = templateEngine.GetImportItems(importFileAtAreaPagesRoot) - .Select(item => item.FilePath); - - additionalImportFilePaths = additionalImportFilePaths - .Concat(importPathsOutsideAreaPagesRoot) - .Distinct(StringComparer.OrdinalIgnoreCase); - - _searchPatterns = new[] - { - pagesRootSearchPattern, - areaRootSearchPattern - }; - } - else - { - _searchPatterns = new[] { pagesRootSearchPattern, }; - } - - _additionalFilesToTrack = additionalImportFilePaths.ToArray(); - } - - public IChangeToken GetChangeToken() - { - if (!_watchForChanges) - { - return NullChangeToken.Singleton; - } - - var changeTokens = new IChangeToken[_additionalFilesToTrack.Length + _searchPatterns.Length]; - for (var i = 0; i < _additionalFilesToTrack.Length; i++) - { - changeTokens[i] = _fileProvider.Watch(_additionalFilesToTrack[i]); - } - - for (var i = 0; i < _searchPatterns.Length; i++) - { - var wildcardChangeToken = _fileProvider.Watch(_searchPatterns[i]); - changeTokens[_additionalFilesToTrack.Length + i] = wildcardChangeToken; - } - - return new CompositeChangeToken(changeTokens); - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionInvokerProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionInvokerProvider.cs index e5fd4e97c..ffb9f9330 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionInvokerProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionInvokerProvider.cs @@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -36,7 +35,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure private readonly MvcOptions _mvcOptions; private readonly HtmlHelperOptions _htmlHelperOptions; private readonly IPageHandlerMethodSelector _selector; - private readonly RazorProjectFileSystem _razorFileSystem; private readonly DiagnosticListener _diagnosticListener; private readonly ILogger _logger; private readonly IActionResultTypeMapper _mapper; @@ -56,7 +54,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure IOptions mvcOptions, IOptions htmlHelperOptions, IPageHandlerMethodSelector selector, - RazorProjectFileSystem razorFileSystem, DiagnosticListener diagnosticListener, ILoggerFactory loggerFactory, IActionResultTypeMapper mapper) @@ -75,7 +72,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure _mvcOptions = mvcOptions.Value; _htmlHelperOptions = htmlHelperOptions.Value; _selector = selector; - _razorFileSystem = razorFileSystem; _diagnosticListener = diagnosticListener; _logger = loggerFactory.CreateLogger(); _mapper = mapper; @@ -215,12 +211,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure { var viewStartFactories = new List>(); // Always pick up all _ViewStarts, including the ones outside the Pages root. - var viewStartItems = _razorFileSystem.FindHierarchicalItems( - descriptor.RelativePath, - ViewStartFileName); - foreach (var item in viewStartItems) + foreach (var filePath in RazorFileHierarchy.GetViewStartPaths(descriptor.RelativePath)) { - var factoryResult = _razorPageFactoryProvider.CreateFactory(item.FilePath); + var factoryResult = _razorPageFactoryProvider.CreateFactory(filePath); if (factoryResult.Success) { viewStartFactories.Insert(0, factoryResult.RazorPageFactory); diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs deleted file mode 100644 index db310a80a..000000000 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Microsoft.AspNetCore.Mvc.Razor.Extensions; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.Language.Intermediate; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure -{ - public static class PageDirectiveFeature - { - private static readonly RazorProjectEngine PageDirectiveEngine = RazorProjectEngine.Create(RazorConfiguration.Default, new EmptyRazorProjectFileSystem(), builder => - { - for (var i = builder.Phases.Count - 1; i >= 0; i--) - { - var phase = builder.Phases[i]; - builder.Phases.RemoveAt(i); - if (phase is IRazorDocumentClassifierPhase) - { - break; - } - } - - RazorExtensions.Register(builder); - builder.Features.Add(new PageDirectiveParserOptionsFeature()); - }); - - public static bool TryGetPageDirective(ILogger logger, RazorProjectItem projectItem, out string template) - { - if (projectItem == null) - { - throw new ArgumentNullException(nameof(projectItem)); - } - - var codeDocument = PageDirectiveEngine.Process(projectItem); - - var documentIRNode = codeDocument.GetDocumentIntermediateNode(); - if (PageDirective.TryGetPageDirective(documentIRNode, out var pageDirective)) - { - if (pageDirective.DirectiveNode is MalformedDirectiveIntermediateNode malformedNode) - { - logger.MalformedPageDirective(projectItem.FilePath, malformedNode.Diagnostics); - } - - template = pageDirective.RouteTemplate; - return true; - } - - template = null; - return false; - } - - private class PageDirectiveParserOptionsFeature : RazorEngineFeatureBase, IConfigureRazorParserOptionsFeature - { - public int Order { get; } - - public void Configure(RazorParserOptionsBuilder options) - { - options.ParseLeadingDirectives = true; - } - } - - private class EmptyRazorProjectFileSystem : RazorProjectFileSystem - { - public override IEnumerable EnumerateItems(string basePath) - { - return Enumerable.Empty(); - } - - public override IEnumerable FindHierarchicalItems(string basePath, string path, string fileName) - { - return Enumerable.Empty(); - } - - public override RazorProjectItem GetItem(string path) - { - return new NotFoundProjectItem(string.Empty, path); - } - - private class NotFoundProjectItem : RazorProjectItem - { - public NotFoundProjectItem(string basePath, string path) - { - BasePath = basePath; - FilePath = path; - } - - /// - public override string BasePath { get; } - - /// - public override string FilePath { get; } - - /// - public override bool Exists => false; - - /// - public override string PhysicalPath => throw new NotSupportedException(); - - /// - public override Stream Read() => throw new NotSupportedException(); - } - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageLoggerExtensions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageLoggerExtensions.cs index 7d1464263..2407767fc 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageLoggerExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageLoggerExtensions.cs @@ -2,11 +2,9 @@ // 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 Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Mvc.RazorPages @@ -151,20 +149,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages _pageFilterShortCircuit(logger, filter, null); } - public static void MalformedPageDirective(this ILogger logger, string filePath, IList diagnostics) - { - if (logger.IsEnabled(LogLevel.Warning)) - { - var messages = new string[diagnostics.Count]; - for (var i = 0; i < diagnostics.Count; i++) - { - messages[i] = diagnostics[i].GetMessage(); - } - - _malformedPageDirective(logger, filePath, messages, null); - } - } - public static void NotMostEffectiveFilter(this ILogger logger, Type policyType) { _notMostEffectiveFilter(logger, policyType, null); diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Mvc.RazorPages/breakingchanges.netcore.json index 9bb751718..e2fa5d1d6 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/breakingchanges.netcore.json +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/breakingchanges.netcore.json @@ -23,5 +23,9 @@ "TypeId": "public class Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageResultExecutor : Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor", "MemberId": "public .ctor(Microsoft.AspNetCore.Mvc.Infrastructure.IHttpResponseStreamWriterFactory writerFactory, Microsoft.AspNetCore.Mvc.ViewEngines.ICompositeViewEngine compositeViewEngine, Microsoft.AspNetCore.Mvc.Razor.IRazorViewEngine razorViewEngine, Microsoft.AspNetCore.Mvc.Razor.IRazorPageActivator razorPageActivator, System.Diagnostics.DiagnosticSource diagnosticSource, System.Text.Encodings.Web.HtmlEncoder htmlEncoder)", "Kind": "Removal" + }, + { + "TypeId": "public static class Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageDirectiveFeature", + "Kind": "Removal" } ] \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs index 7241fbf2e..985b07e0e 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs @@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests public HttpClient Client { get; } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Mvc/issues/8753")] public async Task CompilationFailuresAreListedByErrorPageMiddleware() { // Arrange @@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.DoesNotContain(PreserveCompilationContextMessage, content); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Mvc/issues/8753")] public async Task ParseFailuresAreListedByErrorPageMiddleware() { // Arrange @@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Contains(expected, content); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Mvc/issues/8753")] public async Task CompilationFailuresFromViewImportsAreListed() { // Arrange @@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Contains(expectedCompilationContent, content); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Mvc/issues/8753")] public async Task RuntimeErrorAreListedByErrorPageMiddleware() { // Arrange diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorBuildTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorBuildTest.cs index 187e9d9ab..08097d522 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorBuildTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorBuildTest.cs @@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal("Hello from buildtime-compiled precompilation view!", responseBody.Trim()); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Mvc/issues/8753")] public async Task Rzc_LocalPageWithDifferentContent_IsUsed() { // Act @@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal("Hello from runtime-compiled rzc page!", responseBody.Trim()); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Mvc/issues/8753")] public async Task Rzc_LocalViewWithDifferentContent_IsUsed() { // Act diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorFileUpdateTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorFileUpdateTests.cs deleted file mode 100644 index b4aee9ad9..000000000 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorFileUpdateTests.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Razor; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.FunctionalTests -{ - // Verifies that updating Razor files (views and pages) with AllowRecompilingViewsOnFileChange=true works - public class RazorFileUpdateTests : IClassFixture> - { - public RazorFileUpdateTests(MvcTestFixture fixture) - { - var factory = fixture.WithWebHostBuilder(builder => - { - builder.UseStartup(); - builder.ConfigureTestServices(services => - { - services.Configure(options => options.AllowRecompilingViewsOnFileChange = true); - }); - }); - Client = factory.CreateDefaultClient(); - } - - public HttpClient Client { get; } - - [Fact] - public async Task RazorViews_AreUpdatedOnChange() - { - // Arrange - var expected1 = "Original content"; - var expected2 = "New content"; - var path = "/Views/UpdateableShared/_Partial.cshtml"; - - // Act - 1 - var body = await Client.GetStringAsync("/UpdateableFileProvider"); - - // Assert - 1 - Assert.Equal(expected1, body.Trim(), ignoreLineEndingDifferences: true); - - // Act - 2 - await UpdateFile(path, expected2); - body = await Client.GetStringAsync("/UpdateableFileProvider"); - - // Assert - 2 - Assert.Equal(expected2, body.Trim(), ignoreLineEndingDifferences: true); - } - - [Fact] - public async Task RazorViews_AreUpdatedWhenViewImportsChange() - { - // Arrange - var content = "@GetType().Assembly.FullName"; - await UpdateFile("/Views/UpdateableIndex/Index.cshtml", content); - var initial = await Client.GetStringAsync("/UpdateableFileProvider"); - - // Act - // Trigger a change in ViewImports - await UpdateFile("/Views/UpdateableIndex/_ViewImports.cshtml", string.Empty); - var updated = await Client.GetStringAsync("/UpdateableFileProvider"); - - // Assert - Assert.NotEqual(initial, updated); - } - - [Fact] - public async Task RazorPages_AreUpdatedOnChange() - { - // Arrange - var expected1 = "Original content"; - var expected2 = "New content"; - - // Act - 1 - var body = await Client.GetStringAsync("/UpdateablePage"); - - // Assert - 1 - Assert.Equal(expected1, body.Trim(), ignoreLineEndingDifferences: true); - - // Act - 2 - await UpdateRazorPages(); - await UpdateFile("/Pages/UpdateablePage.cshtml", "@page" + Environment.NewLine + expected2); - body = await Client.GetStringAsync("/UpdateablePage"); - - // Assert - 2 - Assert.Equal(expected2, body.Trim(), ignoreLineEndingDifferences: true); - } - - [Fact] - public async Task RazorPages_AreUpdatedWhenViewImportsChange() - { - // Arrange - var content = "@GetType().Assembly.FullName"; - await UpdateFile("/Pages/UpdateablePage.cshtml", "@page" + Environment.NewLine + content); - var initial = await Client.GetStringAsync("/UpdateablePage"); - - // Act - // Trigger a change in ViewImports - await UpdateRazorPages(); - await UpdateFile("/Pages/UpdateablePage.cshtml", "@page" + Environment.NewLine + content); - var updated = await Client.GetStringAsync("/UpdateablePage"); - - // Assert - Assert.NotEqual(initial, updated); - } - - private async Task UpdateFile(string path, string content) - { - var updateContent = new FormUrlEncodedContent(new Dictionary - { - { "path", path }, - { "content", content }, - }); - - var response = await Client.PostAsync($"/UpdateableFileProvider/Update", updateContent); - response.EnsureSuccessStatusCode(); - } - - private async Task UpdateRazorPages() - { - var response = await Client.PostAsync($"/UpdateableFileProvider/UpdateRazorPages", new StringContent(string.Empty)); - response.EnsureSuccessStatusCode(); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ViewEngineTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ViewEngineTests.cs index 7c23e4c37..84283fe77 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ViewEngineTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ViewEngineTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Net; using System.Net.Http; using System.Reflection; using System.Threading.Tasks; @@ -66,9 +67,12 @@ ViewWithNestedLayout-Content public async Task RazorView_ExecutesPageAndLayout(string actionName, string expected) { // Arrange & Act - var body = await Client.GetStringAsync("http://localhost/ViewEngine/" + actionName); + var response = await Client.GetAsync("http://localhost/ViewEngine/" + actionName); // Assert + await response.AssertStatusCodeAsync(HttpStatusCode.OK); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true); } @@ -240,17 +244,19 @@ ViewWithNestedLayout-Content public async Task RazorViewEngine_RendersPartialViews(string actionName, string expected) { // Arrange & Act - var body = await Client.GetStringAsync("http://localhost/PartialViewEngine/" + actionName); + var response = await Client.GetAsync("http://localhost/PartialViewEngine/" + actionName); // Assert + await response.AssertStatusCodeAsync(HttpStatusCode.OK); + var body = await response.Content.ReadAsStringAsync(); Assert.Equal(expected, body.Trim(), ignoreLineEndingDifferences: true); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Mvc/issues/8754")] public Task RazorViewEngine_RendersViewsFromEmbeddedFileProvider_WhenLookedupByName() => RazorViewEngine_RendersIndexViewsFromEmbeddedFileProvider("/EmbeddedViews/LookupByName"); - [Fact] + [Fact(Skip = "https://github.com/aspnet/Mvc/issues/8754")] public Task RazorViewEngine_RendersViewsFromEmbeddedFileProvider_WhenLookedupByPath() => RazorViewEngine_RendersIndexViewsFromEmbeddedFileProvider("/EmbeddedViews/LookupByPath"); @@ -479,7 +485,7 @@ Partial"; Assert.Equal(expected, responseContent, ignoreLineEndingDifferences: true); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/Mvc/issues/8754")] public async Task ViewEngine_ResolvesPathsWithSlashesThatDoNotHaveExtensions() { // Arrange diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/CSharpCompilerTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/CSharpCompilerTest.cs deleted file mode 100644 index f25d1fc02..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/CSharpCompilerTest.cs +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq; -using System.Reflection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Emit; -using Microsoft.CodeAnalysis.Text; -using Moq; -using Xunit; -using DependencyContextCompilationOptions = Microsoft.Extensions.DependencyModel.CompilationOptions; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - public class CSharpCompilerTest - { -#pragma warning disable CS0618 // Type or member is obsolete - private readonly RazorReferenceManager ReferenceManager = Mock.Of(); -#pragma warning restore CS0618 // Type or member is obsolete - - [Theory] - [InlineData(null)] - [InlineData("")] - public void GetCompilationOptions_ReturnsDefaultOptionsIfApplicationNameIsNullOrEmpty(string name) - { - // Arrange - var hostingEnvironment = Mock.Of(e => e.ApplicationName == name); - var compiler = new CSharpCompiler(ReferenceManager, hostingEnvironment); - - // Act - var options = compiler.GetDependencyContextCompilationOptions(); - - // Assert - Assert.Same(DependencyContextCompilationOptions.Default, options); - } - - [Fact] - public void GetCompilationOptions_ReturnsDefaultOptionsIfApplicationDoesNotHaveDependencyContext() - { - // Arrange - var hostingEnvironment = new Mock(); - hostingEnvironment.SetupGet(e => e.ApplicationName) - .Returns(typeof(Controller).GetTypeInfo().Assembly.GetName().Name); - var compiler = new CSharpCompiler(ReferenceManager, hostingEnvironment.Object); - - // Act - var options = compiler.GetDependencyContextCompilationOptions(); - - // Assert - Assert.Same(DependencyContextCompilationOptions.Default, options); - } - - [Fact] - public void Constructor_SetsCompilationOptionsFromDependencyContext() - { - // Arrange - var hostingEnvironment = new Mock(); - hostingEnvironment.SetupGet(e => e.ApplicationName) - .Returns(GetType().GetTypeInfo().Assembly.GetName().Name); - var compiler = new CSharpCompiler(ReferenceManager, hostingEnvironment.Object); - - // Act & Assert - var parseOptions = compiler.ParseOptions; - Assert.Contains("SOME_TEST_DEFINE", parseOptions.PreprocessorSymbolNames); - } - - [Theory] - [InlineData("Development", OptimizationLevel.Debug)] - [InlineData("Staging", OptimizationLevel.Release)] - [InlineData("Production", OptimizationLevel.Release)] - public void Constructor_SetsOptimizationLevelBasedOnEnvironment( - string environment, - OptimizationLevel expected) - { - // Arrange - var options = new RazorViewEngineOptions(); - var hostingEnvironment = new Mock(); - hostingEnvironment.SetupGet(e => e.EnvironmentName) - .Returns(environment); - var compiler = new CSharpCompiler(ReferenceManager, hostingEnvironment.Object); - - // Act & Assert - var compilationOptions = compiler.CSharpCompilationOptions; - Assert.Equal(expected, compilationOptions.OptimizationLevel); - } - - [Theory] - [InlineData("Development", "DEBUG")] - [InlineData("Staging", "RELEASE")] - [InlineData("Production", "RELEASE")] - public void EnsureOptions_SetsPreprocessorSymbols(string environment, string expectedConfiguration) - { - // Arrange - var options = new RazorViewEngineOptions(); - var hostingEnvironment = new Mock(); - hostingEnvironment.SetupGet(e => e.EnvironmentName) - .Returns(environment); - var compiler = new CSharpCompiler(ReferenceManager, hostingEnvironment.Object); - - // Act & Assert - var parseOptions = compiler.ParseOptions; - Assert.Equal(new[] { expectedConfiguration }, parseOptions.PreprocessorSymbolNames); - } - - [Fact] - public void EnsureOptions_ConfiguresDefaultCompilationOptions() - { - // Arrange - var hostingEnvironment = Mock.Of(h => h.EnvironmentName == "Development"); - var compiler = new CSharpCompiler(ReferenceManager, hostingEnvironment); - - // Act & Assert - var compilationOptions = compiler.CSharpCompilationOptions; - Assert.False(compilationOptions.AllowUnsafe); - Assert.Equal(ReportDiagnostic.Default, compilationOptions.GeneralDiagnosticOption); - Assert.Equal(OptimizationLevel.Debug, compilationOptions.OptimizationLevel); - Assert.Collection(compilationOptions.SpecificDiagnosticOptions.OrderBy(d => d.Key), - item => - { - Assert.Equal("CS1701", item.Key); - Assert.Equal(ReportDiagnostic.Suppress, item.Value); - }, - item => - { - Assert.Equal("CS1702", item.Key); - Assert.Equal(ReportDiagnostic.Suppress, item.Value); - }, - item => - { - Assert.Equal("CS1705", item.Key); - Assert.Equal(ReportDiagnostic.Suppress, item.Value); - }); - } - - [Fact] - public void EnsureOptions_ConfiguresDefaultParseOptions() - { - // Arrange - var hostingEnvironment = Mock.Of(h => h.EnvironmentName == "Development"); - var compiler = new CSharpCompiler(ReferenceManager, hostingEnvironment); - - // Act & Assert - var parseOptions = compiler.ParseOptions; - Assert.Equal(LanguageVersion.CSharp7, parseOptions.LanguageVersion); - Assert.Equal(new[] { "DEBUG" }, parseOptions.PreprocessorSymbolNames); - } - - [Fact] - public void Constructor_ConfiguresLanguageVersion() - { - // Arrange - var dependencyContextOptions = new DependencyContextCompilationOptions( - new[] { "MyDefine" }, - languageVersion: "7.1", - platform: null, - allowUnsafe: true, - warningsAsErrors: null, - optimize: null, - keyFile: null, - delaySign: null, - publicSign: null, - debugType: null, - emitEntryPoint: null, - generateXmlDocumentation: null); - var hostingEnvironment = Mock.Of(); - - var compiler = new TestCSharpCompiler(ReferenceManager, hostingEnvironment, dependencyContextOptions); - - // Act & Assert - var compilationOptions = compiler.ParseOptions; - Assert.Equal(LanguageVersion.CSharp7_1, compilationOptions.LanguageVersion); - } - - - [Fact] - public void EmitOptions_ReadsDebugTypeFromDependencyContext() - { - // Arrange - var dependencyContextOptions = new DependencyContextCompilationOptions( - new[] { "MyDefine" }, - languageVersion: "7.1", - platform: null, - allowUnsafe: true, - warningsAsErrors: null, - optimize: null, - keyFile: null, - delaySign: null, - publicSign: null, - debugType: "portable", - emitEntryPoint: null, - generateXmlDocumentation: null); - var hostingEnvironment = Mock.Of(); - - var compiler = new TestCSharpCompiler(ReferenceManager, hostingEnvironment, dependencyContextOptions); - - // Act & Assert - var emitOptions = compiler.EmitOptions; - Assert.Equal(DebugInformationFormat.PortablePdb, emitOptions.DebugInformationFormat); - Assert.True(compiler.EmitPdb); - } - - [Fact] - public void EmitOptions_SetsDebugInformationFormatToPortable_WhenDebugTypeIsEmbedded() - { - // Arrange - var dependencyContextOptions = new DependencyContextCompilationOptions( - new[] { "MyDefine" }, - languageVersion: "7.1", - platform: null, - allowUnsafe: true, - warningsAsErrors: null, - optimize: null, - keyFile: null, - delaySign: null, - publicSign: null, - debugType: "embedded", - emitEntryPoint: null, - generateXmlDocumentation: null); - var hostingEnvironment = Mock.Of(); - - var compiler = new TestCSharpCompiler(ReferenceManager, hostingEnvironment, dependencyContextOptions); - - // Act & Assert - var emitOptions = compiler.EmitOptions; - Assert.Equal(DebugInformationFormat.PortablePdb, emitOptions.DebugInformationFormat); - Assert.True(compiler.EmitPdb); - } - - [Fact] - public void EmitOptions_DoesNotSetEmitPdb_IfDebugTypeIsNone() - { - // Arrange - var dependencyContextOptions = new DependencyContextCompilationOptions( - new[] { "MyDefine" }, - languageVersion: "7.1", - platform: null, - allowUnsafe: true, - warningsAsErrors: null, - optimize: null, - keyFile: null, - delaySign: null, - publicSign: null, - debugType: "none", - emitEntryPoint: null, - generateXmlDocumentation: null); - var hostingEnvironment = Mock.Of(); - - var compiler = new TestCSharpCompiler(ReferenceManager, hostingEnvironment, dependencyContextOptions); - - // Act & Assert - Assert.False(compiler.EmitPdb); - } - - [Fact] - public void Constructor_ConfiguresAllowUnsafe() - { - // Arrange - var dependencyContextOptions = new DependencyContextCompilationOptions( - new[] { "MyDefine" }, - languageVersion: null, - platform: null, - allowUnsafe: true, - warningsAsErrors: null, - optimize: null, - keyFile: null, - delaySign: null, - publicSign: null, - debugType: null, - emitEntryPoint: null, - generateXmlDocumentation: null); - var hostingEnvironment = Mock.Of(); - - var compiler = new TestCSharpCompiler(ReferenceManager, hostingEnvironment, dependencyContextOptions); - - // Act & Assert - var compilationOptions = compiler.CSharpCompilationOptions; - Assert.True(compilationOptions.AllowUnsafe); - } - - [Fact] - public void Constructor_SetsDiagnosticOption() - { - // Arrange - var dependencyContextOptions = new DependencyContextCompilationOptions( - new[] { "MyDefine" }, - languageVersion: null, - platform: null, - allowUnsafe: null, - warningsAsErrors: true, - optimize: null, - keyFile: null, - delaySign: null, - publicSign: null, - debugType: null, - emitEntryPoint: null, - generateXmlDocumentation: null); - var hostingEnvironment = Mock.Of(); - - var compiler = new TestCSharpCompiler(ReferenceManager, hostingEnvironment, dependencyContextOptions); - - // Act & Assert - var compilationOptions = compiler.CSharpCompilationOptions; - Assert.Equal(ReportDiagnostic.Error, compilationOptions.GeneralDiagnosticOption); - } - - [Fact] - public void Constructor_SetsOptimizationLevel() - { - // Arrange - var dependencyContextOptions = new DependencyContextCompilationOptions( - new[] { "MyDefine" }, - languageVersion: null, - platform: null, - allowUnsafe: null, - warningsAsErrors: null, - optimize: true, - keyFile: null, - delaySign: null, - publicSign: null, - debugType: null, - emitEntryPoint: null, - generateXmlDocumentation: null); - var hostingEnvironment = Mock.Of(); - - var compiler = new TestCSharpCompiler(ReferenceManager, hostingEnvironment, dependencyContextOptions); - - // Act & Assert - var compilationOptions = compiler.CSharpCompilationOptions; - Assert.Equal(OptimizationLevel.Release, compilationOptions.OptimizationLevel); - } - - [Fact] - public void Constructor_SetsDefines() - { - // Arrange - var dependencyContextOptions = new DependencyContextCompilationOptions( - new[] { "MyDefine" }, - languageVersion: null, - platform: null, - allowUnsafe: null, - warningsAsErrors: null, - optimize: true, - keyFile: null, - delaySign: null, - publicSign: null, - debugType: null, - emitEntryPoint: null, - generateXmlDocumentation: null); - var hostingEnvironment = Mock.Of(); - - var compiler = new TestCSharpCompiler(ReferenceManager, hostingEnvironment, dependencyContextOptions); - - // Act & Assert - var parseOptions = compiler.ParseOptions; - Assert.Equal(new[] { "MyDefine", "RELEASE" }, parseOptions.PreprocessorSymbolNames); - } - - [Fact] - public void Compile_UsesApplicationsCompilationSettings_ForParsingAndCompilation() - { - // Arrange - var content = "public class Test {}"; - var define = "MY_CUSTOM_DEFINE"; - var dependencyContextOptions = new DependencyContextCompilationOptions( - new[] { define }, - languageVersion: null, - platform: null, - allowUnsafe: null, - warningsAsErrors: null, - optimize: true, - keyFile: null, - delaySign: null, - publicSign: null, - debugType: null, - emitEntryPoint: null, - generateXmlDocumentation: null); - var hostingEnvironment = Mock.Of(); - var compiler = new TestCSharpCompiler(ReferenceManager, hostingEnvironment, dependencyContextOptions); - - // Act - var syntaxTree = compiler.CreateSyntaxTree(SourceText.From(content)); - - // Assert - Assert.Contains(define, syntaxTree.Options.PreprocessorSymbolNames); - } - - private class TestCSharpCompiler : CSharpCompiler - { - private readonly DependencyContextCompilationOptions _options; - - public TestCSharpCompiler( -#pragma warning disable CS0618 // Type or member is obsolete - RazorReferenceManager referenceManager, -#pragma warning restore CS0618 // Type or member is obsolete - IHostingEnvironment hostingEnvironment, - DependencyContextCompilationOptions options) - : base(referenceManager, hostingEnvironment) - { - _options = options; - } - - protected internal override DependencyContextCompilationOptions GetDependencyContextCompilationOptions() - => _options; - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/ChecksumValidatorTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/ChecksumValidatorTest.cs deleted file mode 100644 index cfbf962d2..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/ChecksumValidatorTest.cs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Razor.Hosting; -using Microsoft.AspNetCore.Razor.Language; -using Xunit; -using static Microsoft.AspNetCore.Razor.Hosting.TestRazorCompiledItem; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - public class ChecksumValidatorTest - { - public ChecksumValidatorTest() - { - ProjectFileSystem = new VirtualRazorProjectFileSystem(); - } - - public VirtualRazorProjectFileSystem ProjectFileSystem { get; } - - [Fact] - public void IsRecompilationSupported_NoChecksums_ReturnsFalse() - { - // Arrange - var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] { }); - - // Act - var result = ChecksumValidator.IsRecompilationSupported(item); - - // Assert - Assert.False(result); - } - - [Fact] - public void IsRecompilationSupported_NoPrimaryChecksum_ReturnsFalse() - { - // Arrange - var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Views/Home/_ViewImports.cstml"), - }); - - // Act - var result = ChecksumValidator.IsRecompilationSupported(item); - - // Assert - Assert.False(result); - } - - [Fact] - public void IsRecompilationSupported_HasPrimaryChecksum_ReturnsTrue() - { - // Arrange - var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Views/Home/_ViewImports.cstml"), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"), - }); - - // Act - var result = ChecksumValidator.IsRecompilationSupported(item); - - // Assert - Assert.True(result); - } - - [Fact] - public void IsItemValid_NoChecksums_ReturnsTrue() - { - // Arrange - var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] { }); - - // Act - var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item); - - // Assert - Assert.True(result); - } - - [Fact] - public void IsItemValid_NoPrimaryChecksum_ReturnsTrue() - { - // Arrange - var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Views/Home/_ViewImports.cstml"), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/About.cstml"), - }); - - // Act - var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item); - - // Assert - Assert.True(result); - } - - [Fact] - public void IsItemValid_PrimaryFileDoesNotExist_ReturnsTrue() - { - // Arrange - var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Views/Home/_ViewImports.cstml"), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"), - }); - - ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/_ViewImports.cstml", "dkdkfkdf")); // This will be ignored - - // Act - var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item); - - // Assert - Assert.True(result); - } - - [Fact] - public void IsItemValid_PrimaryFileExistsButDoesNotMatch_ReturnsFalse() - { - // Arrange - var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Views/Home/_ViewImports.cstml"), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"), - }); - - ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/Index.cstml", "other content")); - - // Act - var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item); - - // Assert - Assert.False(result); - } - - [Fact] - public void IsItemValid_ImportFileDoesNotExist_ReturnsFalse() - { - // Arrange - var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Views/Home/_ViewImports.cstml"), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"), - }); - - ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/Index.cstml", "some content")); - - // Act - var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item); - - // Assert - Assert.False(result); - } - - [Fact] - public void IsItemValid_ImportFileExistsButDoesNotMatch_ReturnsFalse() - { - // Arrange - var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Views/Home/_ViewImports.cstml"), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"), - }); - - ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/Index.cstml", "some content")); - ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/_ViewImports.cstml", "some other import")); - - // Act - var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item); - - // Assert - Assert.False(result); - } - - [Fact] - public void IsItemValid_AllFilesMatch_ReturnsTrue() - { - // Arrange - var item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Views/Home/Index.cstml", new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some other import"), "/Views/_ViewImports.cstml"), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Views/Home/_ViewImports.cstml"), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Views/Home/Index.cstml"), - }); - - ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/Index.cstml", "some content")); - ProjectFileSystem.Add(new TestRazorProjectItem("/Views/Home/_ViewImports.cstml", "some import")); - ProjectFileSystem.Add(new TestRazorProjectItem("/Views/_ViewImports.cstml", "some other import")); - - // Act - var result = ChecksumValidator.IsItemValid(ProjectFileSystem, item); - - // Assert - Assert.True(result); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/CompilerFailedExceptionFactoryTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/CompilerFailedExceptionFactoryTest.cs deleted file mode 100644 index 5cfca9ca3..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/CompilerFailedExceptionFactoryTest.cs +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; -using System.Linq; -using System.Text; -using Microsoft.AspNetCore.Mvc.Razor.Extensions; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Text; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - public class CompilerFailedExceptionFactoryTest - { - [Fact] - public void GetCompilationFailedResult_ReadsRazorErrorsFromPage() - { - // Arrange - var viewPath = "/Views/Home/Index.cshtml"; - - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem(viewPath, "")); - - var razorEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine; - - var templateEngine = new MvcRazorTemplateEngine(razorEngine, fileSystem); - var codeDocument = templateEngine.CreateCodeDocument(viewPath); - - // Act - var csharpDocument = templateEngine.GenerateCode(codeDocument); - var compilationResult = CompilationFailedExceptionFactory.Create(codeDocument, csharpDocument.Diagnostics); - - // Assert - var failure = Assert.Single(compilationResult.CompilationFailures); - Assert.Equal(viewPath, failure.SourceFilePath); - Assert.Collection(failure.Messages, - message => Assert.StartsWith( - @"Unterminated string literal.", - message.Message), - message => Assert.StartsWith( - @"The explicit expression block is missing a closing "")"" character.", - message.Message)); - } - - [Fact] - public void GetCompilationFailedResult_WithMissingReferences() - { - // Arrange - var expected = "One or more compilation references may be missing. If you're seeing this in a published application, set 'CopyRefAssembliesToPublishDirectory' to true in your project file to ensure files in the refs directory are published."; - var compilation = CSharpCompilation.Create("Test", options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - var syntaxTree = CSharpSyntaxTree.ParseText("@class Test { public string Test { get; set; } }"); - compilation = compilation.AddSyntaxTrees(syntaxTree); - var emitResult = compilation.Emit(new MemoryStream()); - - // Act - var exception = CompilationFailedExceptionFactory.Create( - RazorCodeDocument.Create(RazorSourceDocument.Create("Test", "Index.cshtml"), Enumerable.Empty()), - syntaxTree.ToString(), - "Test", - emitResult.Diagnostics); - - // Assert - Assert.Collection( - exception.CompilationFailures, - failure => Assert.Equal(expected, failure.FailureSummary)); - } - - [Fact] - public void GetCompilationFailedResult_UsesPhysicalPath() - { - // Arrange - var viewPath = "/Views/Home/Index.cshtml"; - var physicalPath = @"x:\myapp\views\home\index.cshtml"; - - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem(viewPath, "", physicalPath: physicalPath)); - - var razorEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine; - var templateEngine = new MvcRazorTemplateEngine(razorEngine, fileSystem); - - var codeDocument = templateEngine.CreateCodeDocument(viewPath); - - // Act - var csharpDocument = templateEngine.GenerateCode(codeDocument); - var compilationResult = CompilationFailedExceptionFactory.Create(codeDocument, csharpDocument.Diagnostics); - - // Assert - var failure = Assert.Single(compilationResult.CompilationFailures); - Assert.Equal(physicalPath, failure.SourceFilePath); - } - - [Fact] - public void GetCompilationFailedResult_ReadsContentFromSourceDocuments() - { - // Arrange - var viewPath = "/Views/Home/Index.cshtml"; - var fileContent = -@" -@if (User.IsAdmin) -{ - -} -"; - - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem(viewPath, fileContent)); - - var razorEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine; - var templateEngine = new MvcRazorTemplateEngine(razorEngine, fileSystem); - - var codeDocument = templateEngine.CreateCodeDocument(viewPath); - - // Act - var csharpDocument = templateEngine.GenerateCode(codeDocument); - var compilationResult = CompilationFailedExceptionFactory.Create(codeDocument, csharpDocument.Diagnostics); - - // Assert - var failure = Assert.Single(compilationResult.CompilationFailures); - Assert.Equal(fileContent, failure.SourceFileContent); - } - - [Fact] - public void GetCompilationFailedResult_ReadsContentFromImports() - { - // Arrange - var viewPath = "/Views/Home/Index.cshtml"; - var importsPath = "/Views/_MyImports.cshtml"; - var fileContent = "@ "; - var importsContent = "@(abc"; - - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem(viewPath, fileContent)); - fileSystem.Add(new TestRazorProjectItem("/Views/_MyImports.cshtml", importsContent)); - - var razorEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine; - var templateEngine = new MvcRazorTemplateEngine(razorEngine, fileSystem) - { - Options = - { - ImportsFileName = "_MyImports.cshtml", - } - }; - var codeDocument = templateEngine.CreateCodeDocument(viewPath); - - // Act - var csharpDocument = templateEngine.GenerateCode(codeDocument); - var compilationResult = CompilationFailedExceptionFactory.Create(codeDocument, csharpDocument.Diagnostics); - - // Assert - Assert.Collection( - compilationResult.CompilationFailures, - failure => - { - Assert.Equal(viewPath, failure.SourceFilePath); - Assert.Collection(failure.Messages, - message => - { - Assert.Equal(@"A space or line break was encountered after the ""@"" character. Only valid identifiers, keywords, comments, ""("" and ""{"" are valid at the start of a code block and they must occur immediately following ""@"" with no space in between.", - message.Message); - }); - }, - failure => - { - Assert.Equal(importsPath, failure.SourceFilePath); - Assert.Collection(failure.Messages, - message => - { - Assert.Equal(@"The explicit expression block is missing a closing "")"" character. Make sure you have a matching "")"" character for all the ""("" characters within this block, and that none of the "")"" characters are being interpreted as markup.", - message.Message); - }); - }); - } - - [Fact] - public void GetCompilationFailedResult_GroupsMessages() - { - // Arrange - var viewPath = "views/index.razor"; - var viewImportsPath = "views/global.import.cshtml"; - var codeDocument = RazorCodeDocument.Create( - Create(viewPath, "View Content"), - new[] { Create(viewImportsPath, "Global Import Content") }); - var diagnostics = new[] - { - GetRazorDiagnostic("message-1", new SourceLocation(1, 2, 17), length: 1), - GetRazorDiagnostic("message-2", new SourceLocation(viewPath, 1, 4, 6), length: 7), - GetRazorDiagnostic("message-3", SourceLocation.Undefined, length: -1), - GetRazorDiagnostic("message-4", new SourceLocation(viewImportsPath, 1, 3, 8), length: 4), - }; - - // Act - var result = CompilationFailedExceptionFactory.Create(codeDocument, diagnostics); - - // Assert - Assert.Collection(result.CompilationFailures, - failure => - { - Assert.Equal(viewPath, failure.SourceFilePath); - Assert.Equal("View Content", failure.SourceFileContent); - Assert.Collection(failure.Messages, - message => - { - Assert.Equal(diagnostics[0].GetMessage(), 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(diagnostics[1].GetMessage(), 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(diagnostics[2].GetMessage(), message.Message); - Assert.Equal(viewPath, message.SourceFilePath); - Assert.Equal(0, message.StartLine); - Assert.Equal(-1, message.StartColumn); - Assert.Equal(0, message.EndLine); - Assert.Equal(-2, message.EndColumn); - }); - }, - failure => - { - Assert.Equal(viewImportsPath, failure.SourceFilePath); - Assert.Equal("Global Import Content", failure.SourceFileContent); - Assert.Collection(failure.Messages, - message => - { - Assert.Equal(diagnostics[3].GetMessage(), message.Message); - Assert.Equal(viewImportsPath, message.SourceFilePath); - Assert.Equal(4, message.StartLine); - Assert.Equal(8, message.StartColumn); - Assert.Equal(4, message.EndLine); - Assert.Equal(12, message.EndColumn); - }); - }); - } - - [Fact] - public void GetCompilationFailedResult_ReturnsCompilationResult_WithGroupedMessages() - { - // Arrange - var viewPath = "Views/Home/Index"; - var generatedCodeFileName = "Generated Code"; - var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("view-content", viewPath)); - var assemblyName = "random-assembly-name"; - - var diagnostics = new[] - { - Diagnostic.Create( - GetRoslynDiagnostic("message-1"), - Location.Create( - viewPath, - new TextSpan(10, 5), - new LinePositionSpan(new LinePosition(10, 1), new LinePosition(10, 2)))), - Diagnostic.Create( - GetRoslynDiagnostic("message-2"), - Location.Create( - assemblyName, - new TextSpan(1, 6), - new LinePositionSpan(new LinePosition(1, 2), new LinePosition(3, 4)))), - Diagnostic.Create( - GetRoslynDiagnostic("message-3"), - Location.Create( - viewPath, - new TextSpan(40, 50), - new LinePositionSpan(new LinePosition(30, 5), new LinePosition(40, 12)))), - }; - - // Act - var compilationResult = CompilationFailedExceptionFactory.Create( - codeDocument, - "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 RazorSourceDocument Create(string path, string template) - { - var stream = new MemoryStream(Encoding.UTF8.GetBytes(template)); - return RazorSourceDocument.ReadFrom(stream, path); - } - - private static RazorDiagnostic GetRazorDiagnostic(string message, SourceLocation sourceLocation, int length) - { - var diagnosticDescriptor = new RazorDiagnosticDescriptor("test-id", () => message, RazorDiagnosticSeverity.Error); - var sourceSpan = new SourceSpan(sourceLocation, length); - - return RazorDiagnostic.Create(diagnosticDescriptor, sourceSpan); - } - - private static DiagnosticDescriptor GetRoslynDiagnostic(string messageFormat) - { - return new DiagnosticDescriptor( - id: "someid", - title: "sometitle", - messageFormat: messageFormat, - category: "some-category", - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/DefaultRazorReferenceManagerTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/DefaultRazorReferenceManagerTest.cs deleted file mode 100644 index 6e634fd96..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/DefaultRazorReferenceManagerTest.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq; -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.CodeAnalysis; -using Microsoft.Extensions.Options; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ -#pragma warning disable CS0618 // Type or member is obsolete - public class DefaultRazorReferenceManagerTest - { - [Fact] - public void GetCompilationReferences_CombinesApplicationPartAndOptionMetadataReferences() - { - // Arrange - var options = new RazorViewEngineOptions(); - var objectAssemblyLocation = typeof(object).GetTypeInfo().Assembly.Location; - var objectAssemblyMetadataReference = MetadataReference.CreateFromFile(objectAssemblyLocation); - options.AdditionalCompilationReferences.Add(objectAssemblyMetadataReference); - - var applicationPartManager = GetApplicationPartManager(); - var feature = new MetadataReferenceFeature(); - applicationPartManager.PopulateFeature(feature); - var partReferences = feature.MetadataReferences; - var expectedReferenceDisplays = partReferences - .Concat(new[] { objectAssemblyMetadataReference }) - .Select(r => r.Display); - var referenceManager = new DefaultRazorReferenceManager( - applicationPartManager, - Options.Create(options)); - - // Act - var references = referenceManager.CompilationReferences; - var referenceDisplays = references.Select(reference => reference.Display); - - // Assert - Assert.Equal(expectedReferenceDisplays, referenceDisplays); - } - - private static ApplicationPartManager GetApplicationPartManager() - { - var applicationPartManager = new ApplicationPartManager(); - var assembly = typeof(DefaultRazorReferenceManagerTest).GetTypeInfo().Assembly; - applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly)); - applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider()); - - return applicationPartManager; - } - } -#pragma warning restore CS0618 // Type or member is obsolete -} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/ExpressionRewriterTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/ExpressionRewriterTest.cs deleted file mode 100644 index 7dd6bc25d..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/ExpressionRewriterTest.cs +++ /dev/null @@ -1,576 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - public class ExpressionRewriterTest - { - [Fact] - public void ExpressionRewriter_DoesNotThrowsOnUnknownTypes() - { - // Arrange - var source = @" -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Razor; - -public class ExamplePage : RazorPage -{ - public IViewComponentHelper Component { get; set; } - - public override async Task ExecuteAsync() - { - Write( - await Component.InvokeAsync( - ""SomeComponent"", - item => new HelperResult((__razor_template_writer) => WriteLiteralTo(__razor_template_writer, ""Hello World"")))); - } - } -"; - var tree = CSharpSyntaxTree.ParseText(source); - - // Allow errors here because of an anomaly where Roslyn (depending on code sample) will finish compilation - // without diagnostic errors. This test case replicates that scenario by allowing a semantic model with - // errors to be visited by the expression rewriter to validate unexpected exceptions aren't thrown. - // Error created: "Cannot convert lambda expression to type 'object' because it is not a delegate type." - var compilation = Compile(tree, allowErrors: true); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - var rewriter = new ExpressionRewriter(semanticModel); - var root = tree.GetRoot(); - - // Act - var result = rewriter.Visit(root); - - // Assert - Assert.True(root.IsEquivalentTo(result)); - } - - [Fact] - public void ExpressionRewriter_CanRewriteExpression_IdentityExpression() - { - // Arrange - var source = @" -using System; -using System.Linq.Expressions; -public class Program -{ - public static void CalledWithExpression(Expression> expression) - { - } - - public static void Main(string[] args) - { - CalledWithExpression(x => x); - } -} -"; - - var tree = CSharpSyntaxTree.ParseText(source); - var compilation = Compile(tree); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - - var rewriter = new ExpressionRewriter(semanticModel); - - // Act - var result = rewriter.Visit(tree.GetRoot()); - - // Assert - var fields = FindFields(result); - - var field = Assert.Single(fields); - Assert.Collection( - field.Modifiers, - m => Assert.Equal("private", m.ToString()), - m => Assert.Equal("static", m.ToString()), - m => Assert.Equal("readonly", m.ToString())); - - var declaration = field.Declaration; - Assert.Equal( - "global::System.Linq.Expressions.Expression>", - declaration.Type.ToString()); - - var variable = Assert.Single(declaration.Variables); - Assert.Equal("__h0", variable.Identifier.ToString()); - Assert.Equal("x => x", variable.Initializer.Value.ToString()); - - var arguments = FindArguments(result); - var argument = Assert.IsType(Assert.Single(arguments.Arguments).Expression); - Assert.Equal("__h0", argument.Identifier.ToString()); - } - - [Fact] - public void ExpressionRewriter_CanRewriteExpression_MemberAccessExpression() - { - // Arrange - var source = @" -using System; -using System.Linq.Expressions; -public class Program -{ - public static void CalledWithExpression(Expression> expression) - { - } - - public static void Main(string[] args) - { - CalledWithExpression(x => x.Name); - } -} - -public class Person -{ - public string Name { get; set; } -} -"; - - var tree = CSharpSyntaxTree.ParseText(source); - var compilation = Compile(tree); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - - var rewriter = new ExpressionRewriter(semanticModel); - - // Act - var result = rewriter.Visit(tree.GetRoot()); - - // Assert - var fields = FindFields(result); - - var field = Assert.Single(fields); - Assert.Collection( - field.Modifiers, - m => Assert.Equal("private", m.ToString()), - m => Assert.Equal("static", m.ToString()), - m => Assert.Equal("readonly", m.ToString())); - - var declaration = field.Declaration; - Assert.Equal( - "global::System.Linq.Expressions.Expression>", - declaration.Type.ToString()); - - var variable = Assert.Single(declaration.Variables); - Assert.Equal("__h0", variable.Identifier.ToString()); - Assert.Equal("x => x.Name", variable.Initializer.Value.ToString()); - - var arguments = FindArguments(result); - var argument = Assert.IsType(Assert.Single(arguments.Arguments).Expression); - Assert.Equal("__h0", argument.Identifier.ToString()); - } - - [Fact] - public void ExpressionRewriter_CanRewriteExpression_ChainedMemberAccessExpression() - { - // Arrange - var source = @" -using System; -using System.Linq.Expressions; -public class Program -{ - public static void CalledWithExpression(Expression> expression) - { - } - - public static void Main(string[] args) - { - CalledWithExpression(x => x.Name.Length); - } -} - -public class Person -{ - public string Name { get; set; } -} -"; - - var tree = CSharpSyntaxTree.ParseText(source); - var compilation = Compile(tree); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - - var rewriter = new ExpressionRewriter(semanticModel); - - // Act - var result = rewriter.Visit(tree.GetRoot()); - - // Assert - var fields = FindFields(result); - - var field = Assert.Single(fields); - Assert.Collection( - field.Modifiers, - m => Assert.Equal("private", m.ToString()), - m => Assert.Equal("static", m.ToString()), - m => Assert.Equal("readonly", m.ToString())); - - var declaration = field.Declaration; - Assert.Equal( - "global::System.Linq.Expressions.Expression>", - declaration.Type.ToString()); - - var variable = Assert.Single(declaration.Variables); - Assert.Equal("__h0", variable.Identifier.ToString()); - Assert.Equal("x => x.Name.Length", variable.Initializer.Value.ToString()); - - var arguments = FindArguments(result); - var argument = Assert.IsType(Assert.Single(arguments.Arguments).Expression); - Assert.Equal("__h0", argument.Identifier.ToString()); - } - - [Fact] - public void ExpressionRewriter_CannotRewriteExpression_MethodCall() - { - // Arrange - var source = @" -using System; -using System.Linq.Expressions; -public class Program -{ - public static void CalledWithExpression(Expression> expression) - { - } - - public static void Main(string[] args) - { - CalledWithExpression(x => x.GetHashCode()); - } -} -"; - - var tree = CSharpSyntaxTree.ParseText(source); - var compilation = Compile(tree); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - - var rewriter = new ExpressionRewriter(semanticModel); - - // Act - var result = rewriter.Visit(tree.GetRoot()); - - // Assert - Assert.Empty(FindFields(result)); - } - - [Fact] - public void ExpressionRewriter_CannotRewriteExpression_NonArgument() - { - // Arrange - var source = @" -using System; -using System.Linq.Expressions; -public class Program -{ - public static void CalledWithExpression(Expression> expression) - { - } - - public static void Main(string[] args) - { - Expression> expr = x => x.GetHashCode(); - CalledWithExpression(expr); - } -} -"; - - var tree = CSharpSyntaxTree.ParseText(source); - var compilation = Compile(tree); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - - var rewriter = new ExpressionRewriter(semanticModel); - - // Act - var result = rewriter.Visit(tree.GetRoot()); - - // Assert - Assert.Empty(FindFields(result)); - } - - [Fact] - public void ExpressionRewriter_CannotRewriteExpression_NestedClass() - { - // Arrange - var source = @" -using System; -using System.Linq.Expressions; -public class Program -{ - private class Nested - { - public static void CalledWithExpression(Expression> expression) - { - } - - public static void Main(string[] args) - { - Expression> expr = x => x.GetHashCode(); - CalledWithExpression(expr); - } - } -} -"; - - var tree = CSharpSyntaxTree.ParseText(source); - var compilation = Compile(tree); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - - var rewriter = new ExpressionRewriter(semanticModel); - - // Act - var result = rewriter.Visit(tree.GetRoot()); - - // Assert - Assert.Empty(FindFields(result)); - } - - [Fact] - public void ExpressionRewriter_CanRewriteExpression_AdditionalArguments() - { - // Arrange - var source = @" -using System; -using System.Linq.Expressions; -public class Program -{ - public static void CalledWithExpression(int x, Expression> expression, string name) - { - } - - public static void Main(string[] args) - { - CalledWithExpression(5, x => x, ""Billy""); - } -} -"; - - var tree = CSharpSyntaxTree.ParseText(source); - var compilation = Compile(tree); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - - var rewriter = new ExpressionRewriter(semanticModel); - - // Act - var result = rewriter.Visit(tree.GetRoot()); - - // Assert - var fields = FindFields(result); - - var field = Assert.Single(fields); - Assert.Collection( - field.Modifiers, - m => Assert.Equal("private", m.ToString()), - m => Assert.Equal("static", m.ToString()), - m => Assert.Equal("readonly", m.ToString())); - - var declaration = field.Declaration; - Assert.Equal( - "global::System.Linq.Expressions.Expression>", - declaration.Type.ToString()); - - var variable = Assert.Single(declaration.Variables); - Assert.Equal("__h0", variable.Identifier.ToString()); - Assert.Equal("x => x", variable.Initializer.Value.ToString()); - - var arguments = FindArguments(result); - Assert.Equal(3, arguments.Arguments.Count); - var argument = Assert.IsType(arguments.Arguments[1].Expression); - Assert.Equal("__h0", argument.Identifier.ToString()); - } - - // When we rewrite the expression, we want to maintain the original span as much as possible. - [Fact] - public void ExpressionRewriter_CanRewriteExpression_SimpleFormatting() - { - // Arrange - var source = @" -using System; -using System.Linq.Expressions; -public class Program -{ - public static void CalledWithExpression(Expression> expression) - { - } - - public static void Main(string[] args) - { - CalledWithExpression(x => x.Name.Length); - } -} - -public class Person -{ - public string Name { get; set; } -} -"; - - var tree = CSharpSyntaxTree.ParseText(source); - - var originalArguments = FindArguments(tree.GetRoot()); - var originalSpan = originalArguments.GetLocation().GetMappedLineSpan(); - - var compilation = Compile(tree); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - - var rewriter = new ExpressionRewriter(semanticModel); - - // Act - var result = rewriter.Visit(tree.GetRoot()); - - // Assert - var arguments = FindArguments(result); - Assert.Equal(originalSpan, arguments.GetLocation().GetMappedLineSpan()); - } - - // When we rewrite the expression, we want to maintain the original span as much as possible. - [Fact] - public void ExpressionRewriter_CanRewriteExpression_ComplexFormatting() - { - // Arrange - var source = @" -using System; -using System.Linq.Expressions; -public class Program -{ - public static void CalledWithExpression(int z, Expression> expression) - { - } - - public static void Main(string[] args) - { - CalledWithExpression( - 17, - x => - x.Name. - Length - ); - } -} - -public class Person -{ - public string Name { get; set; } -} -"; - - var tree = CSharpSyntaxTree.ParseText(source); - - var originalArguments = FindArguments(tree.GetRoot()); - var originalSpan = originalArguments.GetLocation().GetMappedLineSpan(); - - var compilation = Compile(tree); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - - var rewriter = new ExpressionRewriter(semanticModel); - - // Act - var result = rewriter.Visit(tree.GetRoot()); - - // Assert - var arguments = FindArguments(result); - Assert.Equal(originalSpan, arguments.GetLocation().GetMappedLineSpan()); - } - - [Fact] - public void ExpressionRewriter_CanRewriteExpression_BadlyIndentedFormatting() - { - // Arrange - var source = @" -using System; -using System.Linq.Expressions; -public class Program -{ - public static void CalledWithExpression(Expression> expression) - { - } - - public static void Main(string[] args) - { - CalledWithExpression(x => - x.Name. - Length); - } -} - -public class Person -{ - public string Name { get; set; } -} -"; - - var tree = CSharpSyntaxTree.ParseText(source); - - var originalArguments = FindArguments(tree.GetRoot()); - var originalSpan = originalArguments.GetLocation().GetMappedLineSpan(); - - var compilation = Compile(tree); - var semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility: true); - - var rewriter = new ExpressionRewriter(semanticModel); - - // Act - var result = rewriter.Visit(tree.GetRoot()); - - // Assert - var arguments = FindArguments(result); - Assert.Equal(originalSpan, arguments.GetLocation().GetMappedLineSpan()); - } - - public ArgumentListSyntax FindArguments(SyntaxNode node) - { - return node - .DescendantNodes(n => true) - .Where(n => n.IsKind(SyntaxKind.ArgumentList)) - .Cast() - .Single(); - } - - public IEnumerable FindFields(SyntaxNode node) - { - return node - .DescendantNodes(n => true) - .Where(n => n.IsKind(SyntaxKind.FieldDeclaration)) - .Cast(); - } - - private CSharpCompilation Compile(SyntaxTree tree, bool allowErrors = false) - { - // Disable 1702 until roslyn turns this off by default - var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) - .WithSpecificDiagnosticOptions(new Dictionary - { - { "CS1701", ReportDiagnostic.Suppress }, // Binding redirects - { "CS1702", ReportDiagnostic.Suppress }, - { "CS1705", ReportDiagnostic.Suppress } - }); - - var compilation = CSharpCompilation.Create( - "Test.Assembly", - new[] { tree }, - GetReferences(), - options: options); - - if (!allowErrors) - { - var diagnostics = compilation.GetDiagnostics(); - Assert.True(diagnostics.Length == 0, string.Join(Environment.NewLine, diagnostics)); - } - - return compilation; - } - - private IEnumerable GetReferences() - { - var types = new[] - { - typeof(System.Linq.Expressions.Expression), - typeof(string), - }; - - return types.Select(t => MetadataReference.CreateFromFile(t.GetTypeInfo().Assembly.Location)); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/MetadataReferenceFeatureProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/MetadataReferenceFeatureProviderTest.cs deleted file mode 100644 index 8f9653c70..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/MetadataReferenceFeatureProviderTest.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Moq; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ -#pragma warning disable CS0618 // Type or member is obsolete - public class MetadataReferenceFeatureProviderTest - { - [Fact] - public void PopulateFeature_ReturnsEmptyList_IfNoAssemblyPartsAreRegistered() - { - // Arrange - var applicationPartManager = new ApplicationPartManager(); - applicationPartManager.ApplicationParts.Add(Mock.Of()); - applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider()); - var feature = new MetadataReferenceFeature(); - - // Act - applicationPartManager.PopulateFeature(feature); - - // Assert - Assert.Empty(feature.MetadataReferences); - } - - [Fact] - public void PopulateFeature_AddsMetadataReferenceForAssemblyPartsWithDependencyContext() - { - // Arrange - var applicationPartManager = new ApplicationPartManager(); - var currentAssembly = GetType().GetTypeInfo().Assembly; - var assemblyPart1 = new AssemblyPart(currentAssembly); - applicationPartManager.ApplicationParts.Add(assemblyPart1); - var assemblyPart2 = new AssemblyPart(typeof(MetadataReferenceFeatureProvider).GetTypeInfo().Assembly); - applicationPartManager.ApplicationParts.Add(assemblyPart2); - applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider()); - var feature = new MetadataReferenceFeature(); - - // Act - applicationPartManager.PopulateFeature(feature); - - // Assert - Assert.Contains( - feature.MetadataReferences, - reference => reference.Display.Equals(currentAssembly.Location)); - } - } -#pragma warning restore CS0618 // Type or member is obsolete -} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/RazorViewCompilerProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/RazorViewCompilerProviderTest.cs deleted file mode 100644 index e6e23255b..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/RazorViewCompilerProviderTest.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Moq; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.Razor.Compilation -{ - public class RazorViewCompilerProviderTest - { - [Fact] - public void GetCompiler_ThrowsIfNullFileProvider() - { - // Arrange - var expected = - $"'{typeof(RazorViewEngineOptions).FullName}.{nameof(RazorViewEngineOptions.FileProviders)}' must " + - $"not be empty. At least one '{typeof(IFileProvider).FullName}' is required to locate a view for " + - "rendering."; - var fileProvider = new NullFileProvider(); - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var partManager = new ApplicationPartManager(); - var options = Options.Create(new RazorViewEngineOptions()); - - var referenceManager = new DefaultRazorReferenceManager(partManager, options); - - var provider = new RazorViewCompilerProvider( - partManager, - RazorProjectEngine.Create( - RazorConfiguration.Default, - new FileProviderRazorProjectFileSystem(accessor, Mock.Of())), - accessor, - new CSharpCompiler(referenceManager, Mock.Of()), - options, - new RazorViewCompilationMemoryCacheProvider(), - NullLoggerFactory.Instance); - - // Act & Assert - var exception = Assert.Throws( - () => provider.GetCompiler()); - Assert.Equal(expected, exception.Message); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/RazorViewCompilerTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/RazorViewCompilerTest.cs index 2fb6d3d40..ac26f8e66 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/RazorViewCompilerTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Compilation/RazorViewCompilerTest.cs @@ -3,22 +3,9 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.AspNetCore.Mvc.Razor.Extensions; -using Microsoft.AspNetCore.Razor.Hosting; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Emit; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Moq; using Xunit; -using static Microsoft.AspNetCore.Razor.Hosting.TestRazorCompiledItem; namespace Microsoft.AspNetCore.Mvc.Razor.Compilation { @@ -29,249 +16,51 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation { // Arrange var path = "/file/does-not-exist"; - var fileProvider = new TestFileProvider(); - var viewCompiler = GetViewCompiler(fileProvider); + var viewCompiler = GetViewCompiler(); // Act var result1 = await viewCompiler.CompileAsync(path); var result2 = await viewCompiler.CompileAsync(path); // Assert - Assert.Same(result1, result2); Assert.Null(result1.ViewAttribute); Assert.Empty(result1.ExpirationTokens); } [Fact] - public async Task CompileAsync_ReturnsResultWithExpirationToken_WhenWatchingForFileChanges() - { - // Arrange - var path = "/file/does-not-exist"; - var fileProvider = new TestFileProvider(); - var viewCompiler = GetViewCompiler(fileProvider); - viewCompiler.AllowRecompilingViewsOnFileChange = true; - - // Act - var result1 = await viewCompiler.CompileAsync(path); - var result2 = await viewCompiler.CompileAsync(path); - - // Assert - Assert.Same(result1, result2); - Assert.Null(result1.ViewAttribute); - Assert.Collection( - result1.ExpirationTokens, - token => Assert.Equal(fileProvider.GetChangeToken(path), token)); - } - - [Fact] - public async Task CompileAsync_AddsChangeTokensForViewStartsIfFileExists() - { - // Arrange - var path = "/file/exists/FilePath.cshtml"; - var fileProvider = new TestFileProvider(); - fileProvider.AddFile(path, "Content"); - var viewCompiler = GetViewCompiler(fileProvider); - - // Act - var result = await viewCompiler.CompileAsync(path); - - // Assert - Assert.NotNull(result.ViewAttribute); - Assert.Empty(result.ExpirationTokens); - } - - [Fact] - public async Task CompileAsync_AddsChangeTokensForViewStartsIfFileExists_WhenWatchingForFileChanges() - { - // Arrange - var path = "/file/exists/FilePath.cshtml"; - var fileProvider = new TestFileProvider(); - fileProvider.AddFile(path, "Content"); - var viewCompiler = GetViewCompiler(fileProvider); - viewCompiler.AllowRecompilingViewsOnFileChange = true; - - // Act - var result = await viewCompiler.CompileAsync(path); - - // Assert - Assert.NotNull(result.ViewAttribute); - Assert.Collection( - result.ExpirationTokens, - token => Assert.Same(fileProvider.GetChangeToken(path), token), - token => Assert.Same(fileProvider.GetChangeToken("/_ViewImports.cshtml"), token), - token => Assert.Same(fileProvider.GetChangeToken("/file/_ViewImports.cshtml"), token), - token => Assert.Same(fileProvider.GetChangeToken("/file/exists/_ViewImports.cshtml"), token)); - } - - [Theory] - [InlineData("/Areas/Finances/Views/Home/Index.cshtml")] - [InlineData(@"Areas\Finances\Views\Home\Index.cshtml")] - [InlineData(@"\Areas\Finances\Views\Home\Index.cshtml")] - [InlineData(@"\Areas\Finances\Views/Home\Index.cshtml")] - public async Task CompileAsync_NormalizesPathSeparatorForPaths(string relativePath) - { - // Arrange - var viewPath = "/Areas/Finances/Views/Home/Index.cshtml"; - var fileProvider = new TestFileProvider(); - fileProvider.AddFile(viewPath, "some content"); - var viewCompiler = GetViewCompiler(fileProvider); - - // Act - 1 - var result1 = await viewCompiler.CompileAsync(@"Areas\Finances\Views\Home\Index.cshtml"); - - // Act - 2 - viewCompiler.Compile = _ => throw new Exception("Can't call me"); - var result2 = await viewCompiler.CompileAsync(relativePath); - - // Assert - 2 - Assert.Same(result1, result2); - } - - [Fact] - public async Task CompileAsync_DoesNotInvalidCache_IfChangeTokenChanges() + public async Task CompileAsync_ReturnsCompiledViews() { // Arrange var path = "/Views/Home/Index.cshtml"; - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - var viewCompiler = GetViewCompiler(fileProvider); - var changeToken = fileProvider.Watch(path); - - // Act 1 - var result1 = await viewCompiler.CompileAsync(path); - - // Assert 1 - Assert.NotNull(result1.ViewAttribute); - - // Act 2 - fileProvider.DeleteFile(path); - fileProvider.GetChangeToken(path).HasChanged = true; - viewCompiler.Compile = _ => throw new Exception("Can't call me"); - var result2 = await viewCompiler.CompileAsync(path); - - // Assert 2 - Assert.Same(result1, result2); - } - - [Fact] - public async Task CompileAsync_InvalidatesCache_IfChangeTokenExpires_WhenWatchingForFileChanges() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - var viewCompiler = GetViewCompiler(fileProvider); - viewCompiler.AllowRecompilingViewsOnFileChange = true; - - // Act 1 - var result1 = await viewCompiler.CompileAsync(path); - - // Assert 1 - Assert.NotNull(result1.ViewAttribute); - - // Act 2 - fileProvider.DeleteFile(path); - fileProvider.GetChangeToken(path).HasChanged = true; - viewCompiler.Compile = _ => throw new Exception("Can't call me"); - var result2 = await viewCompiler.CompileAsync(path); - - // Assert 2 - Assert.NotSame(result1, result2); - Assert.Null(result2.ViewAttribute); - } - - [Fact] - public async Task CompileAsync_ReturnsNewResultIfFileWasModified() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - var viewCompiler = GetViewCompiler(fileProvider); - viewCompiler.AllowRecompilingViewsOnFileChange = true; - var expected2 = new CompiledViewDescriptor(); - - // Act 1 - var result1 = await viewCompiler.CompileAsync(path); - - // Assert 1 - Assert.NotNull(result1.ViewAttribute); - - // Act 2 - fileProvider.GetChangeToken(path).HasChanged = true; - viewCompiler.Compile = _ => expected2; - var result2 = await viewCompiler.CompileAsync(path); - - // Assert 2 - Assert.NotSame(result1, result2); - Assert.Same(expected2, result2); - } - - [Fact] - public async Task CompileAsync_ReturnsNewResult_IfAncestorViewImportsWereModified() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - var viewCompiler = GetViewCompiler(fileProvider); - viewCompiler.AllowRecompilingViewsOnFileChange = true; - var expected2 = new CompiledViewDescriptor(); - - // Act 1 - var result1 = await viewCompiler.CompileAsync(path); - - // Assert 1 - Assert.NotNull(result1.ViewAttribute); - - // Act 2 - fileProvider.GetChangeToken("/Views/_ViewImports.cshtml").HasChanged = true; - viewCompiler.Compile = _ => expected2; - var result2 = await viewCompiler.CompileAsync(path); - - // Assert 2 - Assert.NotSame(result1, result2); - Assert.Same(expected2, result2); - } - - [Fact] - public async Task CompileAsync_ReturnsPrecompiledViews() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - var precompiledView = new CompiledViewDescriptor + var compiledView = new CompiledViewDescriptor { RelativePath = path, }; - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); + var viewCompiler = GetViewCompiler(compiledViews: new[] { compiledView }); // Act var result = await viewCompiler.CompileAsync(path); // Assert - Assert.Same(precompiledView, result); + Assert.Same(compiledView, result); // This view doesn't have checksums so it can't be recompiled. - Assert.Null(precompiledView.ExpirationTokens); + Assert.Null(compiledView.ExpirationTokens); } [Theory] [InlineData("/views/home/index.cshtml")] [InlineData("/VIEWS/HOME/INDEX.CSHTML")] [InlineData("/viEws/HoME/inDex.cshtml")] - public async Task CompileAsync_PerformsCaseInsensitiveLookupsForPrecompiledViews(string lookupPath) + public async Task CompileAsync_PerformsCaseInsensitiveLookupsForCompiledViews(string lookupPath) { // Arrange var path = "/Views/Home/Index.cshtml"; - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); var precompiledView = new CompiledViewDescriptor { RelativePath = path, }; - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); + var viewCompiler = GetViewCompiler(compiledViews: new[] { precompiledView }); // Act var result = await viewCompiler.CompileAsync(lookupPath); @@ -281,744 +70,37 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation } [Fact] - public async Task CompileAsync_PerformsCaseInsensitiveLookupsForPrecompiledViews_WithNonNormalizedPaths() + public async Task CompileAsync_PerformsCaseInsensitiveLookupsForCompiledViews_WithNonNormalizedPaths() { // Arrange var path = "/Views/Home/Index.cshtml"; - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - var precompiledView = new CompiledViewDescriptor + var compiledView = new CompiledViewDescriptor { RelativePath = path, }; - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); + var viewCompiler = GetViewCompiler(compiledViews: new[] { compiledView }); // Act var result = await viewCompiler.CompileAsync("Views\\Home\\Index.cshtml"); // Assert - Assert.Same(precompiledView, result); + Assert.Same(compiledView, result); } - [Fact] - public async Task CompileAsync_PrecompiledViewWithoutChecksumForMainSource_DoesNotSupportRecompilation() + private static TestRazorViewCompiler GetViewCompiler(IList compiledViews = null) { - // Arrange - var path = "/Views/Home/Index.cshtml"; + compiledViews = compiledViews ?? Array.Empty(); - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - - var precompiledView = new CompiledViewDescriptor - { - RelativePath = path, - Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] - { - new RazorSourceChecksumAttribute("sha1", GetChecksum("some content"), "/Views/Some-Other-View"), - }), - }; - - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); - - // Act - 1 - var result = await viewCompiler.CompileAsync(path); - - // Assert - 1 - Assert.Same(precompiledView.Item, result.Item); - - // Act - 2 - fileProvider.Watch(path); - fileProvider.GetChangeToken(path).HasChanged = true; - result = await viewCompiler.CompileAsync(path); - - // Assert - 2 - Assert.Same(precompiledView.Item, result.Item); - - // This view doesn't have checksums so it can't be recompiled. - Assert.Null(result.ExpirationTokens); - } - - [Fact] - public async Task CompileAsync_PrecompiledViewWithoutAnyChecksum_DoesNotSupportRecompilation() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - - var precompiledView = new CompiledViewDescriptor - { - RelativePath = path, - Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] { }), - }; - - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); - - // Act - 1 - var result = await viewCompiler.CompileAsync(path); - - // Assert - 1 - Assert.Same(precompiledView, result); - - // Act - 2 - fileProvider.Watch(path); - fileProvider.GetChangeToken(path).HasChanged = true; - result = await viewCompiler.CompileAsync(path); - - // Assert - 2 - Assert.Same(precompiledView, result); - - // This view doesn't have checksums so it can't be recompiled. - Assert.Null(result.ExpirationTokens); - } - - [Fact] - public async Task CompileAsync_PrecompiledViewWithChecksum_UsesPrecompiledViewWhenChecksumIsMatch() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - - var precompiledView = new CompiledViewDescriptor - { - RelativePath = path, - Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), path), - }), - }; - - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); - viewCompiler.AllowRecompilingViewsOnFileChange = true; - - // Act - var result = await viewCompiler.CompileAsync(path); - - // Assert - Assert.Same(precompiledView.Item, result.Item); - - // This view has checksums so it should also have tokens - Assert.Collection( - result.ExpirationTokens, - token => Assert.Same(fileProvider.GetChangeToken(path), token)); - } - - [Fact] - public async Task CompileAsync_PrecompiledViewWithChecksum_CanRejectWhenChecksumFails() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - - var expected = new CompiledViewDescriptor(); - - var precompiledView = new CompiledViewDescriptor - { - RelativePath = path, - Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some other content"), path), - }), - }; - - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); - viewCompiler.Compile = _ => expected; - - // Act - var result = await viewCompiler.CompileAsync(path); - - // Assert - Assert.Same(expected, result); - } - - [Fact] - public async Task CompileAsync_PrecompiledViewWithChecksum_DoesNotAddExpirationTokens() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - - var expected2 = new CompiledViewDescriptor(); - - var precompiledView = new CompiledViewDescriptor - { - RelativePath = path, - Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), path), - }), - }; - - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); - - // Act - var result = await viewCompiler.CompileAsync(path); - - // Assert - Assert.Same(precompiledView.Item, result.Item); - Assert.Empty(result.ExpirationTokens); - } - - [Fact] - public async Task CompileAsync_PrecompiledViewWithChecksum_CanRecompile() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - - var expected2 = new CompiledViewDescriptor(); - - var precompiledView = new CompiledViewDescriptor - { - RelativePath = path, - Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), path), - }), - }; - - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); - viewCompiler.AllowRecompilingViewsOnFileChange = true; - - // Act - 1 - var result = await viewCompiler.CompileAsync(path); - - // Assert - 1 - Assert.Same(precompiledView.Item, result.Item); - Assert.NotEmpty(result.ExpirationTokens); - - // Act - 2 - fileInfo.Content = "some other content"; - fileProvider.GetChangeToken(path).HasChanged = true; - viewCompiler.Compile = _ => expected2; - result = await viewCompiler.CompileAsync(path); - - // Assert - 2 - Assert.Same(expected2, result); - } - - [Fact] - public async Task CompileAsync_PrecompiledViewWithChecksum_DoesNotRecompiledWithoutContentChange() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - - var precompiledView = new CompiledViewDescriptor - { - RelativePath = path, - Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), path), - }), - }; - - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); - viewCompiler.AllowRecompilingViewsOnFileChange = true; - - // Act - 1 - var result = await viewCompiler.CompileAsync(path); - - // Assert - 1 - Assert.Same(precompiledView.Item, result.Item); - - // Act - 2 - fileProvider.GetChangeToken(path).HasChanged = true; - result = await viewCompiler.CompileAsync(path); - - // Assert - 2 - Assert.Same(precompiledView.Item, result.Item); - } - - [Fact] - public async Task CompileAsync_PrecompiledViewWithChecksum_CanReusePrecompiledViewIfContentChangesToMatch() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some other content"); - - var expected1 = new CompiledViewDescriptor(); - - var precompiledView = new CompiledViewDescriptor - { - RelativePath = path, - Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), path), - }), - }; - - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); - viewCompiler.AllowRecompilingViewsOnFileChange = true; - viewCompiler.Compile = _ => expected1; - - // Act - 1 - var result = await viewCompiler.CompileAsync(path); - - // Assert - 1 - Assert.Same(expected1, result); - - // Act - 2 - fileInfo.Content = "some content"; - fileProvider.GetChangeToken(path).HasChanged = true; - result = await viewCompiler.CompileAsync(path); - - // Assert - 2 - Assert.Same(precompiledView.Item, result.Item); - } - - [Fact] - public async Task CompileAsync_PrecompiledViewWithChecksum_CanRecompileWhenViewImportChanges() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - var importPath = "/Views/_ViewImports.cshtml"; - - var fileProvider = new TestFileProvider(); - var fileInfo = fileProvider.AddFile(path, "some content"); - var importFileInfo = fileProvider.AddFile(importPath, "some import"); - - var expected2 = new CompiledViewDescriptor(); - - var precompiledView = new CompiledViewDescriptor - { - RelativePath = path, - Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), path), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), importPath), - }), - }; - - var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); - viewCompiler.AllowRecompilingViewsOnFileChange = true; - - // Act - 1 - var result = await viewCompiler.CompileAsync(path); - - // Assert - 1 - Assert.Same(precompiledView.Item, result.Item); - - // Act - 2 - importFileInfo.Content = "some import changed"; - fileProvider.GetChangeToken(importPath).HasChanged = true; - viewCompiler.Compile = _ => expected2; - result = await viewCompiler.CompileAsync(path); - - // Assert - 2 - Assert.Same(expected2, result); - } - - [Fact] - public async Task GetOrAdd_AllowsConcurrentCompilationOfMultipleRazorPages() - { - // Arrange - var path1 = "/Views/Home/Index.cshtml"; - var path2 = "/Views/Home/About.cshtml"; - var waitDuration = TimeSpan.FromSeconds(20); - - var fileProvider = new TestFileProvider(); - fileProvider.AddFile(path1, "Index content"); - fileProvider.AddFile(path2, "About content"); - - var resetEvent1 = new AutoResetEvent(initialState: false); - var resetEvent2 = new ManualResetEvent(initialState: false); - - var compilingOne = false; - var compilingTwo = false; - - var result1 = new CompiledViewDescriptor(); - var result2 = new CompiledViewDescriptor(); - - var compiler = GetViewCompiler(fileProvider); - - compiler.Compile = path => - { - if (path == path1) - { - compilingOne = true; - - // Event 2 - Assert.True(resetEvent1.WaitOne(waitDuration)); - - // Event 3 - Assert.True(resetEvent2.Set()); - - // Event 6 - Assert.True(resetEvent1.WaitOne(waitDuration)); - - Assert.True(compilingTwo); - - return result1; - } - else if (path == path2) - { - compilingTwo = true; - - // Event 4 - Assert.True(resetEvent2.WaitOne(waitDuration)); - - // Event 5 - Assert.True(resetEvent1.Set()); - - Assert.True(compilingOne); - - return result2; - } - else - { - throw new Exception(); - } - }; - - // Act - var task1 = Task.Run(() => compiler.CompileAsync(path1)); - var task2 = Task.Run(() => compiler.CompileAsync(path2)); - - // Event 1 - resetEvent1.Set(); - - await Task.WhenAll(task1, task2); - - // Assert - Assert.True(compilingOne); - Assert.True(compilingTwo); - Assert.Same(result1, task1.Result); - Assert.Same(result2, task2.Result); - } - - [Fact] - public async Task CompileAsync_DoesNotCreateMultipleCompilationResults_ForConcurrentInvocations() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - var waitDuration = TimeSpan.FromSeconds(20); - var fileProvider = new TestFileProvider(); - fileProvider.AddFile(path, "some content"); - var resetEvent1 = new ManualResetEvent(initialState: false); - var resetEvent2 = new ManualResetEvent(initialState: false); - var compiler = GetViewCompiler(fileProvider); - - compiler.Compile = _ => - { - // Event 2 - resetEvent1.WaitOne(waitDuration); - - // Event 3 - resetEvent2.Set(); - return new CompiledViewDescriptor(); - }; - - // Act - var task1 = Task.Run(() => compiler.CompileAsync(path)); - var task2 = Task.Run(() => - { - // Event 4 - Assert.True(resetEvent2.WaitOne(waitDuration)); - return compiler.CompileAsync(path); - }); - - // Event 1 - resetEvent1.Set(); - await Task.WhenAll(task1, task2); - - // Assert - var result1 = task1.Result; - var result2 = task2.Result; - Assert.Same(result1, result2); - } - - [Fact] - public async Task GetOrAdd_CachesCompilationExceptions() - { - // Arrange - var path = "/Views/Home/Index.cshtml"; - var fileProvider = new TestFileProvider(); - fileProvider.AddFile(path, "some content"); - var exception = new InvalidTimeZoneException(); - var compiler = GetViewCompiler(fileProvider); - compiler.Compile = _ => throw exception; - - // Act and Assert - 1 - var actual = await Assert.ThrowsAsync( - () => compiler.CompileAsync(path)); - Assert.Same(exception, actual); - - // Act and Assert - 2 - compiler.Compile = _ => throw new Exception("Shouldn't be called"); - - actual = await Assert.ThrowsAsync( - () => compiler.CompileAsync(path)); - Assert.Same(exception, actual); - } - - [Fact] - public void Compile_SucceedsForCSharp7() - { - // Arrange - var content = @" -public class MyTestType -{ - private string _name; - - public string Name - { - get => _name; - set => _name = value ?? throw new System.ArgumentNullException(nameof(value)); - } -}"; - var compiler = GetViewCompiler(new TestFileProvider()); - var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("razor-content", "filename")); - - // Act - var result = compiler.CompileAndEmit(codeDocument, content); - - // Assert - var exportedType = Assert.Single(result.ExportedTypes); - Assert.Equal("MyTestType", exportedType.Name); - } - - [Fact] - public void Compile_ReturnsCompilationFailureWithPathsFromLinePragmas() - { - // Arrange - var viewPath = "some-relative-path"; - var fileContent = "test file content"; - var content = $@" -#line 1 ""{viewPath}"" -this should fail"; - - var compiler = GetViewCompiler(new TestFileProvider()); - var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create(fileContent, viewPath)); - - // Act & Assert - var ex = Assert.Throws(() => compiler.CompileAndEmit(codeDocument, content)); - - var compilationFailure = Assert.Single(ex.CompilationFailures); - Assert.Equal(viewPath, compilationFailure.SourceFilePath); - Assert.Equal(fileContent, compilationFailure.SourceFileContent); - } - - [Fact] - public void Compile_ReturnsGeneratedCodePath_IfLinePragmaIsNotAvailable() - { - // Arrange - var viewPath = "some-relative-path"; - var fileContent = "file content"; - var content = "this should fail"; - - var compiler = GetViewCompiler(new TestFileProvider()); - var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create(fileContent, viewPath)); - - // Act & Assert - var ex = Assert.Throws(() => compiler.CompileAndEmit(codeDocument, content)); - - var compilationFailure = Assert.Single(ex.CompilationFailures); - Assert.Equal("Generated Code", compilationFailure.SourceFilePath); - Assert.Equal(content, compilationFailure.SourceFileContent); - } - - [Fact] - public void Compile_InvokesCallback() - { - // Arrange - var content = "public class MyTestType {}"; - var callbackInvoked = false; - var compiler = GetViewCompiler( - new TestFileProvider(), - context => - { - callbackInvoked = true; - }); - var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("some-content", "some-path")); - - // Act - var result = compiler.CompileAndEmit(codeDocument, content); - - // Assert - Assert.True(callbackInvoked); - } - - [Fact] - public void Compile_SucceedsIfReferencesAreAddedInCallback() - { - // Arrange - Action compilationCallback = context => - { - var assemblyLocation = typeof(object).Assembly.Location; - - context.Compilation = context - .Compilation - .AddReferences(MetadataReference.CreateFromFile(assemblyLocation)); - }; - - var applicationPartManager = new ApplicationPartManager(); - var referenceManager = new DefaultRazorReferenceManager( - applicationPartManager, - Options.Create(new RazorViewEngineOptions())); - var compiler = GetViewCompiler( - compilationCallback: compilationCallback, - referenceManager: referenceManager); - - var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", "some-relative-path.cshtml")); - - // Act - var result = compiler.CompileAndEmit(codeDocument, "public class Test {}"); - - // Assert - Assert.NotNull(result); - } - - [Fact] - public void CompileAndEmit_DoesNotThrowIfDebugTypeIsEmbedded() - { - // Arrange - var referenceManager = CreateReferenceManager(Options.Create(new RazorViewEngineOptions())); - var csharpCompiler = new TestCSharpCompiler(referenceManager, Mock.Of()) - { - EmitOptionsSettable = new EmitOptions(debugInformationFormat: DebugInformationFormat.Embedded), - }; - - var compiler = GetViewCompiler(csharpCompiler: csharpCompiler); - var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", "some-relative-path.cshtml")); - - // Act - var result = compiler.CompileAndEmit(codeDocument, "public class Test{}"); - - // Assert - Assert.NotNull(result); - } - - [Fact] - public void CompileAndEmit_WorksIfEmitPdbIsNotSet() - { - // Arrange - var referenceManager = CreateReferenceManager(Options.Create(new RazorViewEngineOptions())); - var csharpCompiler = new TestCSharpCompiler(referenceManager, Mock.Of()) - { - EmitPdbSettable = false, - }; - - var compiler = GetViewCompiler(csharpCompiler: csharpCompiler); - var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", "some-relative-path.cshtml")); - - // Act - var result = compiler.CompileAndEmit(codeDocument, "public class Test{}"); - - // Assert - Assert.NotNull(result); - } - - private static TestRazorViewCompiler GetViewCompiler( - TestFileProvider fileProvider = null, - Action compilationCallback = null, -#pragma warning disable CS0618 // Type or member is obsolete - RazorReferenceManager referenceManager = null, -#pragma warning restore CS0618 // Type or member is obsolete - IList precompiledViews = null, - CSharpCompiler csharpCompiler = null) - { - fileProvider = fileProvider ?? new TestFileProvider(); - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - compilationCallback = compilationCallback ?? (_ => { }); - var options = Options.Create(new RazorViewEngineOptions()); - if (referenceManager == null) - { - referenceManager = CreateReferenceManager(options); - } - - precompiledViews = precompiledViews ?? Array.Empty(); - - var hostingEnvironment = Mock.Of(e => e.ContentRootPath == "BasePath"); - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, hostingEnvironment); - var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, builder => - { - RazorExtensions.Register(builder); - }); - - csharpCompiler = csharpCompiler ?? new CSharpCompiler(referenceManager, hostingEnvironment); - - var viewCompiler = new TestRazorViewCompiler( - fileProvider, - projectEngine, - csharpCompiler, - compilationCallback, - precompiledViews); + var viewCompiler = new TestRazorViewCompiler(compiledViews); return viewCompiler; } -#pragma warning disable CS0618 // Type or member is obsolete - private static RazorReferenceManager CreateReferenceManager(IOptions options) - { - var applicationPartManager = new ApplicationPartManager(); - var assembly = typeof(RazorViewCompilerTest).Assembly; - applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly)); - applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider()); - - return new DefaultRazorReferenceManager(applicationPartManager, options); - } -#pragma warning restore CS0618 // Type or member is obsolete - private class TestRazorViewCompiler : RazorViewCompiler { - public TestRazorViewCompiler( - TestFileProvider fileProvider, - RazorProjectEngine projectEngine, - CSharpCompiler csharpCompiler, - Action compilationCallback, - IList precompiledViews, - Func compile = null) : - base(fileProvider, projectEngine, csharpCompiler, compilationCallback, precompiledViews, new MemoryCache(new MemoryCacheOptions()), NullLogger.Instance) - { - Compile = compile; - if (Compile == null) - { - Compile = path => new CompiledViewDescriptor - { - RelativePath = path, - ViewAttribute = new RazorViewAttribute(path, typeof(object)), - }; - } - } - - public Func Compile { get; set; } - - protected override CompiledViewDescriptor CompileAndEmit(string relativePath) - { - return Compile(relativePath); - } - } - - private class TestCSharpCompiler : CSharpCompiler - { -#pragma warning disable CS0618 // Type or member is obsolete - public TestCSharpCompiler(RazorReferenceManager manager, IHostingEnvironment hostingEnvironment) -#pragma warning restore CS0618 // Type or member is obsolete - : base(manager, hostingEnvironment) + public TestRazorViewCompiler(IList compiledViews) : + base(compiledViews, NullLogger.Instance) { } - - public EmitOptions EmitOptionsSettable { get; set; } - - public bool EmitPdbSettable { get; set; } - - public override EmitOptions EmitOptions => EmitOptionsSettable; - - public override bool EmitPdb => EmitPdbSettable; } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/DefaultRazorViewEngineFileProviderAccessorTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/DefaultRazorViewEngineFileProviderAccessorTest.cs deleted file mode 100644 index d69853f2b..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/DefaultRazorViewEngineFileProviderAccessorTest.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Options; -using Moq; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.Razor -{ - public class DefaultRazorViewEngineFileProviderAccessorTest - { - [Fact] - public void FileProvider_ReturnsInstance_IfExactlyOneFileProviderIsRegistered() - { - // Arrange - var fileProvider = new TestFileProvider(); - var options = new RazorViewEngineOptions(); - options.FileProviders.Add(fileProvider); - var optionsAccessor = new Mock>(); - optionsAccessor.SetupGet(o => o.Value).Returns(options); - var fileProviderAccessor = new DefaultRazorViewEngineFileProviderAccessor(optionsAccessor.Object); - - // Act - var actual = fileProviderAccessor.FileProvider; - - // Assert - Assert.Same(fileProvider, actual); - } - - [Fact] - public void FileProvider_ReturnsNullFileProvider_IfNoInstancesAreRegistered() - { - // Arrange - var options = new RazorViewEngineOptions(); - var optionsAccessor = new Mock>(); - optionsAccessor.SetupGet(o => o.Value).Returns(options); - var fileProviderAccessor = new DefaultRazorViewEngineFileProviderAccessor(optionsAccessor.Object); - - // Act - var actual = fileProviderAccessor.FileProvider; - - // Assert - Assert.IsType(actual); - } - - [Fact] - public void FileProvider_ReturnsCompositeFileProvider_IfMoreThanOneInstanceIsRegistered() - { - // Arrange - var options = new RazorViewEngineOptions(); - options.FileProviders.Add(new TestFileProvider()); - options.FileProviders.Add(new TestFileProvider()); - var optionsAccessor = new Mock>(); - optionsAccessor.SetupGet(o => o.Value).Returns(options); - var fileProviderAccessor = new DefaultRazorViewEngineFileProviderAccessor(optionsAccessor.Object); - - // Act - var actual = fileProviderAccessor.FileProvider; - - // Assert - Assert.IsType(actual); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/DependencyInjection/MvcRazorMvcCoreBuilderExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/DependencyInjection/MvcRazorMvcCoreBuilderExtensionsTest.cs index bf5e47176..ad46856f3 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/DependencyInjection/MvcRazorMvcCoreBuilderExtensionsTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/DependencyInjection/MvcRazorMvcCoreBuilderExtensionsTest.cs @@ -53,63 +53,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test.DependencyInjection Assert.Empty(builder.PartManager.ApplicationParts); } - [Fact] - public void AddRazorViewEngine_AddsMetadataReferenceFeatureProvider() - { - // Arrange - var services = new ServiceCollection(); - var builder = services.AddMvcCore(); - - // Act - builder.AddRazorViewEngine(); - - // Assert -#pragma warning disable CS0618 // Type or member is obsolete - Assert.Single(builder.PartManager.FeatureProviders.OfType()); -#pragma warning restore CS0618 // Type or member is obsolete - } - - [Fact] - public void AddRazorViewEngine_DoesNotAddMultipleMetadataReferenceFeatureProvider_OnMultipleInvocations() - { - // Arrange - var services = new ServiceCollection(); - var builder = services.AddMvcCore(); - - // Act - 1 - builder.AddRazorViewEngine(); - - // Act - 2 - builder.AddRazorViewEngine(); - - // Assert -#pragma warning disable CS0618 // Type or member is obsolete - Assert.Single(builder.PartManager.FeatureProviders.OfType()); -#pragma warning restore CS0618 // Type or member is obsolete - } - - [Fact] - public void AddRazorViewEngine_DoesNotReplaceExistingMetadataReferenceFeatureProvider() - { - // Arrange - var services = new ServiceCollection(); - var builder = services.AddMvcCore(); -#pragma warning disable CS0618 // Type or member is obsolete - var metadataReferenceFeatureProvider = new MetadataReferenceFeatureProvider(); -#pragma warning restore CS0618 // Type or member is obsolete - builder.PartManager.FeatureProviders.Add(metadataReferenceFeatureProvider); - - // Act - builder.AddRazorViewEngine(); - - // Assert - var actual = Assert.Single( -#pragma warning disable CS0618 // Type or member is obsolete - collection: builder.PartManager.FeatureProviders.OfType()); -#pragma warning restore CS0618 // Type or member is obsolete - Assert.Same(metadataReferenceFeatureProvider, actual); - } - [Fact] public void AddTagHelpersAsServices_ReplacesTagHelperActivatorAndTagHelperTypeResolver() { diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/DependencyInjection/RazorViewEngineOptionsSetupTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/DependencyInjection/RazorViewEngineOptionsSetupTest.cs deleted file mode 100644 index 79d9b334d..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/DependencyInjection/RazorViewEngineOptionsSetupTest.cs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.AspNetCore.Mvc.Razor; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Moq; -using Xunit; - -namespace Microsoft.Extensions.DependencyInjection -{ - public class RazorViewEngineOptionsSetupTest - { - [Fact] - public void RazorViewEngineOptionsSetup_SetsUpFileProvider() - { - // Arrange - var options = new RazorViewEngineOptions(); - var expected = Mock.Of(); - var hostingEnv = new Mock(); - hostingEnv.SetupGet(e => e.ContentRootFileProvider) - .Returns(expected); - - var optionsSetup = GetSetup(hostingEnvironment: hostingEnv.Object); - - // Act - optionsSetup.Configure(options); - - // Assert - var fileProvider = Assert.Single(options.FileProviders); - Assert.Same(expected, fileProvider); - } - - [Fact] - public void PostConfigure_SetsAllowRecompilingViewsOnFileChange_For21() - { - // Arrange - var options = new RazorViewEngineOptions(); - var optionsSetup = GetSetup(CompatibilityVersion.Version_2_1); - - // Act - optionsSetup.Configure(options); - optionsSetup.PostConfigure(string.Empty, options); - - // Assert - Assert.True(options.AllowRecompilingViewsOnFileChange); - } - - [Theory] - [InlineData(CompatibilityVersion.Version_2_2)] - [InlineData(CompatibilityVersion.Latest)] - public void PostConfigure_SetsAllowRecompilingViewsOnFileChange_InDevelopmentMode(CompatibilityVersion compatibilityVersion) - { - // Arrange - var options = new RazorViewEngineOptions(); - var hostingEnv = Mock.Of(env => env.EnvironmentName == EnvironmentName.Development); - var optionsSetup = GetSetup(compatibilityVersion, hostingEnv); - - // Act - optionsSetup.Configure(options); - optionsSetup.PostConfigure(string.Empty, options); - - // Assert - Assert.True(options.AllowRecompilingViewsOnFileChange); - } - - [Theory] - [InlineData(CompatibilityVersion.Version_2_2)] - [InlineData(CompatibilityVersion.Latest)] - public void PostConfigure_DoesNotSetAllowRecompilingViewsOnFileChange_WhenNotInDevelopment(CompatibilityVersion compatibilityVersion) - { - // Arrange - var options = new RazorViewEngineOptions(); - var hostingEnv = Mock.Of(env => env.EnvironmentName == EnvironmentName.Staging); - var optionsSetup = GetSetup(compatibilityVersion, hostingEnv); - - // Act - optionsSetup.Configure(options); - optionsSetup.PostConfigure(string.Empty, options); - - // Assert - Assert.False(options.AllowRecompilingViewsOnFileChange); - } - - [Fact] - public void RazorViewEngineOptionsSetup_DoesNotOverwriteAllowRecompilingViewsOnFileChange_In21CompatMode() - { - // Arrange - var hostingEnv = Mock.Of(env => env.EnvironmentName == EnvironmentName.Staging); - var compatibilityVersion = new MvcCompatibilityOptions { CompatibilityVersion = CompatibilityVersion.Version_2_1 }; - var optionsSetup = GetSetup(CompatibilityVersion.Version_2_1, hostingEnv); - var serviceProvider = new ServiceCollection() - .AddOptions() - .AddSingleton>(optionsSetup) - .Configure(o => o.AllowRecompilingViewsOnFileChange = false) - .BuildServiceProvider(); - - // Act - var options = serviceProvider.GetRequiredService>(); - - // Assert - Assert.False(options.Value.AllowRecompilingViewsOnFileChange); - } - - [Fact] - public void RazorViewEngineOptionsSetup_ConfiguresAllowRecompilingViewsOnFileChange() - { - // Arrange - var hostingEnv = Mock.Of(env => env.EnvironmentName == EnvironmentName.Production); - var compatibilityVersion = new MvcCompatibilityOptions { CompatibilityVersion = CompatibilityVersion.Version_2_2 }; - var optionsSetup = GetSetup(CompatibilityVersion.Version_2_2, hostingEnv); - var serviceProvider = new ServiceCollection() - .AddOptions() - .AddSingleton>(optionsSetup) - .BuildServiceProvider(); - - // Act - var options = serviceProvider.GetRequiredService>(); - - // Assert - Assert.False(options.Value.AllowRecompilingViewsOnFileChange); - } - - [Fact] - public void RazorViewEngineOptionsSetup_DoesNotOverwriteAllowRecompilingViewsOnFileChange() - { - // Arrange - var hostingEnv = Mock.Of(env => env.EnvironmentName == EnvironmentName.Production); - var optionsSetup = GetSetup(CompatibilityVersion.Version_2_2, hostingEnv); - var serviceProvider = new ServiceCollection() - .AddOptions() - .AddSingleton>(optionsSetup) - .Configure(o => o.AllowRecompilingViewsOnFileChange = true) - .BuildServiceProvider(); - - // Act - var options = serviceProvider.GetRequiredService>(); - - // Assert - Assert.True(options.Value.AllowRecompilingViewsOnFileChange); - } - - private static RazorViewEngineOptionsSetup GetSetup( - CompatibilityVersion compatibilityVersion = CompatibilityVersion.Latest, - IHostingEnvironment hostingEnvironment = null) - { - hostingEnvironment = hostingEnvironment ?? Mock.Of(); - var compatibilityOptions = new MvcCompatibilityOptions { CompatibilityVersion = compatibilityVersion }; - - return new RazorViewEngineOptionsSetup( - hostingEnvironment, - NullLoggerFactory.Instance, - Options.Options.Create(compatibilityOptions)); - } - - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/FileProviderRazorProjectFileSystemTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/FileProviderRazorProjectFileSystemTest.cs deleted file mode 100644 index 4aa75decd..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/FileProviderRazorProjectFileSystemTest.cs +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; -using System.Linq; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.FileProviders; -using Moq; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.Razor -{ - public class FileProviderRazorProjectFileSystemTest - { - [Fact] - public void EnumerateFiles_ReturnsEmptySequenceIfNoCshtmlFilesArePresent() - { - // Arrange - var fileProvider = new TestFileProvider("BasePath"); - var file1 = fileProvider.AddFile("/File1.txt", "content"); - var file2 = fileProvider.AddFile("/File2.js", "content"); - fileProvider.AddDirectoryContent("/", new IFileInfo[] { file1, file2 }); - - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, Mock.Of(e => e.ContentRootPath == "BasePath")); - - // Act - var razorFiles = fileSystem.EnumerateItems("/"); - - // Assert - Assert.Empty(razorFiles); - } - - [Fact] - public void EnumerateFiles_ReturnsCshtmlFiles() - { - // Arrange - var fileProvider = new TestFileProvider("BasePath"); - var file1 = fileProvider.AddFile("/File1.cshtml", "content"); - var file2 = fileProvider.AddFile("/File2.js", "content"); - var file3 = fileProvider.AddFile("/File3.cshtml", "content"); - fileProvider.AddDirectoryContent("/", new IFileInfo[] { file1, file2, file3 }); - - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, Mock.Of(e => e.ContentRootPath == "BasePath")); - - // Act - var razorFiles = fileSystem.EnumerateItems("/"); - - // Assert - Assert.Collection( - razorFiles.OrderBy(f => f.FilePath), - file => - { - Assert.Equal("/File1.cshtml", file.FilePath); - Assert.Equal("/", file.BasePath); - Assert.Equal(Path.Combine("BasePath", "File1.cshtml"), file.PhysicalPath); - Assert.Equal("File1.cshtml", file.RelativePhysicalPath); - }, - file => - { - Assert.Equal("/File3.cshtml", file.FilePath); - Assert.Equal("/", file.BasePath); - Assert.Equal(Path.Combine("BasePath", "File3.cshtml"), file.PhysicalPath); - Assert.Equal("File3.cshtml", file.RelativePhysicalPath); - }); - } - - [Fact] - public void EnumerateFiles_IteratesOverAllCshtmlUnderRoot() - { - // Arrange - var fileProvider = new TestFileProvider("BasePath"); - var directory1 = new TestDirectoryFileInfo - { - Name = "Level1-Dir1", - }; - var file1 = fileProvider.AddFile("File1.cshtml", "content"); - var directory2 = new TestDirectoryFileInfo - { - Name = "Level1-Dir2", - }; - fileProvider.AddDirectoryContent("/", new IFileInfo[] { directory1, file1, directory2 }); - - var file2 = fileProvider.AddFile("/Level1-Dir1/File2.cshtml", "content"); - var file3 = fileProvider.AddFile("/Level1-Dir1/File3.cshtml", "content"); - var file4 = fileProvider.AddFile("/Level1-Dir1/File4.txt", "content"); - var directory3 = new TestDirectoryFileInfo - { - Name = "Level2-Dir1" - }; - fileProvider.AddDirectoryContent("/Level1-Dir1", new IFileInfo[] { file2, directory3, file3, file4 }); - var file5 = fileProvider.AddFile(Path.Combine("Level1-Dir2", "File5.cshtml"), "content"); - fileProvider.AddDirectoryContent("/Level1-Dir2", new IFileInfo[] { file5 }); - fileProvider.AddDirectoryContent("/Level1/Level2", new IFileInfo[0]); - - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, Mock.Of(e => e.ContentRootPath == "BasePath")); - - // Act - var razorFiles = fileSystem.EnumerateItems("/"); - - // Assert - Assert.Collection(razorFiles.OrderBy(f => f.FilePath), - file => - { - Assert.Equal("/File1.cshtml", file.FilePath); - Assert.Equal("/", file.BasePath); - Assert.Equal(Path.Combine("BasePath", "File1.cshtml"), file.PhysicalPath); - Assert.Equal("File1.cshtml", file.RelativePhysicalPath); - }, - file => - { - Assert.Equal("/Level1-Dir1/File2.cshtml", file.FilePath); - Assert.Equal("/", file.BasePath); - Assert.Equal(Path.Combine("BasePath", "Level1-Dir1", "File2.cshtml"), file.PhysicalPath); - Assert.Equal(Path.Combine("Level1-Dir1", "File2.cshtml"), file.RelativePhysicalPath); - }, - file => - { - Assert.Equal("/Level1-Dir1/File3.cshtml", file.FilePath); - Assert.Equal("/", file.BasePath); - Assert.Equal(Path.Combine("BasePath", "Level1-Dir1", "File3.cshtml"), file.PhysicalPath); - Assert.Equal(Path.Combine("Level1-Dir1", "File3.cshtml"), file.RelativePhysicalPath); - }, - file => - { - Assert.Equal("/Level1-Dir2/File5.cshtml", file.FilePath); - Assert.Equal("/", file.BasePath); - Assert.Equal(Path.Combine("BasePath", "Level1-Dir2", "File5.cshtml"), file.PhysicalPath); - Assert.Equal(Path.Combine("Level1-Dir2", "File5.cshtml"), file.RelativePhysicalPath); - }); - } - - [Fact] - public void EnumerateFiles_IteratesOverAllCshtmlUnderPath() - { - // Arrange - var fileProvider = new TestFileProvider("BasePath"); - var directory1 = new TestDirectoryFileInfo - { - Name = "Level1-Dir1", - }; - var file1 = fileProvider.AddFile("/File1.cshtml", "content"); - var directory2 = new TestDirectoryFileInfo - { - Name = "Level1-Dir2", - }; - fileProvider.AddDirectoryContent("/", new IFileInfo[] { directory1, file1, directory2 }); - - var file2 = fileProvider.AddFile("/Level1-Dir1/File2.cshtml", "content"); - var file3 = fileProvider.AddFile("/Level1-Dir1/File3.cshtml", "content"); - var file4 = fileProvider.AddFile("/Level1-Dir1/File4.txt", "content"); - var directory3 = new TestDirectoryFileInfo - { - Name = "Level2-Dir1" - }; - fileProvider.AddDirectoryContent("/Level1-Dir1", new IFileInfo[] { file2, directory3, file3, file4 }); - var file5 = fileProvider.AddFile(Path.Combine("Level1-Dir2", "File5.cshtml"), "content"); - fileProvider.AddDirectoryContent("/Level1-Dir2", new IFileInfo[] { file5 }); - fileProvider.AddDirectoryContent("/Level1/Level2", new IFileInfo[0]); - - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, Mock.Of(e => e.ContentRootPath == "BasePath")); - - // Act - var razorFiles = fileSystem.EnumerateItems("/Level1-Dir1"); - - // Assert - Assert.Collection(razorFiles.OrderBy(f => f.FilePath), - file => - { - Assert.Equal("/File2.cshtml", file.FilePath); - Assert.Equal("/Level1-Dir1", file.BasePath); - Assert.Equal(Path.Combine("BasePath", "Level1-Dir1", "File2.cshtml"), file.PhysicalPath); - Assert.Equal(Path.Combine("Level1-Dir1", "File2.cshtml"), file.RelativePhysicalPath); - }, - file => - { - Assert.Equal("/File3.cshtml", file.FilePath); - Assert.Equal("/Level1-Dir1", file.BasePath); - Assert.Equal(Path.Combine("BasePath", "Level1-Dir1", "File3.cshtml"), file.PhysicalPath); - Assert.Equal(Path.Combine("Level1-Dir1", "File3.cshtml"), file.RelativePhysicalPath); - }); - } - - [Fact] - public void GetItem_ReturnsFileFromDisk() - { - var fileProvider = new TestFileProvider("BasePath"); - var file1 = fileProvider.AddFile("/File1.cshtml", "content"); - var file2 = fileProvider.AddFile("/File2.js", "content"); - var file3 = fileProvider.AddFile("/File3.cshtml", "content"); - fileProvider.AddDirectoryContent("/", new IFileInfo[] { file1, file2, file3 }); - - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, Mock.Of(e => e.ContentRootPath == "BasePath")); - - // Act - var item = fileSystem.GetItem("/File3.cshtml"); - - // Assert - Assert.True(item.Exists); - Assert.Equal("/File3.cshtml", item.FilePath); - Assert.Equal(string.Empty, item.BasePath); - Assert.Equal(Path.Combine("BasePath", "File3.cshtml"), item.PhysicalPath); - Assert.Equal("File3.cshtml", item.RelativePhysicalPath); - } - - [Fact] - public void GetItem_PhysicalPathDoesNotStartWithContentRoot_ReturnsNull() - { - var fileProvider = new TestFileProvider("BasePath2"); - var file1 = fileProvider.AddFile("/File1.cshtml", "content"); - var file2 = fileProvider.AddFile("/File2.js", "content"); - var file3 = fileProvider.AddFile("/File3.cshtml", "content"); - fileProvider.AddDirectoryContent("/", new IFileInfo[] { file1, file2, file3 }); - - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, Mock.Of(e => e.ContentRootPath == "BasePath")); - - // Act - var item = fileSystem.GetItem("/File3.cshtml"); - - // Assert - Assert.True(item.Exists); - Assert.Equal("/File3.cshtml", item.FilePath); - Assert.Equal(string.Empty, item.BasePath); - Assert.Equal(Path.Combine("BasePath2", "File3.cshtml"), item.PhysicalPath); - Assert.Null(item.RelativePhysicalPath); - } - - [Fact] - public void GetItem_ReturnsNotFoundResult() - { - // Arrange - var fileProvider = new TestFileProvider("BasePath"); - var file = fileProvider.AddFile("/SomeFile.cshtml", "content"); - fileProvider.AddDirectoryContent("/", new IFileInfo[] { file }); - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, Mock.Of(e => e.ContentRootPath == "BasePath")); - - // Act - var item = fileSystem.GetItem("/NotFound.cshtml"); - - // Assert - Assert.False(item.Exists); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorFileHierarchyTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorFileHierarchyTest.cs new file mode 100644 index 000000000..ef0e6265b --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorFileHierarchyTest.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.Razor +{ + public class RazorFileHierarchyTest + { + [Fact] + public void GetViewStartPaths_ForFileAtRoot() + { + // Arrange + var expected = new[] { "/_ViewStart.cshtml", }; + var path = "/Home.cshtml"; + + // Act + var actual = RazorFileHierarchy.GetViewStartPaths(path); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void GetViewStartPaths_ForForFileInViewsDirectory() + { + // Arrange + var expected = new[] + { + "/Views/Home/_ViewStart.cshtml", + "/Views/_ViewStart.cshtml", + "/_ViewStart.cshtml", + }; + var path = "/Views/Home/Index.cshtml"; + + // Act + var actual = RazorFileHierarchy.GetViewStartPaths(path); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void GetViewStartPaths_ForForFileInAreasDirectory() + { + // Arrange + var expected = new[] + { + "/Areas/Views/MyArea/Home/_ViewStart.cshtml", + "/Areas/Views/MyArea/_ViewStart.cshtml", + "/Areas/Views/_ViewStart.cshtml", + "/Areas/_ViewStart.cshtml", + "/_ViewStart.cshtml", + }; + var path = "/Areas/Views/MyArea/Home/Index.cshtml"; + + // Act + var actual = RazorFileHierarchy.GetViewStartPaths(path); + + // Assert + Assert.Equal(expected, actual); + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineOptionsTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineOptionsTest.cs index 6ca94c806..7809ca3e1 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineOptionsTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineOptionsTest.cs @@ -72,25 +72,5 @@ namespace Microsoft.AspNetCore.Mvc.Razor // Assert Assert.Equal(viewLocations, formats, StringComparer.Ordinal); } - - [Fact] - public void AddRazorOptions_ConfiguresOptionsAsExpected() - { - // Arrange - var services = new ServiceCollection().AddOptions(); - var fileProvider = new TestFileProvider(); - - // Act - var builder = new MvcBuilder(services, new ApplicationPartManager()); - builder.AddRazorOptions(options => - { - options.FileProviders.Add(fileProvider); - }); - var serviceProvider = services.BuildServiceProvider(); - - // Assert - var accessor = serviceProvider.GetRequiredService>(); - Assert.Same(fileProvider, accessor.Value.FileProviders[0]); - } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs index ea2b61d87..619b8ed28 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs @@ -8,17 +8,13 @@ using System.Threading; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; -using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.ViewEngines; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Microsoft.Extensions.WebEncoders.Testing; @@ -904,62 +900,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor pageFactory.Verify(); } - [Fact] - public void FindView_InvokesPageFactoryIfViewStartExpirationTokensHaveExpired() - { - // Arrange - var page1 = Mock.Of(); - var page2 = Mock.Of(); - var viewStart = Mock.Of(); - var sequence = new MockSequence(); - var cancellationTokenSource = new CancellationTokenSource(); - var changeToken = new CancellationChangeToken(cancellationTokenSource.Token); - - var pageFactory = new Mock(); - pageFactory - .InSequence(sequence) - .Setup(p => p.CreateFactory("/Views/bar/baz.cshtml")) - .Returns(GetPageFactoryResult(() => page1)); - pageFactory - .InSequence(sequence) - .Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml")) - .Returns(GetPageFactoryResult(factory: null, changeTokens: new[] { changeToken })) - .Verifiable(); - pageFactory - .InSequence(sequence) - .Setup(p => p.CreateFactory("/Views/bar/baz.cshtml")) - .Returns(GetPageFactoryResult(() => page2)); - pageFactory - .InSequence(sequence) - .Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml")) - .Returns(GetPageFactoryResult(() => viewStart)); - - var fileSystem = new VirtualRazorProjectFileSystem(); - var viewEngine = CreateViewEngine(pageFactory.Object, fileSystem: fileSystem); - var context = GetActionContext(_controllerTestContext); - - // Act 1 - var result1 = viewEngine.FindView(context, "baz", isMainPage: true); - - // Assert 1 - Assert.True(result1.Success); - var view1 = Assert.IsType(result1.View); - Assert.Same(page1, view1.RazorPage); - Assert.Empty(view1.ViewStartPages); - - // Act 2 - cancellationTokenSource.Cancel(); - var result2 = viewEngine.FindView(context, "baz", isMainPage: true); - - // Assert 2 - Assert.True(result2.Success); - var view2 = Assert.IsType(result2.View); - Assert.Same(page2, view2.RazorPage); - var actualViewStart = Assert.Single(view2.ViewStartPages); - Assert.Equal(viewStart, actualViewStart); - pageFactory.Verify(); - } - // This test validates an important perf scenario of RazorViewEngine not constructing // multiple strings for views that do not exist in the file system on a per-request basis. [Fact] @@ -1363,39 +1303,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor Assert.Equal(expected, result.SearchedLocations); } - [Fact] - public void CreateCacheResult_LogsPrecompiledViewFound() - { - // Arrange - var sink = new TestSink(); - var loggerFactory = new TestLoggerFactory(sink, enabled: true); - - var relativePath = "/Views/Foo/details.cshtml"; - var factoryResult = GetPageFactoryResult(() => Mock.Of()); - factoryResult.ViewDescriptor.IsPrecompiled = true; - var pageFactory = new Mock(); - pageFactory - .Setup(p => p.CreateFactory(relativePath)) - .Returns(factoryResult) - .Verifiable(); - - var viewEngine = new RazorViewEngine( - pageFactory.Object, - Mock.Of(), - new HtmlTestEncoder(), - GetOptionsAccessor(expanders: null), - new VirtualRazorProjectFileSystem(), - loggerFactory, - new DiagnosticListener("Microsoft.AspNetCore.Mvc.Razor")); - - // Act - var result = viewEngine.CreateCacheResult(null, relativePath, false); - - // Assert - var logMessage = Assert.Single(sink.Writes); - Assert.Equal($"Using precompiled view for '{relativePath}'.", logMessage.State.ToString()); - } - [Theory] [InlineData("/Test-View.cshtml")] [InlineData("~/Test-View.CSHTML")] @@ -1989,12 +1896,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor private TestableRazorViewEngine CreateViewEngine( IRazorPageFactoryProvider pageFactory = null, - IEnumerable expanders = null, - RazorProjectFileSystem fileSystem = null) + IEnumerable expanders = null) { pageFactory = pageFactory ?? Mock.Of(); - fileSystem = fileSystem ?? new VirtualRazorProjectFileSystem(); - return new TestableRazorViewEngine(pageFactory, GetOptionsAccessor(expanders), fileSystem); + return new TestableRazorViewEngine(pageFactory, GetOptionsAccessor(expanders)); } private static IOptions GetOptionsAccessor( @@ -2003,10 +1908,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor IEnumerable areaViewLocationFormats = null, IEnumerable pageViewLocationFormats = null) { - var optionsSetup = new RazorViewEngineOptionsSetup( - Mock.Of(), - NullLoggerFactory.Instance, - Options.Create(new MvcCompatibilityOptions())); + var optionsSetup = new RazorViewEngineOptionsSetup(Mock.Of()); var options = new RazorViewEngineOptions(); optionsSetup.Configure(options); @@ -2097,20 +1999,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor public TestableRazorViewEngine( IRazorPageFactoryProvider pageFactory, IOptions optionsAccessor) - : this( - pageFactory, - optionsAccessor, - new FileProviderRazorProjectFileSystem( - Mock.Of(a => a.FileProvider == new TestFileProvider()), - Mock.Of())) - { - } - - public TestableRazorViewEngine( - IRazorPageFactoryProvider pageFactory, - IOptions optionsAccessor, - RazorProjectFileSystem fileSystem) - : base(pageFactory, Mock.Of(), new HtmlTestEncoder(), optionsAccessor, fileSystem, NullLoggerFactory.Instance, new DiagnosticListener("Microsoft.AspNetCore.Mvc.Razor")) + : base(pageFactory, Mock.Of(), new HtmlTestEncoder(), optionsAccessor, NullLoggerFactory.Instance, new DiagnosticListener("Microsoft.AspNetCore.Mvc.Razor")) { } diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/ApplicationModels/CompiledPageRouteModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/ApplicationModels/CompiledPageRouteModelProviderTest.cs index a12c877f5..488cd7781 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/ApplicationModels/CompiledPageRouteModelProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/ApplicationModels/CompiledPageRouteModelProviderTest.cs @@ -9,12 +9,10 @@ using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Razor.Hosting; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Xunit; -using static Microsoft.AspNetCore.Razor.Hosting.TestRazorCompiledItem; namespace Microsoft.AspNetCore.Mvc.ApplicationModels { @@ -125,88 +123,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels }); } - [Fact] - public void OnProvidersExecuting_ValidatesChecksum_RejectsPageWhenContentDoesntMatch() - { - // Arrange - var descriptors = new[] - { - CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"), - }), - }; - - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Pages/About.cshtml", "some other content")); - - var provider = CreateProvider(descriptors: descriptors, fileSystem: fileSystem); - var context = new PageRouteModelProviderContext(); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Empty(context.RouteModels); - } - - [Fact] - public void OnProvidersExecuting_ValidatesChecksum_AcceptsPageWhenContentMatches() - { - // Arrange - var descriptors = new[] - { - CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Pages/_ViewImports.cshtml"), - }), - }; - - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Pages/About.cshtml", "some content")); - fileSystem.Add(new TestRazorProjectItem("/Pages/_ViewImports.cshtml", "some import")); - - var provider = CreateProvider(descriptors: descriptors, fileSystem: fileSystem); - var context = new PageRouteModelProviderContext(); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Collection( - context.RouteModels, - result => Assert.Equal("/Pages/About.cshtml", result.RelativePath)); - } - - [Fact] - public void OnProvidersExecuting_ValidatesChecksum_SkipsValidationWhenMainSourceMissing() - { - // Arrange - var descriptors = new[] - { - CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"), - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Pages/_ViewImports.cshtml"), - }), - }; - - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Pages/_ViewImports.cshtml", "some other import")); - - var provider = CreateProvider(descriptors: descriptors, fileSystem: fileSystem); - var context = new PageRouteModelProviderContext(); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Collection( - context.RouteModels, - result => Assert.Equal("/Pages/About.cshtml", result.RelativePath)); - } - [Fact] public void OnProvidersExecuting_AddsModelsForCompiledAreaPages() { @@ -526,19 +442,10 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels var descriptors = new[] { // Page coming from the app - CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"), - }), - CreateVersion_2_1_Descriptor("/Pages/Home.cshtml", metadata: new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/Index.cshtml"), - }), + CreateVersion_2_1_Descriptor("/Pages/About.cshtml"), + CreateVersion_2_1_Descriptor("/Pages/Home.cshtml"), // Page coming from the app - CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[] - { - new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"), - }), + CreateVersion_2_1_Descriptor("/Pages/About.cshtml"), }; var provider = CreateProvider(descriptors: descriptors); @@ -672,17 +579,13 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels private TestCompiledPageRouteModelProvider CreateProvider( RazorPagesOptions options = null, - IList descriptors = null, - VirtualRazorProjectFileSystem fileSystem = null) + IList descriptors = null) { options = options ?? new RazorPagesOptions(); - fileSystem = fileSystem ?? new VirtualRazorProjectFileSystem(); - var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem); var provider = new TestCompiledPageRouteModelProvider( new ApplicationPartManager(), Options.Create(options), - projectEngine, NullLogger.Instance); provider.Descriptors.AddRange(descriptors ?? Array.Empty()); @@ -694,7 +597,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels { return new CompiledViewDescriptor { - IsPrecompiled = true, RelativePath = path, ViewAttribute = new RazorPageAttribute(path, typeof(object), routeTemplate), }; @@ -706,7 +608,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels { return new CompiledViewDescriptor { - IsPrecompiled = true, RelativePath = path, Item = new TestRazorCompiledItem(typeof(object), "mvc.1.0.razor-page", path, metadata ?? Array.Empty()), }; @@ -717,9 +618,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels public TestCompiledPageRouteModelProvider( ApplicationPartManager partManager, IOptions options, - RazorProjectEngine projectEngine, ILogger logger) - : base(partManager, options, projectEngine, logger) + : base(partManager, options, logger) { } diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/ApplicationModels/RazorProjectPageRouteModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/ApplicationModels/RazorProjectPageRouteModelProviderTest.cs deleted file mode 100644 index 70b80a5c6..000000000 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/ApplicationModels/RazorProjectPageRouteModelProviderTest.cs +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.ApplicationModels -{ - public class RazorProjectPageRouteModelProviderTest - { - [Fact] - public void OnProvidersExecuting_ReturnsPagesWithPageDirective() - { - // Arrange - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Pages/Home.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Pages/Test.cshtml", "Hello world")); - - var optionsManager = Options.Create(new RazorPagesOptions()); - optionsManager.Value.RootDirectory = "/"; - var provider = new RazorProjectPageRouteModelProvider(fileSystem, optionsManager, NullLoggerFactory.Instance); - var context = new PageRouteModelProviderContext(); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Collection(context.RouteModels, - model => - { - Assert.Equal("/Pages/Home.cshtml", model.RelativePath); - Assert.Equal("/Pages/Home", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("Pages/Home", selector.AttributeRouteModel.Template)); - Assert.Collection(model.RouteValues.OrderBy(k => k.Key), - kvp => - { - Assert.Equal("page", kvp.Key); - Assert.Equal("/Pages/Home", kvp.Value); - }); - }); - } - - [Fact] - public void OnProvidersExecuting_AddsPagesUnderAreas() - { - // Arrange - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/Manage/Categories.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/Index.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/List.cshtml", "@page \"{sortOrder?}\"")); - fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/_Test.cshtml", "@page")); - - var optionsManager = Options.Create(new RazorPagesOptions { AllowAreas = true }); - var provider = new RazorProjectPageRouteModelProvider(fileSystem, optionsManager, NullLoggerFactory.Instance); - var context = new PageRouteModelProviderContext(); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Collection(context.RouteModels, - model => - { - Assert.Equal("/Areas/Products/Pages/Index.cshtml", model.RelativePath); - Assert.Equal("/Index", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("Products/Index", selector.AttributeRouteModel.Template), - selector => Assert.Equal("Products", selector.AttributeRouteModel.Template)); - Assert.Collection(model.RouteValues.OrderBy(k => k.Key), - kvp => - { - Assert.Equal("area", kvp.Key); - Assert.Equal("Products", kvp.Value); - }, - kvp => - { - Assert.Equal("page", kvp.Key); - Assert.Equal("/Index", kvp.Value); - }); - }, - model => - { - Assert.Equal("/Areas/Products/Pages/List.cshtml", model.RelativePath); - Assert.Equal("/List", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("Products/List/{sortOrder?}", selector.AttributeRouteModel.Template)); - Assert.Collection(model.RouteValues.OrderBy(k => k.Key), - kvp => - { - Assert.Equal("area", kvp.Key); - Assert.Equal("Products", kvp.Value); - }, - kvp => - { - Assert.Equal("page", kvp.Key); - Assert.Equal("/List", kvp.Value); - }); - }, - model => - { - Assert.Equal("/Areas/Products/Pages/_Test.cshtml", model.RelativePath); - Assert.Equal("/_Test", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("Products/_Test", selector.AttributeRouteModel.Template)); - Assert.Collection(model.RouteValues.OrderBy(k => k.Key), - kvp => - { - Assert.Equal("area", kvp.Key); - Assert.Equal("Products", kvp.Value); - }, - kvp => - { - Assert.Equal("page", kvp.Key); - Assert.Equal("/_Test", kvp.Value); - }); - }, - model => - { - Assert.Equal("/Areas/Products/Pages/Manage/Categories.cshtml", model.RelativePath); - Assert.Equal("/Manage/Categories", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("Products/Manage/Categories", selector.AttributeRouteModel.Template)); - Assert.Collection(model.RouteValues.OrderBy(k => k.Key), - kvp => - { - Assert.Equal("area", kvp.Key); - Assert.Equal("Products", kvp.Value); - }, - kvp => - { - Assert.Equal("page", kvp.Key); - Assert.Equal("/Manage/Categories", kvp.Value); - }); - }); - } - - [Fact] - public void OnProvidersExecuting_DoesNotAddPagesUnderAreas_WhenFeatureIsDisabled() - { - // Arrange - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/Manage/Categories.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/Index.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/List.cshtml", "@page \"{sortOrder?}\"")); - fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/_ViewStart.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Pages/About.cshtml", "@page")); - - var optionsManager = Options.Create(new RazorPagesOptions { AllowAreas = false }); - var provider = new RazorProjectPageRouteModelProvider(fileSystem, optionsManager, NullLoggerFactory.Instance); - var context = new PageRouteModelProviderContext(); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Collection(context.RouteModels, - model => - { - Assert.Equal("/Pages/About.cshtml", model.RelativePath); - Assert.Equal("/About", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("About", selector.AttributeRouteModel.Template)); - }); - } - - [Fact] - public void OnProvidersExecuting_DoesNotAddAreaAndNonAreaRoutesForAPage() - { - // Arrange - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Pages/Categories.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/About.cshtml", "@page")); - // We shouldn't add a route for the following paths. - fileSystem.Add(new TestRazorProjectItem("/Areas/Products/Categories.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Areas/Home.cshtml", "@page")); - - var optionsManager = Options.Create(new RazorPagesOptions - { - RootDirectory = "/", - AllowAreas = true, - }); - var provider = new RazorProjectPageRouteModelProvider(fileSystem, optionsManager, NullLoggerFactory.Instance); - var context = new PageRouteModelProviderContext(); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Collection(context.RouteModels, - model => - { - Assert.Equal("/Areas/Products/Pages/Categories.cshtml", model.RelativePath); - Assert.Equal("/Categories", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("Products/Categories", selector.AttributeRouteModel.Template)); - Assert.Collection(model.RouteValues.OrderBy(k => k.Key), - kvp => - { - Assert.Equal("area", kvp.Key); - Assert.Equal("Products", kvp.Value); - }, - kvp => - { - Assert.Equal("page", kvp.Key); - Assert.Equal("/Categories", kvp.Value); - }); - }, - model => - { - Assert.Equal("/About.cshtml", model.RelativePath); - Assert.Equal("/About", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("About", selector.AttributeRouteModel.Template)); - Assert.Collection(model.RouteValues.OrderBy(k => k.Key), - kvp => - { - Assert.Equal("page", kvp.Key); - Assert.Equal("/About", kvp.Value); - }); - }); - } - - [Fact] - public void OnProvidersExecuting_AddsMultipleSelectorsForIndexPages() - { - // Arrange - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Pages/Index.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Pages/Test.cshtml", "Hello world")); - fileSystem.Add(new TestRazorProjectItem("/Pages/Admin/Index.cshtml", "@page \"test\"")); - - var optionsManager = Options.Create(new RazorPagesOptions()); - optionsManager.Value.RootDirectory = "/"; - var provider = new RazorProjectPageRouteModelProvider(fileSystem, optionsManager, NullLoggerFactory.Instance); - var context = new PageRouteModelProviderContext(); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Collection(context.RouteModels, - model => - { - Assert.Equal("/Pages/Index.cshtml", model.RelativePath); - Assert.Equal("/Pages/Index", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("Pages/Index", selector.AttributeRouteModel.Template), - selector => Assert.Equal("Pages", selector.AttributeRouteModel.Template)); - }, - model => - { - Assert.Equal("/Pages/Admin/Index.cshtml", model.RelativePath); - Assert.Equal("/Pages/Admin/Index", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("Pages/Admin/Index/test", selector.AttributeRouteModel.Template), - selector => Assert.Equal("Pages/Admin/test", selector.AttributeRouteModel.Template)); - }); - } - - [Fact] - public void OnProvidersExecuting_AllowsRouteTemplateWithOverridePattern() - { - // Arrange - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Index.cshtml", "@page \"/custom-route\"")); - - var optionsManager = Options.Create(new RazorPagesOptions()); - optionsManager.Value.RootDirectory = "/"; - var provider = new RazorProjectPageRouteModelProvider(fileSystem, optionsManager, NullLoggerFactory.Instance); - var context = new PageRouteModelProviderContext(); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Collection( - context.RouteModels, - model => - { - Assert.Equal("/Index.cshtml", model.RelativePath); - Assert.Equal("/Index", model.ViewEnginePath); - Assert.Collection( - model.Selectors, - selector => Assert.Equal("custom-route", selector.AttributeRouteModel.Template)); - }); - } - - [Fact] - public void OnProvidersExecuting_DiscoversFilesUnderBasePath() - { - // Arrange - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Pages/Index.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Pages/_Layout.cshtml", "")); - fileSystem.Add(new TestRazorProjectItem("/NotPages/Index.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/NotPages/_Layout.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Index.cshtml", "@page")); - - var optionsManager = Options.Create(new RazorPagesOptions()); - optionsManager.Value.RootDirectory = "/Pages"; - var provider = new RazorProjectPageRouteModelProvider(fileSystem, optionsManager, NullLoggerFactory.Instance); - var context = new PageRouteModelProviderContext(); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Collection(context.RouteModels, - model => - { - Assert.Equal("/Pages/Index.cshtml", model.RelativePath); - }); - } - - [Fact] - public void OnProvidersExecuting_DoesNotAddPageDirectivesIfItAlreadyExists() - { - // Arrange - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Pages/Home.cshtml", "@page")); - fileSystem.Add(new TestRazorProjectItem("/Pages/Test.cshtml", "@page")); - - var optionsManager = Options.Create(new RazorPagesOptions()); - optionsManager.Value.RootDirectory = "/"; - var provider = new RazorProjectPageRouteModelProvider(fileSystem, optionsManager, NullLoggerFactory.Instance); - var context = new PageRouteModelProviderContext(); - var pageModel = new PageRouteModel("/Pages/Test.cshtml", "/Pages/Test"); - context.RouteModels.Add(pageModel); - - // Act - provider.OnProvidersExecuting(context); - - // Assert - Assert.Collection(context.RouteModels, - model => Assert.Same(pageModel, model), - model => - { - Assert.Equal("/Pages/Home.cshtml", model.RelativePath); - Assert.Equal("/Pages/Home", model.ViewEnginePath); - Assert.Collection(model.Selectors, - selector => Assert.Equal("Pages/Home", selector.AttributeRouteModel.Template)); - }); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesRazorViewEngineOptionsSetupTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesRazorViewEngineOptionsSetupTest.cs index f23c91328..d9add6f37 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesRazorViewEngineOptionsSetupTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesRazorViewEngineOptionsSetupTest.cs @@ -2,11 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; -using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; @@ -187,10 +185,7 @@ namespace Microsoft.Extensions.DependencyInjection private static RazorViewEngineOptions GetViewEngineOptions() { - var defaultSetup = new RazorViewEngineOptionsSetup( - Mock.Of(), - NullLoggerFactory.Instance, - Options.Options.Create(new MvcCompatibilityOptions())); + var defaultSetup = new RazorViewEngineOptionsSetup(Mock.Of()); var options = new RazorViewEngineOptions(); defaultSetup.Configure(options); diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorChangeProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorChangeProviderTest.cs deleted file mode 100644 index a7973b277..000000000 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorChangeProviderTest.cs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Razor; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Options; -using Microsoft.Extensions.Primitives; -using Moq; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure -{ - public class PageActionDescriptorChangeProviderTest - { - private readonly IHostingEnvironment _hostingEnvironment = Mock.Of(e => e.ContentRootPath == "BasePath"); - - [Fact] - public void GetChangeToken_WatchesAllCshtmlFilesUnderFileSystemRoot() - { - // Arrange - var fileProvider = new Mock(); - fileProvider.Setup(f => f.Watch(It.IsAny())) - .Returns(Mock.Of()); - var accessor = Mock.Of(a => a.FileProvider == fileProvider.Object); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment); - var templateEngine = new RazorTemplateEngine( - RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine, - fileSystem); - var razorPageOptions = Options.Create(new RazorPagesOptions()); - var razorViewEngineOptions = Options.Create(new RazorViewEngineOptions { AllowRecompilingViewsOnFileChange = true }); - var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, accessor, razorPageOptions, razorViewEngineOptions); - - // Act - var changeToken = changeProvider.GetChangeToken(); - - // Assert - fileProvider.Verify(f => f.Watch("/Pages/**/*.cshtml")); - } - - [Theory] - [InlineData("/pages-base-dir")] - [InlineData("/pages-base-dir/")] - public void GetChangeToken_WatchesAllCshtmlFilesUnderSpecifiedRootDirectory(string rootDirectory) - { - // Arrange - var fileProvider = new Mock(); - fileProvider.Setup(f => f.Watch(It.IsAny())) - .Returns(Mock.Of()); - var accessor = Mock.Of(a => a.FileProvider == fileProvider.Object); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment); - var templateEngine = new RazorTemplateEngine( - RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine, - fileSystem); - var options = Options.Create(new RazorPagesOptions()); - options.Value.RootDirectory = rootDirectory; - var razorViewEngineOptions = Options.Create(new RazorViewEngineOptions { AllowRecompilingViewsOnFileChange = true }); - - var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, accessor, options, razorViewEngineOptions); - - // Act - var changeToken = changeProvider.GetChangeToken(); - - // Assert - fileProvider.Verify(f => f.Watch("/pages-base-dir/**/*.cshtml")); - } - - [Fact] - public void GetChangeToken_WatchesFilesUnderAreaRoot() - { - // Arrange - var fileProvider = new Mock(); - fileProvider.Setup(f => f.Watch(It.IsAny())) - .Returns(Mock.Of()); - var accessor = Mock.Of(a => a.FileProvider == fileProvider.Object); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment); - var templateEngine = new RazorTemplateEngine( - RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine, - fileSystem); - var options = Options.Create(new RazorPagesOptions { AllowAreas = true }); - var razorViewEngineOptions = Options.Create(new RazorViewEngineOptions { AllowRecompilingViewsOnFileChange = true }); - var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, accessor, options, razorViewEngineOptions); - - // Act - var changeToken = changeProvider.GetChangeToken(); - - // Assert - fileProvider.Verify(f => f.Watch("/Areas/**/*.cshtml")); - } - - [Fact] - public void GetChangeToken_WatchesViewImportsOutsidePagesRoot_WhenPagesRootIsNested() - { - // Arrange - var fileProvider = new TestFileProvider(); - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment); - var templateEngine = new RazorTemplateEngine( - RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine, - fileSystem); - templateEngine.Options.ImportsFileName = "_ViewImports.cshtml"; - var options = Options.Create(new RazorPagesOptions()); - options.Value.RootDirectory = "/dir1/dir2"; - var razorViewEngineOptions = Options.Create(new RazorViewEngineOptions { AllowRecompilingViewsOnFileChange = true }); - - var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, accessor, options, razorViewEngineOptions); - - // Act & Assert - var compositeChangeToken = Assert.IsType(changeProvider.GetChangeToken()); - Assert.Collection(compositeChangeToken.ChangeTokens, - changeToken => Assert.Same(fileProvider.GetChangeToken("/dir1/_ViewImports.cshtml"), changeToken), - changeToken => Assert.Same(fileProvider.GetChangeToken("/_ViewImports.cshtml"), changeToken), - changeToken => Assert.Same(fileProvider.GetChangeToken("/dir1/dir2/**/*.cshtml"), changeToken)); - } - - [Fact] - public void GetChangeToken_WatchesViewImportsOutsidePagesRoot_WhenAllowAreasIsSpecified() - { - // Arrange - var fileProvider = new TestFileProvider(); - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment); - var templateEngine = new RazorTemplateEngine( - RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine, - fileSystem); - templateEngine.Options.ImportsFileName = "_ViewImports.cshtml"; - var options = Options.Create(new RazorPagesOptions()); - options.Value.RootDirectory = "/dir1/dir2"; - options.Value.AllowAreas = true; - - var razorViewEngineOptions = Options.Create(new RazorViewEngineOptions { AllowRecompilingViewsOnFileChange = true }); - var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, accessor, options, razorViewEngineOptions); - - // Act & Assert - var compositeChangeToken = Assert.IsType(changeProvider.GetChangeToken()); - Assert.Collection(compositeChangeToken.ChangeTokens, - changeToken => Assert.Same(fileProvider.GetChangeToken("/dir1/_ViewImports.cshtml"), changeToken), - changeToken => Assert.Same(fileProvider.GetChangeToken("/_ViewImports.cshtml"), changeToken), - changeToken => Assert.Same(fileProvider.GetChangeToken("/dir1/dir2/**/*.cshtml"), changeToken), - changeToken => Assert.Same(fileProvider.GetChangeToken("/Areas/**/*.cshtml"), changeToken)); - } - - [Fact] - public void GetChangeToken_WatchesViewImportsOutsidePagesRoot_WhenAreaFeatureIsDisabled() - { - // Arrange - var fileProvider = new TestFileProvider(); - var accessor = Mock.Of(a => a.FileProvider == fileProvider); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment); - var templateEngine = new RazorTemplateEngine( - RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine, - fileSystem); - templateEngine.Options.ImportsFileName = "_ViewImports.cshtml"; - var options = Options.Create(new RazorPagesOptions { AllowAreas = false }); - var razorViewEngineOptions = Options.Create(new RazorViewEngineOptions { AllowRecompilingViewsOnFileChange = true }); - - var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, accessor, options, razorViewEngineOptions); - - // Act & Assert - var compositeChangeToken = Assert.IsType(changeProvider.GetChangeToken()); - Assert.Collection(compositeChangeToken.ChangeTokens, - changeToken => Assert.Same(fileProvider.GetChangeToken("/_ViewImports.cshtml"), changeToken), - changeToken => Assert.Same(fileProvider.GetChangeToken("/Pages/**/*.cshtml"), changeToken)); - } - - [Fact] - public void GetChangeToken_DoesNotWatch_WhenOptionIsReset() - { - // Arrange - var fileProvider = new Mock(MockBehavior.Strict); - var accessor = Mock.Of(a => a.FileProvider == fileProvider.Object); - - var fileSystem = new FileProviderRazorProjectFileSystem(accessor, _hostingEnvironment); - var templateEngine = new RazorTemplateEngine( - RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem).Engine, - fileSystem); - templateEngine.Options.ImportsFileName = "_ViewImports.cshtml"; - var options = Options.Create(new RazorPagesOptions()); - var razorViewEngineOptions = Options.Create(new RazorViewEngineOptions()); - - var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, accessor, options, razorViewEngineOptions); - - // Act & Assert - var compositeChangeToken = Assert.IsType(changeProvider.GetChangeToken()); - fileProvider.Verify(f => f.Watch(It.IsAny()), Times.Never()); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionInvokerProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionInvokerProviderTest.cs index 9bd81c1b0..b6b78b06c 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionInvokerProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionInvokerProviderTest.cs @@ -16,7 +16,6 @@ using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging.Abstractions; @@ -28,15 +27,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure { public class PageInvokerProviderTest { - private readonly IHostingEnvironment _hostingEnvironment = Mock.Of(e => e.ContentRootPath == "BasePath"); - [Fact] public void OnProvidersExecuting_WithEmptyModel_PopulatesCacheEntry() { // Arrange var descriptor = new PageActionDescriptor { - RelativePath = "Path1", + RelativePath = "/Path1", FilterDescriptors = new FilterDescriptor[0], }; @@ -89,7 +86,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure // Arrange var descriptor = new PageActionDescriptor { - RelativePath = "Path1", + RelativePath = "/Path1", FilterDescriptors = new FilterDescriptor[0] }; @@ -190,15 +187,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure .Setup(f => f.CreateFactory("/_ViewStart.cshtml")) .Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), factory2)); - var fileSystem = new VirtualRazorProjectFileSystem(); - fileSystem.Add(new TestRazorProjectItem("/Home/Path1/_ViewStart.cshtml", "content1")); - fileSystem.Add(new TestRazorProjectItem("/_ViewStart.cshtml", "content2")); + var fileProvider = new TestFileProvider(); + fileProvider.AddFile("/Home/Path1/_ViewStart.cshtml", "content1"); + fileProvider.AddFile("/_ViewStart.cshtml", "content2"); var invokerProvider = CreateInvokerProvider( loader.Object, CreateActionDescriptorCollection(descriptor), - razorPageFactoryProvider: razorPageFactoryProvider.Object, - fileSystem: fileSystem); + razorPageFactoryProvider: razorPageFactoryProvider.Object); var context = new ActionInvokerProviderContext(new ActionContext() { @@ -223,7 +219,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure // Arrange var descriptor = new PageActionDescriptor { - RelativePath = "Path1", + RelativePath = "/Path1", FilterDescriptors = new FilterDescriptor[0], }; @@ -273,7 +269,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure // Arrange var descriptor = new PageActionDescriptor { - RelativePath = "Path1", + RelativePath = "/Path1", FilterDescriptors = new FilterDescriptor[0], }; @@ -342,15 +338,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure .Setup(l => l.Load(It.IsAny())) .Returns(compiledPageDescriptor); - var fileProvider = new TestFileProvider(); - fileProvider.AddFile("/_ViewStart.cshtml", "page content"); - fileProvider.AddFile("/Pages/_ViewStart.cshtml", "page content"); - fileProvider.AddFile("/Pages/Level1/_ViewStart.cshtml", "page content"); - fileProvider.AddFile("/Pages/Level1/Level2/_ViewStart.cshtml", "page content"); - fileProvider.AddFile("/Pages/Level1/Level3/_ViewStart.cshtml", "page content"); - - var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment); - var mock = new Mock(MockBehavior.Strict); mock .Setup(p => p.CreateFactory("/Pages/Level1/Level2/_ViewStart.cshtml")) @@ -374,8 +361,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure var invokerProvider = CreateInvokerProvider( loader.Object, CreateActionDescriptorCollection(descriptor), - razorPageFactoryProvider: razorPageFactoryProvider, - fileSystem: fileSystem); + razorPageFactoryProvider: razorPageFactoryProvider); // Act var factories = invokerProvider.GetViewStartFactories(compiledPageDescriptor); @@ -415,15 +401,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure // No files var fileProvider = new TestFileProvider(); - var fileSystem = new TestRazorProjectFileSystem(fileProvider, _hostingEnvironment); var invokerProvider = CreateInvokerProvider( loader.Object, CreateActionDescriptorCollection(descriptor), pageProvider: null, modelProvider: null, - razorPageFactoryProvider: pageFactory.Object, - fileSystem: fileSystem); + razorPageFactoryProvider: pageFactory.Object); var compiledDescriptor = CreateCompiledPageActionDescriptor(descriptor); @@ -468,19 +452,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure IActionDescriptorCollectionProvider actionDescriptorProvider, IPageFactoryProvider pageProvider = null, IPageModelFactoryProvider modelProvider = null, - IRazorPageFactoryProvider razorPageFactoryProvider = null, - RazorProjectFileSystem fileSystem = null) + IRazorPageFactoryProvider razorPageFactoryProvider = null) { var tempDataFactory = new Mock(); tempDataFactory .Setup(t => t.GetTempData(It.IsAny())) .Returns((HttpContext context) => new TempDataDictionary(context, Mock.Of())); - if (fileSystem == null) - { - fileSystem = Mock.Of(); - } - var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelBinderFactory = TestModelBinderFactory.CreateDefault(); var mvcOptions = new MvcOptions @@ -509,7 +487,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure Options.Create(new MvcOptions()), Options.Create(new HtmlHelperOptions()), Mock.Of(), - fileSystem, new DiagnosticListener("Microsoft.AspNetCore"), NullLoggerFactory.Instance, new ActionResultTypeMapper()); diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs deleted file mode 100644 index a56633145..000000000 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Logging.Testing; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure -{ - public class PageDirectiveFeatureTest - { - [Fact] - public void TryGetPageDirective_FindsTemplate() - { - // Arrange - var projectItem = new TestRazorProjectItem("Test.cshtml", @"@page ""Some/Path/{value}"" -The rest of the thing"); - var sink = new TestSink(); - var logger = new TestLogger("logger", sink, enabled: true); - - // Act & Assert - Assert.True(PageDirectiveFeature.TryGetPageDirective(logger, projectItem, out var template)); - Assert.Equal("Some/Path/{value}", template); - Assert.Empty(sink.Writes); - } - - [Fact] - public void TryGetPageDirective_NoNewLine() - { - // Arrange - var projectItem = new TestRazorProjectItem("Test.cshtml", @"@page ""Some/Path/{value}"""); - var sink = new TestSink(); - var logger = new TestLogger("logger", sink, enabled: true); - - // Act & Assert - Assert.True(PageDirectiveFeature.TryGetPageDirective(logger, projectItem, out var template)); - Assert.Equal("Some/Path/{value}", template); - Assert.Empty(sink.Writes); - } - - [Fact] - public void TryGetPageDirective_JunkBeforeDirective() - { - // Arrange - var projectItem = new TestRazorProjectItem("Test.cshtml", @"Not a directive @page ""Some/Path/{value}"""); - var sink = new TestSink(); - var logger = new TestLogger("logger", sink, enabled: true); - - // Act & Assert - Assert.False(PageDirectiveFeature.TryGetPageDirective(logger, projectItem, out var template)); - Assert.Null(template); - Assert.Empty(sink.Writes); - } - - [Theory] - [InlineData(@"""Some/Path/{value}")] - [InlineData(@"Some/Path/{value}""")] - public void TryGetPageDirective_WithoutBothQuotes_LogsWarning(string inTemplate) - { - // Arrange - var expected = "The page directive at 'Test.cshtml' is malformed. Please fix the following issues: The 'page' directive expects a string surrounded by double quotes."; - var sink = new TestSink(); - var logger = new TestLogger("logger", sink, enabled: true); - var projectItem = new TestRazorProjectItem("Test.cshtml", $@"@page {inTemplate}"); - - // Act & Assert - Assert.True(PageDirectiveFeature.TryGetPageDirective(logger, projectItem, out var template)); - Assert.Null(template); - Assert.Collection(sink.Writes, - log => - { - Assert.Equal(LogLevel.Warning, log.LogLevel); - Assert.Equal(expected, log.State.ToString()); - }); - } - - [Fact] - public void TryGetPageDirective_NoQuotesAroundPath_LogsWarning() - { - // Arrange - var expected = "The page directive at 'Test.cshtml' is malformed. Please fix the following issues: The 'page' directive expects a string surrounded by double quotes."; - var sink = new TestSink(); - var logger = new TestLogger("logger", sink, enabled: true); - var projectItem = new TestRazorProjectItem("Test.cshtml", @"@page Some/Path/{value}"); - - // Act & Assert - Assert.True(PageDirectiveFeature.TryGetPageDirective(logger, projectItem, out var template)); - Assert.Null(template); - var logs = sink.Writes.Select(w => w.State.ToString().Trim()).ToList(); - Assert.Collection(sink.Writes, - log => - { - Assert.Equal(LogLevel.Warning, log.LogLevel); - Assert.Equal(expected, log.State.ToString()); - }); - } - - [Fact] - public void TryGetPageDirective_NewLineBeforeDirective() - { - // Arrange - var projectItem = new TestRazorProjectItem("Test.cshtml", "\n @page \"Some/Path/{value}\""); - var sink = new TestSink(); - var logger = new TestLogger("logger", sink, enabled: true); - - // Act - Assert.True(PageDirectiveFeature.TryGetPageDirective(logger, projectItem, out var template)); - Assert.Equal("Some/Path/{value}", template); - Assert.Empty(sink.Writes); - } - - [Fact] - public void TryGetPageDirective_Directive_WithoutPathOrContent() - { - // Arrange - var projectItem = new TestRazorProjectItem("Test.cshtml", @"@page"); - - // Act & Assert - Assert.True(PageDirectiveFeature.TryGetPageDirective(NullLogger.Instance, projectItem, out var template)); - Assert.Null(template); - } - - [Fact] - public void TryGetPageDirective_DirectiveWithContent_WithoutPath() - { - // Arrange - var projectItem = new TestRazorProjectItem("Test.cshtml", @"@page -Non-path things"); - var sink = new TestSink(); - var logger = new TestLogger("logger", sink, enabled: true); - - // Act & Assert - Assert.True(PageDirectiveFeature.TryGetPageDirective(logger, projectItem, out var template)); - Assert.Null(template); - Assert.Empty(sink.Writes); - } - - [Fact] - public void TryGetPageDirective_NoDirective() - { - // Arrange - var projectItem = new TestRazorProjectItem("Test.cshtml", @"This is junk -Nobody will use it"); - var sink = new TestSink(); - var logger = new TestLogger("logger", sink, enabled: true); - - // Act & Assert - Assert.False(PageDirectiveFeature.TryGetPageDirective(logger, projectItem, out var template)); - Assert.Null(template); - Assert.Empty(sink.Writes); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/TestRazorProjectFileSystem.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/TestRazorProjectFileSystem.cs deleted file mode 100644 index c24d0bd83..000000000 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/TestRazorProjectFileSystem.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Razor; -using Microsoft.Extensions.FileProviders; -using Moq; - -namespace Microsoft.AspNetCore.Mvc.RazorPages -{ - internal class TestRazorProjectFileSystem : FileProviderRazorProjectFileSystem - { - public TestRazorProjectFileSystem(IFileProvider fileProvider, IHostingEnvironment hostingEnvironment) - :base(GetAccessor(fileProvider), hostingEnvironment) - { - } - - private static IRazorViewEngineFileProviderAccessor GetAccessor(IFileProvider fileProvider) - { - var fileProviderAccessor = new Mock(); - fileProviderAccessor.SetupGet(f => f.FileProvider) - .Returns(fileProvider); - - return fileProviderAccessor.Object; - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Test/IntegrationTest/CompatibilitySwitchIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/IntegrationTest/CompatibilitySwitchIntegrationTest.cs index d16e2b85d..ca8d89466 100644 --- a/test/Microsoft.AspNetCore.Mvc.Test/IntegrationTest/CompatibilitySwitchIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Test/IntegrationTest/CompatibilitySwitchIntegrationTest.cs @@ -53,7 +53,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest Assert.Null(mvcOptions.MaxValidationDepth); Assert.True(apiBehaviorOptions.SuppressUseValidationProblemDetailsForInvalidModelStateResponses); Assert.True(apiBehaviorOptions.SuppressMapClientErrors); - Assert.True(razorViewEngineOptions.AllowRecompilingViewsOnFileChange); Assert.False(razorPagesOptions.AllowDefaultHandlingForOptionsRequests); Assert.False(xmlOptions.AllowRfc7807CompliantProblemDetailsFormat); Assert.False(mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent); @@ -92,7 +91,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest Assert.Null(mvcOptions.MaxValidationDepth); Assert.True(apiBehaviorOptions.SuppressUseValidationProblemDetailsForInvalidModelStateResponses); Assert.True(apiBehaviorOptions.SuppressMapClientErrors); - Assert.True(razorViewEngineOptions.AllowRecompilingViewsOnFileChange); Assert.False(razorPagesOptions.AllowDefaultHandlingForOptionsRequests); Assert.False(xmlOptions.AllowRfc7807CompliantProblemDetailsFormat); Assert.False(mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent); @@ -131,7 +129,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest Assert.Equal(32, mvcOptions.MaxValidationDepth); Assert.False(apiBehaviorOptions.SuppressUseValidationProblemDetailsForInvalidModelStateResponses); Assert.False(apiBehaviorOptions.SuppressMapClientErrors); - Assert.False(razorViewEngineOptions.AllowRecompilingViewsOnFileChange); Assert.True(razorPagesOptions.AllowDefaultHandlingForOptionsRequests); Assert.True(xmlOptions.AllowRfc7807CompliantProblemDetailsFormat); Assert.True(mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent); @@ -170,7 +167,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest Assert.Equal(32, mvcOptions.MaxValidationDepth); Assert.False(apiBehaviorOptions.SuppressUseValidationProblemDetailsForInvalidModelStateResponses); Assert.False(apiBehaviorOptions.SuppressMapClientErrors); - Assert.False(razorViewEngineOptions.AllowRecompilingViewsOnFileChange); Assert.True(razorPagesOptions.AllowDefaultHandlingForOptionsRequests); Assert.True(xmlOptions.AllowRfc7807CompliantProblemDetailsFormat); Assert.True(mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent); diff --git a/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs index 498275a1b..e0c13777a 100644 --- a/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs @@ -210,9 +210,6 @@ namespace Microsoft.AspNetCore.Mvc Assert.Collection(manager.FeatureProviders, feature => Assert.IsType(feature), feature => Assert.IsType(feature), -#pragma warning disable CS0618 // Type or member is obsolete - feature => Assert.IsType(feature), -#pragma warning restore CS0618 // Type or member is obsolete feature => Assert.IsType(feature), feature => Assert.IsType(feature), #pragma warning disable CS0618 // Type or member is obsolete @@ -462,7 +459,6 @@ namespace Microsoft.AspNetCore.Mvc new[] { typeof(CompiledPageRouteModelProvider), - typeof(RazorProjectPageRouteModelProvider), } }, { diff --git a/test/Microsoft.AspNetCore.Mvc.Views.TestCommon/Microsoft.AspNetCore.Mvc.Views.TestCommon.csproj b/test/Microsoft.AspNetCore.Mvc.Views.TestCommon/Microsoft.AspNetCore.Mvc.Views.TestCommon.csproj index 7f84c7e04..2243f4840 100644 --- a/test/Microsoft.AspNetCore.Mvc.Views.TestCommon/Microsoft.AspNetCore.Mvc.Views.TestCommon.csproj +++ b/test/Microsoft.AspNetCore.Mvc.Views.TestCommon/Microsoft.AspNetCore.Mvc.Views.TestCommon.csproj @@ -11,7 +11,6 @@ - diff --git a/test/Microsoft.AspNetCore.Mvc.Views.TestCommon/TestRazorProjectItem.cs b/test/Microsoft.AspNetCore.Mvc.Views.TestCommon/TestRazorProjectItem.cs deleted file mode 100644 index 05b9aee20..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Views.TestCommon/TestRazorProjectItem.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; -using System.Text; - -namespace Microsoft.AspNetCore.Razor.Language -{ - public class TestRazorProjectItem : RazorProjectItem - { - public TestRazorProjectItem( - string filePath, - string content = "Default content", - string physicalPath = null, - string relativePhysicalPath = null, - string basePath = "/") - { - FilePath = filePath; - PhysicalPath = physicalPath; - RelativePhysicalPath = relativePhysicalPath; - BasePath = basePath; - Content = content; - } - - public override string BasePath { get; } - - public override string FilePath { get; } - - public override string PhysicalPath { get; } - - public override string RelativePhysicalPath { get; } - - public override bool Exists { get; } = true; - - public string Content { get; set; } - - public override Stream Read() - { - var stream = new MemoryStream(Encoding.UTF8.GetBytes(Content)); - - return stream; - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Views.TestCommon/VirtualRazorProjectFileSystem.cs b/test/Microsoft.AspNetCore.Mvc.Views.TestCommon/VirtualRazorProjectFileSystem.cs deleted file mode 100644 index 2d848bc7e..000000000 --- a/test/Microsoft.AspNetCore.Mvc.Views.TestCommon/VirtualRazorProjectFileSystem.cs +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; - -namespace Microsoft.AspNetCore.Razor.Language -{ - public class VirtualRazorProjectFileSystem : RazorProjectFileSystem - { - private readonly DirectoryNode _root = new DirectoryNode("/"); - - public override IEnumerable EnumerateItems(string basePath) - { - basePath = NormalizeAndEnsureValidPath(basePath); - var directory = _root.GetDirectory(basePath); - return directory?.EnumerateItems() ?? Enumerable.Empty(); - } - - public override RazorProjectItem GetItem(string path) - { - path = NormalizeAndEnsureValidPath(path); - return _root.GetItem(path) ?? new NotFoundProjectItem(string.Empty, path); - } - - public void Add(RazorProjectItem projectItem) - { - if (projectItem == null) - { - throw new ArgumentNullException(nameof(projectItem)); - } - - var filePath = NormalizeAndEnsureValidPath(projectItem.FilePath); - _root.AddFile(new FileNode(filePath, projectItem)); - } - - // Internal for testing - [DebuggerDisplay("{Path}")] - internal class DirectoryNode - { - public DirectoryNode(string path) - { - Path = path; - } - - public string Path { get; } - - public List Directories { get; } = new List(); - - public List Files { get; } = new List(); - - public void AddFile(FileNode fileNode) - { - var filePath = fileNode.Path; - if (!filePath.StartsWith(Path, StringComparison.OrdinalIgnoreCase)) - { - throw new InvalidOperationException($"File {fileNode.Path} does not belong to {Path}."); - } - - // Look for the first / that appears in the path after the current directory path. - var directoryPath = GetDirectoryPath(filePath); - var directory = GetOrAddDirectory(this, directoryPath, createIfNotExists: true); - Debug.Assert(directory != null); - directory.Files.Add(fileNode); - } - - public DirectoryNode GetDirectory(string path) - { - if (!path.StartsWith(Path, StringComparison.OrdinalIgnoreCase)) - { - throw new InvalidOperationException($"File {path} does not belong to {Path}."); - } - - return GetOrAddDirectory(this, path); - } - - public IEnumerable EnumerateItems() - { - foreach (var file in Files) - { - yield return file.ProjectItem; - } - - foreach (var directory in Directories) - { - foreach (var file in directory.EnumerateItems()) - { - yield return file; - } - } - } - - public RazorProjectItem GetItem(string path) - { - if (!path.StartsWith(Path, StringComparison.OrdinalIgnoreCase)) - { - throw new InvalidOperationException($"File {path} does not belong to {Path}."); - } - - var directoryPath = GetDirectoryPath(path); - var directory = GetOrAddDirectory(this, directoryPath); - if (directory == null) - { - return null; - } - - foreach (var file in directory.Files) - { - var filePath = file.Path; - var directoryLength = directory.Path.Length; - - // path, filePath -> /Views/Home/Index.cshtml - // directory.Path -> /Views/Home/ - // We only need to match the file name portion since we've already matched the directory segment. - if (string.Compare(path, directoryLength, filePath, directoryLength, path.Length - directoryLength, StringComparison.OrdinalIgnoreCase) == 0) - { - return file.ProjectItem; - } - } - - return null; - } - - private static string GetDirectoryPath(string path) - { - // /dir1/dir2/file.cshtml -> /dir1/dir2/ - var fileNameIndex = path.LastIndexOf('/'); - if (fileNameIndex == -1) - { - return path; - } - - return path.Substring(0, fileNameIndex + 1); - } - - private static DirectoryNode GetOrAddDirectory( - DirectoryNode directory, - string path, - bool createIfNotExists = false) - { - Debug.Assert(!string.IsNullOrEmpty(path)); - if (path[path.Length - 1] != '/') - { - path += '/'; - } - - int index; - while ((index = path.IndexOf('/', directory.Path.Length)) != -1 && index != path.Length) - { - var subDirectory = FindSubDirectory(directory, path); - - if (subDirectory == null) - { - if (createIfNotExists) - { - var directoryPath = path.Substring(0, index + 1); // + 1 to include trailing slash - subDirectory = new DirectoryNode(directoryPath); - directory.Directories.Add(subDirectory); - } - else - { - return null; - } - } - - directory = subDirectory; - } - - return directory; - } - - private static DirectoryNode FindSubDirectory(DirectoryNode parentDirectory, string path) - { - for (var i = 0; i < parentDirectory.Directories.Count; i++) - { - // ParentDirectory.Path -> /Views/Home/ - // CurrentDirectory.Path -> /Views/Home/SubDir/ - // Path -> /Views/Home/SubDir/MorePath/File.cshtml - // Each invocation of FindSubDirectory returns the immediate subdirectory along the path to the file. - - var currentDirectory = parentDirectory.Directories[i]; - var directoryPath = currentDirectory.Path; - var startIndex = parentDirectory.Path.Length; - var directoryNameLength = directoryPath.Length - startIndex; - - if (string.Compare(path, startIndex, directoryPath, startIndex, directoryPath.Length - startIndex, StringComparison.OrdinalIgnoreCase) == 0) - { - return currentDirectory; - } - } - - return null; - } - } - - // Internal for testing - [DebuggerDisplay("{Path}")] - internal struct FileNode - { - public FileNode(string path, RazorProjectItem projectItem) - { - Path = path; - ProjectItem = projectItem; - } - - public string Path { get; } - - public RazorProjectItem ProjectItem { get; } - } - - private class NotFoundProjectItem : RazorProjectItem - { - public NotFoundProjectItem(string basePath, string path) - { - BasePath = basePath; - FilePath = path; - } - - public override string BasePath { get; } - - public override string FilePath { get; } - - public override bool Exists => false; - - public override string PhysicalPath => throw new NotSupportedException(); - - public override Stream Read() => throw new NotSupportedException(); - } - } -} \ No newline at end of file diff --git a/test/WebSites/ControllersFromServicesWebSite/AssemblyMetadataReferenceFeatureProvider.cs b/test/WebSites/ControllersFromServicesWebSite/AssemblyMetadataReferenceFeatureProvider.cs deleted file mode 100644 index 63f1877a4..000000000 --- a/test/WebSites/ControllersFromServicesWebSite/AssemblyMetadataReferenceFeatureProvider.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.AspNetCore.Mvc.Razor.Compilation; -using Microsoft.CodeAnalysis; - -namespace ControllersFromServicesWebSite -{ -#pragma warning disable CS0618 // Type or member is obsolete - public class AssemblyMetadataReferenceFeatureProvider : IApplicationFeatureProvider - { - public void PopulateFeature(IEnumerable parts, MetadataReferenceFeature feature) - { - var currentAssembly = GetType().GetTypeInfo().Assembly; - feature.MetadataReferences.Add(MetadataReference.CreateFromFile(currentAssembly.Location)); - } - } -#pragma warning restore CS0618 // Type or member is obsolete -} diff --git a/test/WebSites/ControllersFromServicesWebSite/Startup.cs b/test/WebSites/ControllersFromServicesWebSite/Startup.cs index b11534fed..ac2553128 100644 --- a/test/WebSites/ControllersFromServicesWebSite/Startup.cs +++ b/test/WebSites/ControllersFromServicesWebSite/Startup.cs @@ -32,7 +32,13 @@ namespace ControllersFromServicesWebSite typeof(ComponentFromServicesViewComponent), typeof(InServicesTagHelper))); - manager.FeatureProviders.Add(new AssemblyMetadataReferenceFeatureProvider()); + var relatedAssenbly = RelatedAssemblyAttribute + .GetRelatedAssemblies(GetType().Assembly, throwOnError: true) + .SingleOrDefault(); + foreach (var part in CompiledRazorAssemblyApplicationPartFactory.GetDefaultApplicationParts(relatedAssenbly)) + { + manager.ApplicationParts.Add(part); + } }) .AddControllersAsServices() .AddViewComponentsAsServices() diff --git a/test/WebSites/RazorPagesWebSite/NonWatchingPhysicalFileProvider.cs b/test/WebSites/RazorPagesWebSite/NonWatchingPhysicalFileProvider.cs deleted file mode 100644 index c93dba01e..000000000 --- a/test/WebSites/RazorPagesWebSite/NonWatchingPhysicalFileProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Primitives; - -namespace RazorPagesWebSite -{ - public class NonWatchingPhysicalFileProvider : PhysicalFileProvider, IFileProvider - { - public NonWatchingPhysicalFileProvider(string root) : base(root) - { - } - - IChangeToken IFileProvider.Watch(string filter) => throw new ArgumentException("This method should not be called."); - } -} diff --git a/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs b/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs index 93d36cea3..bec3f9127 100644 --- a/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs +++ b/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs @@ -35,14 +35,6 @@ namespace RazorPagesWebSite options.Conventions.Add(new CustomModelTypeConvention()); }) .SetCompatibilityVersion(CompatibilityVersion.Latest); - - // Ensure we don't have code paths that call IFileProvider.Watch in the default code path. - // Comment this code block if you happen to run this site in Development. - builder.AddRazorOptions(options => - { - options.FileProviders.Clear(); - options.FileProviders.Add(new NonWatchingPhysicalFileProvider(_hostingEnvironment.ContentRootPath)); - }); } public void Configure(IApplicationBuilder app) diff --git a/test/WebSites/RazorWebSite/Controllers/PartialViewEngineController.cs b/test/WebSites/RazorWebSite/Controllers/PartialViewEngineController.cs index d7fe08f71..c294519f6 100644 --- a/test/WebSites/RazorWebSite/Controllers/PartialViewEngineController.cs +++ b/test/WebSites/RazorWebSite/Controllers/PartialViewEngineController.cs @@ -14,7 +14,7 @@ namespace RazorWebSite.Controllers public IActionResult ViewWithFullPath() { - return PartialView("/Views/ViewEngine/ViewWithFullPath.rzr"); + return PartialView("/Views/ViewEngine/ViewWithFullPath.cshtml"); } public IActionResult PartialViewWithNamePassedIn() diff --git a/test/WebSites/RazorWebSite/Controllers/ViewEngineController.cs b/test/WebSites/RazorWebSite/Controllers/ViewEngineController.cs index 1a36f3774..30693412c 100644 --- a/test/WebSites/RazorWebSite/Controllers/ViewEngineController.cs +++ b/test/WebSites/RazorWebSite/Controllers/ViewEngineController.cs @@ -15,7 +15,7 @@ namespace RazorWebSite.Controllers public IActionResult ViewWithFullPath() { - return View("/Views/ViewEngine/ViewWithFullPath.rzr"); + return View("/Views/ViewEngine/ViewWithFullPath.cshtml"); } public IActionResult ViewWithRelativePath() diff --git a/test/WebSites/RazorWebSite/Startup.cs b/test/WebSites/RazorWebSite/Startup.cs index c4b02e356..1be982ca9 100644 --- a/test/WebSites/RazorWebSite/Startup.cs +++ b/test/WebSites/RazorWebSite/Startup.cs @@ -27,10 +27,6 @@ namespace RazorWebSite .AddMvc() .AddRazorOptions(options => { - options.FileProviders.Add(new EmbeddedFileProvider( - typeof(Startup).GetTypeInfo().Assembly, - $"{nameof(RazorWebSite)}.EmbeddedResources")); - options.FileProviders.Add(updateableFileProvider); options.ViewLocationExpanders.Add(new NonMainPageViewLocationExpander()); options.ViewLocationExpanders.Add(new BackSlashExpander()); }) diff --git a/test/WebSites/RazorWebSite/Views/ViewEngine/ViewWithFullPath.rzr b/test/WebSites/RazorWebSite/Views/ViewEngine/ViewWithFullPath.cshtml similarity index 100% rename from test/WebSites/RazorWebSite/Views/ViewEngine/ViewWithFullPath.rzr rename to test/WebSites/RazorWebSite/Views/ViewEngine/ViewWithFullPath.cshtml