diff --git a/test/WebSites/MvcTagHelpersWebSite/Views/Catalog_CacheTagHelper/_ViewStart.cshtml b/samples/TagHelperSample.Web/Views/_GlobalImport.cshtml similarity index 100% rename from test/WebSites/MvcTagHelpersWebSite/Views/Catalog_CacheTagHelper/_ViewStart.cshtml rename to samples/TagHelperSample.Web/Views/_GlobalImport.cshtml diff --git a/samples/TagHelperSample.Web/Views/_ViewStart.cshtml b/samples/TagHelperSample.Web/Views/_ViewStart.cshtml index 696ca6af26..1af6e49466 100644 --- a/samples/TagHelperSample.Web/Views/_ViewStart.cshtml +++ b/samples/TagHelperSample.Web/Views/_ViewStart.cshtml @@ -1,5 +1,3 @@ -@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" - -@{ +@{ Layout = "_Layout"; } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs index f74ac1db77..20490031ff 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs @@ -13,8 +13,7 @@ using Microsoft.AspNet.Razor.Parser; namespace Microsoft.AspNet.Mvc.Razor.Directives { /// - /// A utility type for supporting inheritance of tag helpers and chunks into a page from applicable _ViewStart - /// pages. + /// A utility type for supporting inheritance of directives into a page from applicable _GlobalImport pages. /// public class ChunkInheritanceUtility { @@ -25,9 +24,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives /// /// Initializes a new instance of . /// - /// The used to parse _ViewStart pages. - /// that caches _ViewStart - /// instances. + /// The used to parse _GlobalImport pages. + /// that caches instances. + /// /// Sequence of s inherited by default. public ChunkInheritanceUtility([NotNull] MvcRazorHost razorHost, [NotNull] ICodeTreeCache codeTreeCache, @@ -39,25 +38,28 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives } /// - /// Gets an ordered of parsed for each _ViewStart that - /// is applicable to the page located at . The list is ordered so that the - /// for the _ViewStart closest to the in the fileProvider - /// appears first. + /// Gets an ordered of parsed for each + /// _GlobalImport that is applicable to the page located at . The list is + /// ordered so that the for the _GlobalImport closest to the + /// in the file system appears first. /// /// The path of the page to locate inherited chunks for. - /// A of parsed _ViewStart s. + /// A of parsed _GlobalImport + /// s. public IReadOnlyList GetInheritedCodeTrees([NotNull] string pagePath) { var inheritedCodeTrees = new List(); var templateEngine = new RazorTemplateEngine(_razorHost); - foreach (var viewStartPath in ViewStartUtility.GetViewStartLocations(pagePath)) + foreach (var globalImportPath in ViewHierarchyUtility.GetGlobalImportLocations(pagePath)) { - // viewStartPath contains the app-relative path of the ViewStart. - // Since the parsing of a _ViewStart would cause parent _ViewStarts to be parsed - // we need to ensure the paths are app-relative to allow the GetViewStartLocations - // for the current _ViewStart to succeed. - var codeTree = _codeTreeCache.GetOrAdd(viewStartPath, - fileInfo => ParseViewFile(templateEngine, fileInfo, viewStartPath)); + // globalImportPath contains the app-relative path of the _GlobalImport. + // Since the parsing of a _GlobalImport would cause parent _GlobalImports to be parsed + // we need to ensure the paths are app-relative to allow the GetGlobalFileLocations + // for the current _GlobalImport to succeed. + var codeTree = _codeTreeCache.GetOrAdd(globalImportPath, + fileInfo => ParseViewFile(templateEngine, + fileInfo, + globalImportPath)); if (codeTree != null) { @@ -70,10 +72,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives /// /// Merges inherited by default and instances produced by parsing - /// _ViewStart files into the specified . + /// _GlobalImport files into the specified . /// /// The to merge in to. - /// inherited from _ViewStart + /// inherited from _GlobalImport /// files. /// The list of chunks to merge. public void MergeInheritedCodeTrees([NotNull] CodeTree codeTree, @@ -96,7 +98,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives // In the second phase we invoke IChunkMerger.Merge for each chunk that has a mapped merger. // During this phase, the merger can either add to the CodeTree or ignore the chunk based on the merging // rules. - // Read the chunks outside in - that is chunks from the _ViewStart closest to the page get merged in first + // Read the chunks outside in - that is chunks from the _GlobalImport closest to the page get merged in first // and the furthest one last. This allows the merger to ignore a directive like @model that was previously // seen. var chunksToMerge = inheritedCodeTrees.SelectMany(tree => tree.Chunks) diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/ViewHierarchyUtility.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/ViewHierarchyUtility.cs new file mode 100644 index 0000000000..1393a657a4 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/ViewHierarchyUtility.cs @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.AspNet.Mvc.Razor +{ + /// + /// Contains methods to locate _ViewStart.cshtml and _GlobalImport.cshtml + /// + public static class ViewHierarchyUtility + { + private const string ViewStartFileName = "_ViewStart.cshtml"; + private const string GlobalImportFileName = "_GlobalImport.cshtml"; + + /// + /// Gets the view start locations that are applicable to the specified path. + /// + /// The application relative path of the file to locate + /// _ViewStarts for. + /// A sequence of paths that represent potential view start locations. + /// + /// This method returns paths starting from the directory of and + /// moves upwards until it hits the application root. + /// e.g. + /// /Views/Home/View.cshtml -> [ /Views/Home/_ViewStart.cshtml, /Views/_ViewStart.cshtml, /_ViewStart.cshtml ] + /// + public static IEnumerable GetViewStartLocations(string applicationRelativePath) + { + return GetHierarchicalPath(applicationRelativePath, ViewStartFileName); + } + + /// + /// Gets the locations for _GlobalImports that are applicable to the specified path. + /// + /// The application relative path of the file to locate + /// _GlobalImports for. + /// A sequence of paths that represent potential _GlobalImport locations. + /// + /// This method returns paths starting from the directory of and + /// moves upwards until it hits the application root. + /// e.g. + /// /Views/Home/View.cshtml -> [ /Views/Home/_GlobalImport.cshtml, /Views/_GlobalImport.cshtml, + /// /_GlobalImport.cshtml ] + /// + public static IEnumerable GetGlobalImportLocations(string applicationRelativePath) + { + return GetHierarchicalPath(applicationRelativePath, GlobalImportFileName); + } + + private static IEnumerable GetHierarchicalPath(string relativePath, string fileName) + { + if (string.IsNullOrEmpty(relativePath)) + { + return Enumerable.Empty(); + } + + if (relativePath.StartsWith("~/", StringComparison.Ordinal)) + { + relativePath = relativePath.Substring(2); + } + + if (relativePath.StartsWith("/", StringComparison.Ordinal)) + { + relativePath = relativePath.Substring(1); + } + + if (Path.IsPathRooted(relativePath)) + { + // If the path looks like it's not app relative, don't attempt to construct paths. + return Enumerable.Empty(); + } + + if (string.Equals(Path.GetFileName(relativePath), fileName, StringComparison.OrdinalIgnoreCase)) + { + // If the specified path is for the file hierarchy being constructed, then the first file that applies + // to it is in a parent directory. + relativePath = Path.GetDirectoryName(relativePath); + if (string.IsNullOrEmpty(relativePath)) + { + return Enumerable.Empty(); + } + } + + var locations = new List(); + while (!string.IsNullOrEmpty(relativePath)) + { + relativePath = Path.GetDirectoryName(relativePath); + var path = Path.Combine(relativePath, fileName); + locations.Add(path); + } + + return locations; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/ViewStartUtility.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/ViewStartUtility.cs deleted file mode 100644 index 45b2bc48e0..0000000000 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/ViewStartUtility.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Microsoft.AspNet.Mvc.Razor -{ - /// - /// Contains the methods to locate _ViewStart.cshtml - /// - public static class ViewStartUtility - { - private const string ViewStartFileName = "_ViewStart.cshtml"; - - /// - /// Determines if the given path represents a view start file. - /// - /// The path to inspect. - /// True if the path is a view start file, false otherwise. - public static bool IsViewStart([NotNull] string path) - { - var fileName = Path.GetFileName(path); - return string.Equals(ViewStartFileName, fileName, StringComparison.OrdinalIgnoreCase); - } - - /// - /// Gets the view start locations that are applicable to the specified path. - /// - /// The application relative path of the file to locate - /// _ViewStarts for. - /// A sequence of paths that represent potential view start locations. - /// - /// This method returns paths starting from the directory of and - /// moves upwards until it hits the application root. - /// e.g. - /// /Views/Home/View.cshtml -> [ /Views/Home/_ViewStart.cshtml, /Views/_ViewStart.cshtml, /_ViewStart.cshtml ] - /// - public static IEnumerable GetViewStartLocations(string applicationRelativePath) - { - if (string.IsNullOrEmpty(applicationRelativePath)) - { - return Enumerable.Empty(); - } - - if (applicationRelativePath.StartsWith("~/", StringComparison.Ordinal)) - { - applicationRelativePath = applicationRelativePath.Substring(2); - } - - if (applicationRelativePath.StartsWith("/", StringComparison.Ordinal)) - { - applicationRelativePath = applicationRelativePath.Substring(1); - } - - if (Path.IsPathRooted(applicationRelativePath)) - { - // If the path looks like it's app relative, don't attempt to construct _ViewStart paths. - return Enumerable.Empty(); - } - - if (IsViewStart(applicationRelativePath)) - { - // If the specified path is a ViewStart file, then the first view start that applies to it is the - // parent view start. - applicationRelativePath = Path.GetDirectoryName(applicationRelativePath); - if (string.IsNullOrEmpty(applicationRelativePath)) - { - return Enumerable.Empty(); - } - } - - var viewStartLocations = new List(); - while (!string.IsNullOrEmpty(applicationRelativePath)) - { - applicationRelativePath = Path.GetDirectoryName(applicationRelativePath); - var viewStartPath = Path.Combine(applicationRelativePath, ViewStartFileName); - viewStartLocations.Add(viewStartPath); - } - - return viewStartLocations; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCache.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCache.cs index bc60f8b2e7..26b705421b 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCache.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCache.cs @@ -57,17 +57,17 @@ namespace Microsoft.AspNet.Mvc.Razor } } - // Set up ViewStarts + // Set up _GlobalImports foreach (var entry in cacheEntries) { - var viewStartLocations = ViewStartUtility.GetViewStartLocations(entry.RelativePath); - foreach (var location in viewStartLocations) + var globalFileLocations = ViewHierarchyUtility.GetGlobalImportLocations(entry.RelativePath); + foreach (var location in globalFileLocations) { - var viewStartEntry = _cache.Get(location); - if (viewStartEntry != null) + var globalFileEntry = _cache.Get(location); + if (globalFileEntry != null) { - // Add the the composite _ViewStart entry as a dependency. - entry.AssociatedViewStartEntry = viewStartEntry; + // Add the the composite _GlobalImport entry as a dependency. + entry.AssociatedGlobalFileEntry = globalFileEntry; break; } } @@ -106,7 +106,7 @@ namespace Microsoft.AspNet.Mvc.Razor else if (cacheEntry.IsPreCompiled && !cacheEntry.IsValidatedPreCompiled) { // For precompiled views, the first time the entry is read, we need to ensure that no changes were made - // either to the file associated with this entry, or any _ViewStart associated with it between the time + // either to the file associated with this entry, or any _GlobalImport associated with it between the time // the View was precompiled and the time EnsureInitialized was called. For later iterations, we can // rely on expiration triggers ensuring the validity of the entry. @@ -123,9 +123,9 @@ namespace Microsoft.AspNet.Mvc.Razor return OnCacheMiss(relativeFileInfo, normalizedPath, compile); } - if (AssociatedViewStartsChanged(cacheEntry, compile)) + if (AssociatedGlobalFilesChanged(cacheEntry, compile)) { - // Recompile if the view starts have changed since the entry was created. + // Recompile if _GlobalImports have changed since the entry was created. return OnCacheMiss(relativeFileInfo, normalizedPath, compile); } @@ -192,8 +192,8 @@ namespace Microsoft.AspNet.Mvc.Razor var entry = (CompilerCacheEntry)cacheSetContext.State; cacheSetContext.AddExpirationTrigger(_fileProvider.Watch(entry.RelativePath)); - var viewStartLocations = ViewStartUtility.GetViewStartLocations(cacheSetContext.Key); - foreach (var location in viewStartLocations) + var globalImportPaths = ViewHierarchyUtility.GetGlobalImportLocations(cacheSetContext.Key); + foreach (var location in globalImportPaths) { cacheSetContext.AddExpirationTrigger(_fileProvider.Watch(location)); } @@ -201,31 +201,31 @@ namespace Microsoft.AspNet.Mvc.Razor return entry; } - private bool AssociatedViewStartsChanged(CompilerCacheEntry entry, - Func compile) + private bool AssociatedGlobalFilesChanged(CompilerCacheEntry entry, + Func compile) { - var viewStartEntry = GetCompositeViewStartEntry(entry.RelativePath, compile); - return entry.AssociatedViewStartEntry != viewStartEntry; + var globalFileEntry = GetCompositeGlobalFileEntry(entry.RelativePath, compile); + return entry.AssociatedGlobalFileEntry != globalFileEntry; } - // Returns the entry for the nearest _ViewStart that the file inherits directives from. Since _ViewStart - // entries are affected by other _ViewStart entries that are in the path hierarchy, the returned value - // represents the composite result of performing a cache check on individual _ViewStart entries. - private CompilerCacheEntry GetCompositeViewStartEntry(string relativePath, + // Returns the entry for the nearest _GlobalImport that the file inherits directives from. Since _GlobalImport + // entries are affected by other _GlobalImport entries that are in the path hierarchy, the returned value + // represents the composite result of performing a cache check on individual _GlobalImport entries. + private CompilerCacheEntry GetCompositeGlobalFileEntry(string relativePath, Func compile) { - var viewStartLocations = ViewStartUtility.GetViewStartLocations(relativePath); - foreach (var viewStartLocation in viewStartLocations) + var globalImportLocations = ViewHierarchyUtility.GetGlobalImportLocations(relativePath); + foreach (var globalImport in globalImportLocations) { - var getOrAddResult = GetOrAddCore(viewStartLocation, compile); + var getOrAddResult = GetOrAddCore(globalImport, compile); if (getOrAddResult != null) { - // This is the nearest _ViewStart that exists on disk. + // This is the nearest _GlobalImport that exists on disk. return getOrAddResult.CompilerCacheEntry; } } - // No _ViewStarts discovered. + // No _GlobalImports discovered. return null; } @@ -240,8 +240,7 @@ namespace Microsoft.AspNet.Mvc.Razor return path; } - internal static IEnumerable - GetFileInfos(IEnumerable assemblies) + internal static IEnumerable GetFileInfos(IEnumerable assemblies) { return assemblies.SelectMany(a => a.ExportedTypes) .Where(Match) diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCacheEntry.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCacheEntry.cs index 5e51be5605..49c303ddb8 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCacheEntry.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCacheEntry.cs @@ -78,10 +78,10 @@ namespace Microsoft.AspNet.Mvc.Razor public bool IsPreCompiled { get; } /// - /// Gets or sets the for the nearest ViewStart that the compiled type + /// Gets or sets the for the nearest _GlobalImport that the compiled type /// depends on. /// - public CompilerCacheEntry AssociatedViewStartEntry { get; set; } + public CompilerCacheEntry AssociatedGlobalFileEntry { get; set; } /// /// Gets or sets a flag that determines if the validity of this cache entry was performed at runtime. diff --git a/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs b/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs index 326e6b5ad8..2cbf4596bf 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs @@ -121,9 +121,9 @@ namespace Microsoft.AspNet.Mvc.Razor if (entry != null) { cacheSetContext.AddExpirationTrigger(_fileProvider.Watch(fileInfo.RelativePath)); - foreach (var viewStartPath in ViewStartUtility.GetViewStartLocations(fileInfo.RelativePath)) + foreach (var path in ViewHierarchyUtility.GetGlobalImportLocations(fileInfo.RelativePath)) { - cacheSetContext.AddExpirationTrigger(_fileProvider.Watch(viewStartPath)); + cacheSetContext.AddExpirationTrigger(_fileProvider.Watch(path)); } } diff --git a/src/Microsoft.AspNet.Mvc.Razor/ViewStartProvider.cs b/src/Microsoft.AspNet.Mvc.Razor/ViewStartProvider.cs index 8ca80e6958..8d0d34ea4b 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/ViewStartProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/ViewStartProvider.cs @@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.Razor /// public IEnumerable GetViewStartPages([NotNull] string path) { - var viewStartLocations = ViewStartUtility.GetViewStartLocations(path); + var viewStartLocations = ViewHierarchyUtility.GetViewStartLocations(path); var viewStarts = viewStartLocations.Select(_pageFactory.CreateInstance) .Where(p => p != null) .ToArray(); diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/DirectivesTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/DirectivesTest.cs index fd576b7ee4..b52f59b3c6 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/DirectivesTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/DirectivesTest.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests var client = server.CreateClient(); // Act - var body = await client.GetStringAsync("http://localhost/Directives/ViewInheritsInjectAndUsingsFromViewStarts"); + var body = await client.GetStringAsync("http://localhost/Directives/ViewInheritsInjectAndUsingsFromGlobalImports"); // Assert Assert.Equal(expected, body.Trim()); @@ -37,7 +37,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests var client = server.CreateClient(); // Act - var body = await client.GetStringAsync("http://localhost/Directives/ViewInheritsBasePageFromViewStarts"); + var body = await client.GetStringAsync("http://localhost/Directives/ViewInheritsBasePageFromGlobalImports"); // Assert Assert.Equal(expected, body.Trim()); diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/PrecompilationTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/PrecompilationTest.cs index b07461b97d..c0d75f60d8 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/PrecompilationTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/PrecompilationTest.cs @@ -32,6 +32,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests var layoutContent = File.ReadAllText(Path.Combine(viewsDirectory, "Layout.cshtml")); var indexContent = File.ReadAllText(Path.Combine(viewsDirectory, "Index.cshtml")); var viewstartContent = File.ReadAllText(Path.Combine(viewsDirectory, "_ViewStart.cshtml")); + var globalContent = File.ReadAllText(Path.Combine(viewsDirectory, "_GlobalImport.cshtml")); var server = TestServer.Create(_services, _app); var client = server.CreateClient(); @@ -65,57 +66,54 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.Equal(assemblyName, response2.Index); // Act - 3 - // Touch the _ViewStart file and verify it causes all files to recompile. + // Touch the _ViewStart file and verify it is is dynamically compiled. await TouchFile(viewsDirectory, "_ViewStart.cshtml"); responseContent = await client.GetStringAsync("http://localhost/Home/Index"); // Assert - 3 var response3 = new ParsedResponse(responseContent); Assert.NotEqual(assemblyName, response3.ViewStart); - Assert.NotEqual(assemblyName, response3.Index); - Assert.NotEqual(response2.Layout, response3.Layout); + Assert.Equal(assemblyName, response3.Index); + Assert.Equal(response2.Layout, response3.Layout); // Act - 4 - // Touch Index file and verify it is the only page that recompiles. - await TouchFile(viewsDirectory, "Index.cshtml"); + // Touch the _GlobalImport file and verify it causes all files to recompile. + await TouchFile(viewsDirectory, "_GlobalImport.cshtml"); responseContent = await client.GetStringAsync("http://localhost/Home/Index"); // Assert - 4 var response4 = new ParsedResponse(responseContent); - // Layout and _ViewStart should not have changed. - Assert.Equal(response3.Layout, response4.Layout); - Assert.Equal(response3.ViewStart, response4.ViewStart); + Assert.NotEqual(response3.ViewStart, response4.ViewStart); Assert.NotEqual(response3.Index, response4.Index); + Assert.NotEqual(response3.Layout, response4.Layout); // Act - 5 - // Touch the _ViewStart file. This time, we'll verify the Non-precompiled -> Non-precompiled workflow. - await TouchFile(viewsDirectory, "_ViewStart.cshtml"); + // Touch Index file and verify it is the only page that recompiles. + await TouchFile(viewsDirectory, "Index.cshtml"); responseContent = await client.GetStringAsync("http://localhost/Home/Index"); // Assert - 5 var response5 = new ParsedResponse(responseContent); - // Everything should've recompiled. - Assert.NotEqual(response4.ViewStart, response5.ViewStart); + // Layout and _ViewStart should not have changed. + Assert.Equal(response4.Layout, response5.Layout); + Assert.Equal(response4.ViewStart, response5.ViewStart); Assert.NotEqual(response4.Index, response5.Index); - Assert.NotEqual(response4.Layout, response5.Layout); // Act - 6 - // Add a new _ViewStart file - File.WriteAllText(Path.Combine(viewsDirectory, "..", "_ViewStart.cshtml"), string.Empty); - await Task.Delay(_cacheDelayInterval); + // Touch the _GlobalImport file. This time, we'll verify the Non-precompiled -> Non-precompiled workflow. + await TouchFile(viewsDirectory, "_GlobalImport.cshtml"); responseContent = await client.GetStringAsync("http://localhost/Home/Index"); // Assert - 6 - // Everything should've recompiled. var response6 = new ParsedResponse(responseContent); + // Everything should've recompiled. Assert.NotEqual(response5.ViewStart, response6.ViewStart); Assert.NotEqual(response5.Index, response6.Index); Assert.NotEqual(response5.Layout, response6.Layout); // Act - 7 - // Remove new _ViewStart file - File.Delete(Path.Combine(viewsDirectory, "..", "_ViewStart.cshtml")); - await Task.Delay(_cacheDelayInterval); + // Add a new _GlobalImport file + File.WriteAllText(Path.Combine(viewsDirectory, "..", "_GlobalImport.cshtml"), string.Empty); responseContent = await client.GetStringAsync("http://localhost/Home/Index"); // Assert - 7 @@ -126,20 +124,33 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.NotEqual(response6.Layout, response7.Layout); // Act - 8 + // Remove new _GlobalImport file + File.Delete(Path.Combine(viewsDirectory, "..", "_GlobalImport.cshtml")); + responseContent = await client.GetStringAsync("http://localhost/Home/Index"); + + // Assert - 8 + // Everything should've recompiled. + var response8 = new ParsedResponse(responseContent); + Assert.NotEqual(response6.ViewStart, response7.ViewStart); + Assert.NotEqual(response6.Index, response7.Index); + Assert.NotEqual(response6.Layout, response7.Layout); + + // Act - 9 // Refetch and verify we get cached types responseContent = await client.GetStringAsync("http://localhost/Home/Index"); - // Assert - 7 - var response8 = new ParsedResponse(responseContent); - Assert.Equal(response7.ViewStart, response8.ViewStart); - Assert.Equal(response7.Index, response8.Index); - Assert.Equal(response7.Layout, response8.Layout); + // Assert - 9 + var response9 = new ParsedResponse(responseContent); + Assert.Equal(response8.ViewStart, response9.ViewStart); + Assert.Equal(response8.Index, response9.Index); + Assert.Equal(response8.Layout, response9.Layout); } finally { - File.WriteAllText(Path.Combine(viewsDirectory, "Layout.cshtml"), layoutContent); - File.WriteAllText(Path.Combine(viewsDirectory, "Index.cshtml"), indexContent); - File.WriteAllText(Path.Combine(viewsDirectory, "_ViewStart.cshtml"), viewstartContent); + File.WriteAllText(Path.Combine(viewsDirectory, "Layout.cshtml"), layoutContent.TrimEnd(' ')); + File.WriteAllText(Path.Combine(viewsDirectory, "Index.cshtml"), indexContent.TrimEnd(' ')); + File.WriteAllText(Path.Combine(viewsDirectory, "_ViewStart.cshtml"), viewstartContent.TrimEnd(' ')); + File.WriteAllText(Path.Combine(viewsDirectory, "_GlobalImport.cshtml"), globalContent.TrimEnd(' ')); } } @@ -168,7 +179,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests } [Fact] - public async Task DeletingPrecompiledViewStart_PriorToFirstRequestToAView_CausesViewToBeRecompiled() + public async Task DeletingPrecompiledGlobalFile_PriorToFirstRequestToAView_CausesViewToBeRecompiled() { // Arrange var expected = typeof(Startup).GetTypeInfo().Assembly.GetName().ToString(); @@ -177,9 +188,11 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests var applicationEnvironment = _services.GetRequiredService(); - var viewsDirectory = Path.Combine(applicationEnvironment.ApplicationBasePath, "Views", "ViewStartDelete"); - var viewStartPath = Path.Combine(viewsDirectory, "_ViewStart.cshtml"); - var viewStartContent = File.ReadAllText(viewStartPath); + var viewsDirectory = Path.Combine(applicationEnvironment.ApplicationBasePath, + "Views", + "GlobalImportDelete"); + var globalPath = Path.Combine(viewsDirectory, "_GlobalImport.cshtml"); + var globalContent = File.ReadAllText(globalPath); // Act - 1 // Query the Test view so we know the compiler cache gets populated. @@ -191,14 +204,15 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests try { // Act - 2 - var response2 = await client.GetStringAsync("http://localhost/Home/ViewStartDeletedPriorToFirstRequest"); + File.Delete(globalPath); + var response2 = await client.GetStringAsync("http://localhost/Home/GlobalDeletedPriorToFirstRequest"); // Assert - 2 Assert.NotEqual(expected, response2.Trim()); } finally { - File.WriteAllText(viewStartPath, viewStartContent); + File.WriteAllText(globalPath, globalContent); } } diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs index 568aea5d07..3fe7614b12 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs @@ -286,11 +286,8 @@ View With Layout Assert.Equal(expected, body.Trim()); } - // Inheritance of chunks in _ViewStart is affected by paths being app-relative and not absolute. - // This change ensures that _ViewStart files correctly inherits directives from parent _ViewStarts - // which guarantees that paths flow correctly through MvcRazorHost. [Fact] - public async Task ViewStartsCanUseDirectivesInjectedFromParentViewStarts() + public async Task ViewStartsCanUseDirectivesInjectedFromParentGlobals() { // Arrange var expected = @@ -298,7 +295,7 @@ View With Layout Hello Controller-Person"; var server = TestServer.Create(_provider, _app); var client = server.CreateClient(); - var target = "http://localhost/NestedViewStarts/NestedViewStartUsingParentDirectives"; + var target = "http://localhost/NestedGlobalImports"; // Act var body = await client.GetStringAsync(target); diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs index 04202fe34c..a89ec40d3d 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs @@ -9,14 +9,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives public class ChunkInheritanceUtilityTest { [Fact] - public void GetInheritedChunks_ReadsChunksFromViewStartsInPath() + public void GetInheritedChunks_ReadsChunksFromGlobalFilesInPath() { // Arrange var fileProvider = new TestFileProvider(); - fileProvider.AddFile(@"Views\accounts\_ViewStart.cshtml", "@using AccountModels"); - fileProvider.AddFile(@"Views\Shared\_ViewStart.cshtml", "@inject SharedHelper Shared"); - fileProvider.AddFile(@"Views\home\_ViewStart.cshtml", "@using MyNamespace"); - fileProvider.AddFile(@"Views\_ViewStart.cshtml", + fileProvider.AddFile(@"Views\accounts\_GlobalImport.cshtml", "@using AccountModels"); + fileProvider.AddFile(@"Views\Shared\_GlobalImport.cshtml", "@inject SharedHelper Shared"); + fileProvider.AddFile(@"Views\home\_GlobalImport.cshtml", "@using MyNamespace"); + fileProvider.AddFile(@"Views\_GlobalImport.cshtml", @"@inject MyHelper Helper @inherits MyBaseType @@ -64,13 +64,13 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives } [Fact] - public void GetInheritedChunks_ReturnsEmptySequenceIfNoViewStartsArePresent() + public void GetInheritedChunks_ReturnsEmptySequenceIfNoGlobalsArePresent() { // Arrange var fileProvider = new TestFileProvider(); - fileProvider.AddFile(@"_ViewStart.cs", string.Empty); + fileProvider.AddFile(@"_GlobalImport.cs", string.Empty); fileProvider.AddFile(@"Views\_Layout.cshtml", string.Empty); - fileProvider.AddFile(@"Views\home\_not-viewstart.cshtml", string.Empty); + fileProvider.AddFile(@"Views\home\_not-globalimport.cshtml", string.Empty); var cache = new DefaultCodeTreeCache(fileProvider); var host = new MvcRazorHost(cache); var defaultChunks = new Chunk[] @@ -92,7 +92,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives { // Arrange var fileProvider = new TestFileProvider(); - fileProvider.AddFile(@"Views\_ViewStart.cshtml", + fileProvider.AddFile(@"Views\_GlobalImport.cshtml", "@inject DifferentHelper Html"); var cache = new DefaultCodeTreeCache(fileProvider); var host = new MvcRazorHost(cache); diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ViewHierarchyUtilityTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ViewHierarchyUtilityTest.cs new file mode 100644 index 0000000000..f10b6eafe0 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ViewHierarchyUtilityTest.cs @@ -0,0 +1,249 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Razor +{ + public class ViewHierarchyUtilityTest + { + [Theory] + [InlineData(null)] + [InlineData("")] + public void GetViewStartLocations_ReturnsEmptySequenceIfViewPathIsEmpty(string viewPath) + { + // Act + var result = ViewHierarchyUtility.GetViewStartLocations(viewPath); + + // Assert + Assert.Empty(result); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void GetGlobalImportLocations_ReturnsEmptySequenceIfViewPathIsEmpty(string viewPath) + { + // Act + var result = ViewHierarchyUtility.GetGlobalImportLocations(viewPath); + + // Assert + Assert.Empty(result); + } + + [Theory] + [InlineData("/Views/Home/MyView.cshtml")] + [InlineData("~/Views/Home/MyView.cshtml")] + [InlineData("Views/Home/MyView.cshtml")] + public void GetViewStartLocations_ReturnsPotentialViewStartLocations_PathStartswithSlash(string inputPath) + { + // Arrange + var expected = new[] + { + @"Views\Home\_ViewStart.cshtml", + @"Views\_ViewStart.cshtml", + @"_ViewStart.cshtml" + }; + + // Act + var result = ViewHierarchyUtility.GetViewStartLocations(inputPath); + + // Assert + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("/Views/Home/MyView.cshtml")] + [InlineData("~/Views/Home/MyView.cshtml")] + [InlineData("Views/Home/MyView.cshtml")] + public void GetGlobalImportLocations_ReturnsPotentialViewStartLocations_PathStartswithSlash(string inputPath) + { + // Arrange + var expected = new[] + { + @"Views\Home\_GlobalImport.cshtml", + @"Views\_GlobalImport.cshtml", + @"_GlobalImport.cshtml" + }; + + // Act + var result = ViewHierarchyUtility.GetGlobalImportLocations(inputPath); + + // Assert + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("/Views/Home/_ViewStart.cshtml")] + [InlineData("~/Views/Home/_ViewStart.cshtml")] + [InlineData("Views/Home/_ViewStart.cshtml")] + public void GetViewStartLocations_SkipsCurrentPath_IfCurrentIsViewStart(string inputPath) + { + // Arrange + var expected = new[] + { + @"Views\_ViewStart.cshtml", + @"_ViewStart.cshtml" + }; + + // Act + var result = ViewHierarchyUtility.GetViewStartLocations(inputPath); + + // Assert + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("/Views/Home/_ViewStart.cshtml")] + [InlineData("~/Views/Home/_ViewStart.cshtml")] + [InlineData("Views/Home/_ViewStart.cshtml")] + public void GetGlobalImportLocations_WhenCurrentIsViewStart(string inputPath) + { + // Arrange + var expected = new[] + { + @"Views\Home\_GlobalImport.cshtml", + @"Views\_GlobalImport.cshtml", + @"_GlobalImport.cshtml" + }; + + // Act + var result = ViewHierarchyUtility.GetGlobalImportLocations(inputPath); + + // Assert + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("/Views/Home/_GlobalImport.cshtml")] + [InlineData("~/Views/Home/_GlobalImport.cshtml")] + [InlineData("Views/Home/_GlobalImport.cshtml")] + public void GetGlobalImportLocations_SkipsCurrentPath_IfCurrentIsGlobalImport(string inputPath) + { + // Arrange + var expected = new[] + { + @"Views\_GlobalImport.cshtml", + @"_GlobalImport.cshtml" + }; + + // Act + var result = ViewHierarchyUtility.GetGlobalImportLocations(inputPath); + + // Assert + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("Test.cshtml")] + [InlineData("ViewStart.cshtml")] + public void GetViewStartLocations_ReturnsPotentialViewStartLocations(string fileName) + { + // Arrange + var expected = new[] + { + @"Areas\MyArea\Sub\Views\Admin\_ViewStart.cshtml", + @"Areas\MyArea\Sub\Views\_ViewStart.cshtml", + @"Areas\MyArea\Sub\_ViewStart.cshtml", + @"Areas\MyArea\_ViewStart.cshtml", + @"Areas\_ViewStart.cshtml", + @"_ViewStart.cshtml", + }; + var viewPath = Path.Combine("Areas", "MyArea", "Sub", "Views", "Admin", fileName); + + // Act + var result = ViewHierarchyUtility.GetViewStartLocations(viewPath); + + // Assert + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("Test.cshtml")] + [InlineData("Global.cshtml")] + [InlineData("_ViewStart.cshtml")] + public void GetGlobalImportLocations_ReturnsPotentialGlobalLocations(string fileName) + { + // Arrange + var expected = new[] + { + @"Areas\MyArea\Sub\Views\Admin\_GlobalImport.cshtml", + @"Areas\MyArea\Sub\Views\_GlobalImport.cshtml", + @"Areas\MyArea\Sub\_GlobalImport.cshtml", + @"Areas\MyArea\_GlobalImport.cshtml", + @"Areas\_GlobalImport.cshtml", + @"_GlobalImport.cshtml", + }; + var viewPath = Path.Combine("Areas", "MyArea", "Sub", "Views", "Admin", fileName); + + // Act + var result = ViewHierarchyUtility.GetGlobalImportLocations(viewPath); + + // Assert + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("_ViewStart.cshtml")] + [InlineData("_viewstart.cshtml")] + public void GetViewStartLocations_SkipsCurrentPath_IfPathIsAViewStartFile(string fileName) + { + // Arrange + var expected = new[] + { + @"Areas\MyArea\Sub\Views\_ViewStart.cshtml", + @"Areas\MyArea\Sub\_ViewStart.cshtml", + @"Areas\MyArea\_ViewStart.cshtml", + @"Areas\_ViewStart.cshtml", + @"_ViewStart.cshtml", + }; + var viewPath = Path.Combine("Areas", "MyArea", "Sub", "Views", "Admin", fileName); + + // Act + var result = ViewHierarchyUtility.GetViewStartLocations(viewPath); + + // Assert + Assert.Equal(expected, result); + } + + [Fact] + public void GetViewStartLocations_ReturnsEmptySequence_IfViewStartIsAtRoot() + { + // Arrange + var viewPath = "_ViewStart.cshtml"; + + // Act + var result = ViewHierarchyUtility.GetViewStartLocations(viewPath); + + // Assert + Assert.Empty(result); + } + + [Fact] + public void GetViewStartLocations_ReturnsEmptySequence_IfPathIsRooted() + { + // Arrange + var absolutePath = Path.Combine(Directory.GetCurrentDirectory(), "Index.cshtml"); + + // Act + var result = ViewHierarchyUtility.GetViewStartLocations(absolutePath); + + // Assert + Assert.Empty(result); + } + + [Fact] + public void GetGlobalImportLocations_ReturnsEmptySequence_IfPathIsRooted() + { + // Arrange + var absolutePath = Path.Combine(Directory.GetCurrentDirectory(), "Index.cshtml"); + + // Act + var result = ViewHierarchyUtility.GetGlobalImportLocations(absolutePath); + + // Assert + Assert.Empty(result); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ViewStartUtilityTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ViewStartUtilityTest.cs deleted file mode 100644 index ed589da934..0000000000 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ViewStartUtilityTest.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; -using Microsoft.AspNet.FileProviders; -using Microsoft.Framework.Runtime; -using Microsoft.Framework.Runtime.Infrastructure; -using Xunit; - -namespace Microsoft.AspNet.Mvc.Razor -{ - public class ViewStartUtilityTest - { - [Theory] - [InlineData(null)] - [InlineData("")] - public void GetViewStartLocations_ReturnsEmptySequenceIfViewPathIsEmpty(string viewPath) - { - // Act - var result = ViewStartUtility.GetViewStartLocations(viewPath); - - // Assert - Assert.Empty(result); - } - - [Theory] - [InlineData("/Views/Home/MyView.cshtml")] - [InlineData("~/Views/Home/MyView.cshtml")] - [InlineData("Views/Home/MyView.cshtml")] - public void GetViewStartLocations_ReturnsPotentialViewStartLocations_PathStartswithSlash(string inputPath) - { - // Arrange - var expected = new[] - { - @"Views\Home\_ViewStart.cshtml", - @"Views\_ViewStart.cshtml", - @"_ViewStart.cshtml" - }; - - // Act - var result = ViewStartUtility.GetViewStartLocations(inputPath); - - // Assert - Assert.Equal(expected, result); - } - - [Theory] - [InlineData("/Views/Home/_ViewStart.cshtml")] - [InlineData("~/Views/Home/_Viewstart.cshtml")] - [InlineData("Views/Home/_Viewstart.cshtml")] - public void GetViewStartLocations_SkipsCurrentPath_IfCurrentIsViewStart(string inputPath) - { - // Arrange - var expected = new[] - { - @"Views\_ViewStart.cshtml", - @"_ViewStart.cshtml" - }; - var fileProvider = new PhysicalFileProvider(GetTestFileProviderBase()); - - // Act - var result = ViewStartUtility.GetViewStartLocations(inputPath); - - // Assert - Assert.Equal(expected, result); - } - - [Theory] - [InlineData("Test.cshtml")] - [InlineData("ViewStart.cshtml")] - public void GetViewStartLocations_ReturnsPotentialViewStartLocations(string fileName) - { - // Arrange - var expected = new[] - { - @"Areas\MyArea\Sub\Views\Admin\_ViewStart.cshtml", - @"Areas\MyArea\Sub\Views\_ViewStart.cshtml", - @"Areas\MyArea\Sub\_ViewStart.cshtml", - @"Areas\MyArea\_ViewStart.cshtml", - @"Areas\_ViewStart.cshtml", - @"_ViewStart.cshtml", - }; - var viewPath = Path.Combine("Areas", "MyArea", "Sub", "Views", "Admin", fileName); - - // Act - var result = ViewStartUtility.GetViewStartLocations(viewPath); - - // Assert - Assert.Equal(expected, result); - } - - [Theory] - [InlineData("_ViewStart.cshtml")] - [InlineData("_viewstart.cshtml")] - public void GetViewStartLocations_SkipsCurrentPath_IfPathIsAViewStartFile(string fileName) - { - // Arrange - var expected = new[] - { - @"Areas\MyArea\Sub\Views\_ViewStart.cshtml", - @"Areas\MyArea\Sub\_ViewStart.cshtml", - @"Areas\MyArea\_ViewStart.cshtml", - @"Areas\_ViewStart.cshtml", - @"_ViewStart.cshtml", - }; - var viewPath = Path.Combine("Areas", "MyArea", "Sub", "Views", "Admin", fileName); - - // Act - var result = ViewStartUtility.GetViewStartLocations(viewPath); - - // Assert - Assert.Equal(expected, result); - } - - [Fact] - public void GetViewStartLocations_ReturnsEmptySequence_IfViewStartIsAtRoot() - { - // Arrange - var appBase = GetTestFileProviderBase(); - var viewPath = "_ViewStart.cshtml"; - - // Act - var result = ViewStartUtility.GetViewStartLocations(viewPath); - - // Assert - Assert.Empty(result); - } - - [Fact] - public void GetViewStartLocations_ReturnsEmptySequence_IfPathIsRooted() - { - // Arrange - var appBase = GetTestFileProviderBase(); - var absolutePath = Path.Combine(Directory.GetCurrentDirectory(), "Index.cshtml"); - - // Act - var result = ViewStartUtility.GetViewStartLocations(absolutePath); - - // Assert - Assert.Empty(result); - } - - private static string GetTestFileProviderBase() - { - var serviceProvider = CallContextServiceLocator.Locator.ServiceProvider; - var appEnv = (IApplicationEnvironment)serviceProvider.GetService(typeof(IApplicationEnvironment)); - return Path.Combine(appEnv.ApplicationBasePath, "TestFiles", "ViewStartUtilityFiles"); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilerCacheTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilerCacheTest.cs index 95140e525a..b79820e1db 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilerCacheTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilerCacheTest.cs @@ -7,7 +7,6 @@ using System.Globalization; using System.IO; using System.Linq; using System.Text; -using Microsoft.AspNet.FileProviders; using Moq; using Xunit; @@ -271,10 +270,10 @@ namespace Microsoft.AspNet.Mvc.Razor } [Fact] - public void GetOrAdd_UsesValueFromCache_IfViewStartHasNotChanged() + public void GetOrAdd_UsesValueFromCache_IfGlobalHasNotChanged() { // Arrange - var instance = (View)Activator.CreateInstance(typeof(PreCompile)); + var instance = new PreCompile(); var length = Encoding.UTF8.GetByteCount(instance.Content); var fileProvider = new TestFileProvider(); @@ -288,25 +287,25 @@ namespace Microsoft.AspNet.Mvc.Razor }; fileProvider.AddFile(ViewPath, fileInfo); - var viewStartContent = "viewstart-content"; - var viewStartFileInfo = new TestFileInfo + var globalContent = "global-content"; + var globalFileInfo = new TestFileInfo { - Content = viewStartContent, + Content = globalContent, LastModified = DateTime.UtcNow }; - fileProvider.AddFile("_ViewStart.cshtml", viewStartFileInfo); - var viewStartRazorFileInfo = new RazorFileInfo + fileProvider.AddFile("_GlobalImport.cshtml", globalFileInfo); + var globalRazorFileInfo = new RazorFileInfo { - Hash = Crc32.Calculate(GetMemoryStream(viewStartContent)).ToString(CultureInfo.InvariantCulture), + Hash = Crc32.Calculate(GetMemoryStream(globalContent)).ToString(CultureInfo.InvariantCulture), HashAlgorithmVersion = 1, - LastModified = viewStartFileInfo.LastModified, - Length = viewStartFileInfo.Length, - RelativePath = "_ViewStart.cshtml", + LastModified = globalFileInfo.LastModified, + Length = globalFileInfo.Length, + RelativePath = "_GlobalImport.cshtml", FullTypeName = typeof(RuntimeCompileIdentical).FullName }; var precompiledViews = new ViewCollection(); - precompiledViews.Add(viewStartRazorFileInfo); + precompiledViews.Add(globalRazorFileInfo); var cache = new CompilerCache(new[] { precompiledViews }, fileProvider); // Act @@ -378,7 +377,7 @@ namespace Microsoft.AspNet.Mvc.Razor } [Fact] - public void GetOrAdd_IgnoresCachedValueIfFileIsIdentical_ButViewStartWasAdedSinceTheCacheWasCreated() + public void GetOrAdd_IgnoresCachedValueIfFileIsIdentical_ButGlobalImportWasAdedSinceTheCacheWasCreated() { // Arrange var expectedType = typeof(RuntimeCompileDifferent); @@ -408,8 +407,8 @@ namespace Microsoft.AspNet.Mvc.Razor Assert.Equal(typeof(PreCompile), actual1.CompiledType); // Act 2 - var viewStartTrigger = fileProvider.GetTrigger("Views\\_ViewStart.cshtml"); - viewStartTrigger.IsExpired = true; + var globalTrigger = fileProvider.GetTrigger("Views\\_GlobalImport.cshtml"); + globalTrigger.IsExpired = true; var result2 = cache.GetOrAdd(testFile.PhysicalPath, compile: _ => CompilationResult.Successful(expectedType)); @@ -421,7 +420,7 @@ namespace Microsoft.AspNet.Mvc.Razor } [Fact] - public void GetOrAdd_IgnoresCachedValueIfFileIsIdentical_ButViewStartWasDeletedSinceCacheWasCreated() + public void GetOrAdd_IgnoresCachedValueIfFileIsIdentical_ButGlobalWasDeletedSinceCacheWasCreated() { // Arrange var expectedType = typeof(RuntimeCompileDifferent); @@ -439,24 +438,24 @@ namespace Microsoft.AspNet.Mvc.Razor }; fileProvider.AddFile(viewFileInfo.PhysicalPath, viewFileInfo); - var viewStartFileInfo = new TestFileInfo + var globalFileInfo = new TestFileInfo { - PhysicalPath = "Views\\_ViewStart.cshtml", + PhysicalPath = "Views\\_GlobalImport.cshtml", Content = "viewstart-content", LastModified = lastModified }; - var viewStart = new RazorFileInfo + var globalFile = new RazorFileInfo { FullTypeName = typeof(RuntimeCompileIdentical).FullName, - RelativePath = viewStartFileInfo.PhysicalPath, - LastModified = viewStartFileInfo.LastModified, - Hash = RazorFileHash.GetHash(viewStartFileInfo, hashAlgorithmVersion: 1), + RelativePath = globalFileInfo.PhysicalPath, + LastModified = globalFileInfo.LastModified, + Hash = RazorFileHash.GetHash(globalFileInfo, hashAlgorithmVersion: 1), HashAlgorithmVersion = 1, - Length = viewStartFileInfo.Length + Length = globalFileInfo.Length }; - fileProvider.AddFile(viewStartFileInfo.PhysicalPath, viewStartFileInfo); + fileProvider.AddFile(globalFileInfo.PhysicalPath, globalFileInfo); - viewCollection.Add(viewStart); + viewCollection.Add(globalFile); var cache = new CompilerCache(new[] { viewCollection }, fileProvider); // Act 1 @@ -470,7 +469,7 @@ namespace Microsoft.AspNet.Mvc.Razor Assert.Equal(typeof(PreCompile), actual1.CompiledType); // Act 2 - var trigger = fileProvider.GetTrigger(viewStartFileInfo.PhysicalPath); + var trigger = fileProvider.GetTrigger(globalFileInfo.PhysicalPath); trigger.IsExpired = true; var result2 = cache.GetOrAdd(viewFileInfo.PhysicalPath, compile: _ => CompilationResult.Successful(expectedType)); @@ -482,15 +481,15 @@ namespace Microsoft.AspNet.Mvc.Razor Assert.Equal(expectedType, actual2.CompiledType); } - public static IEnumerable GetOrAdd_IgnoresCachedValue_IfViewStartWasChangedSinceCacheWasCreatedData + public static IEnumerable GetOrAdd_IgnoresCachedValue_IfGlobalWasChangedSinceCacheWasCreatedData { get { - var viewStartContent = "viewstart-content"; - var contentStream = GetMemoryStream(viewStartContent); + var globalContent = "global-content"; + var contentStream = GetMemoryStream(globalContent); var lastModified = DateTime.UtcNow; - int length = Encoding.UTF8.GetByteCount(viewStartContent); - var path = "Views\\_ViewStart.cshtml"; + int length = Encoding.UTF8.GetByteCount(globalContent); + var path = "Views\\_GlobalImport.cshtml"; var razorFileInfo = new RazorFileInfo { @@ -522,9 +521,9 @@ namespace Microsoft.AspNet.Mvc.Razor } [Theory] - [MemberData(nameof(GetOrAdd_IgnoresCachedValue_IfViewStartWasChangedSinceCacheWasCreatedData))] - public void GetOrAdd_IgnoresCachedValue_IfViewStartWasChangedSinceCacheWasCreated( - RazorFileInfo viewStartRazorFileInfo, TestFileInfo viewStartFileInfo) + [MemberData(nameof(GetOrAdd_IgnoresCachedValue_IfGlobalWasChangedSinceCacheWasCreatedData))] + public void GetOrAdd_IgnoresCachedValue_IfGlobalFileWasChangedSinceCacheWasCreated( + RazorFileInfo viewStartRazorFileInfo, TestFileInfo globalFileInfo) { // Arrange var expectedType = typeof(RuntimeCompileDifferent); @@ -541,7 +540,7 @@ namespace Microsoft.AspNet.Mvc.Razor var fileProvider = new TestFileProvider(); fileProvider.AddFile(fileInfo.PhysicalPath, fileInfo); - fileProvider.AddFile(viewStartRazorFileInfo.RelativePath, viewStartFileInfo); + fileProvider.AddFile(viewStartRazorFileInfo.RelativePath, globalFileInfo); var viewCollection = new ViewCollection(); var cache = new CompilerCache(new[] { viewCollection }, fileProvider); diff --git a/test/WebSites/MvcTagHelpersWebSite/Views/Catalog_CacheTagHelper/_GlobalImport.cshtml b/test/WebSites/MvcTagHelpersWebSite/Views/Catalog_CacheTagHelper/_GlobalImport.cshtml new file mode 100644 index 0000000000..cdde6456b8 --- /dev/null +++ b/test/WebSites/MvcTagHelpersWebSite/Views/Catalog_CacheTagHelper/_GlobalImport.cshtml @@ -0,0 +1 @@ +@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" diff --git a/test/WebSites/PrecompilationWebSite/Controllers/HomeController.cs b/test/WebSites/PrecompilationWebSite/Controllers/HomeController.cs index 1dba8f1f28..595b1d8f0b 100644 --- a/test/WebSites/PrecompilationWebSite/Controllers/HomeController.cs +++ b/test/WebSites/PrecompilationWebSite/Controllers/HomeController.cs @@ -17,9 +17,9 @@ namespace PrecompilationWebSite.Controllers return View("~/Views/ViewsConsumingCompilationOptions/Index"); } - public IActionResult ViewStartDeletedPriorToFirstRequest() + public IActionResult GlobalDeletedPriorToFirstRequest() { - return View("~/Views/ViewStartDelete/Index"); + return View("~/Views/GlobalImportDelete/Index"); } [HttpGet("/Test")] diff --git a/test/WebSites/PrecompilationWebSite/Views/ViewStartDelete/Index.cshtml b/test/WebSites/PrecompilationWebSite/Views/GlobalImportDelete/Index.cshtml similarity index 100% rename from test/WebSites/PrecompilationWebSite/Views/ViewStartDelete/Index.cshtml rename to test/WebSites/PrecompilationWebSite/Views/GlobalImportDelete/Index.cshtml diff --git a/test/WebSites/PrecompilationWebSite/Views/ViewStartDelete/_ViewStart.cshtml b/test/WebSites/PrecompilationWebSite/Views/GlobalImportDelete/_GlobalImport.cshtml similarity index 100% rename from test/WebSites/PrecompilationWebSite/Views/ViewStartDelete/_ViewStart.cshtml rename to test/WebSites/PrecompilationWebSite/Views/GlobalImportDelete/_GlobalImport.cshtml diff --git a/test/WebSites/PrecompilationWebSite/Views/Home/Index.cshtml b/test/WebSites/PrecompilationWebSite/Views/Home/Index.cshtml index 37e13cd4cb..77a9e96d2e 100644 --- a/test/WebSites/PrecompilationWebSite/Views/Home/Index.cshtml +++ b/test/WebSites/PrecompilationWebSite/Views/Home/Index.cshtml @@ -1 +1 @@ -index:@GetType().GetTypeInfo().Assembly.GetName() \ No newline at end of file +index:@GetType().GetTypeInfo().Assembly.GetName() \ No newline at end of file diff --git a/test/WebSites/PrecompilationWebSite/Views/Home/Layout.cshtml b/test/WebSites/PrecompilationWebSite/Views/Home/Layout.cshtml index 57c4e0541c..c068404da0 100644 --- a/test/WebSites/PrecompilationWebSite/Views/Home/Layout.cshtml +++ b/test/WebSites/PrecompilationWebSite/Views/Home/Layout.cshtml @@ -1,2 +1,2 @@ Layout:@GetType().GetTypeInfo().Assembly.FullName -@RenderBody() \ No newline at end of file +@RenderBody() \ No newline at end of file diff --git a/test/WebSites/PrecompilationWebSite/Views/Home/_GlobalImport.cshtml b/test/WebSites/PrecompilationWebSite/Views/Home/_GlobalImport.cshtml new file mode 100644 index 0000000000..0397cb780f --- /dev/null +++ b/test/WebSites/PrecompilationWebSite/Views/Home/_GlobalImport.cshtml @@ -0,0 +1 @@ +@using System.Reflection \ No newline at end of file diff --git a/test/WebSites/PrecompilationWebSite/Views/Home/_ViewStart.cshtml b/test/WebSites/PrecompilationWebSite/Views/Home/_ViewStart.cshtml index 80fc904470..56a7573285 100644 --- a/test/WebSites/PrecompilationWebSite/Views/Home/_ViewStart.cshtml +++ b/test/WebSites/PrecompilationWebSite/Views/Home/_ViewStart.cshtml @@ -1,4 +1,2 @@ -@using System.Reflection @{ Layout = "/Views/Home/Layout.cshtml";} _viewstart:@GetType().GetTypeInfo().Assembly.FullName - \ No newline at end of file diff --git a/test/WebSites/RazorWebSite/Controllers/DirectivesController.cs b/test/WebSites/RazorWebSite/Controllers/DirectivesController.cs index 09327b37ad..66c7f980dc 100644 --- a/test/WebSites/RazorWebSite/Controllers/DirectivesController.cs +++ b/test/WebSites/RazorWebSite/Controllers/DirectivesController.cs @@ -7,14 +7,14 @@ namespace RazorWebSite { public class DirectivesController : Controller { - public ViewResult ViewInheritsInjectAndUsingsFromViewStarts() + public ViewResult ViewInheritsInjectAndUsingsFromGlobalImports() { return View(new Person { Name = "Person1" }); } - public ViewResult ViewInheritsBasePageFromViewStarts() + public ViewResult ViewInheritsBasePageFromGlobalImports() { - return View("/views/directives/scoped/ViewInheritsBasePageFromViewStarts.cshtml", + return View("/views/directives/scoped/ViewInheritsBasePageFromGlobalImports.cshtml", new Person { Name = "Person2" }); } } diff --git a/test/WebSites/RazorWebSite/Controllers/NestedGlobalImports.cs b/test/WebSites/RazorWebSite/Controllers/NestedGlobalImports.cs new file mode 100644 index 0000000000..2cecb215a3 --- /dev/null +++ b/test/WebSites/RazorWebSite/Controllers/NestedGlobalImports.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc; + +namespace RazorWebSite.Controllers +{ + public class NestedGlobalImportsController : Controller + { + public ViewResult Index() + { + var model = new Person + { + Name = "Controller-Person" + }; + + return View("~/Views/NestedGlobalImports/Nested/Index.cshtml", model); + } + } +} \ No newline at end of file diff --git a/test/WebSites/RazorWebSite/Controllers/NestedViewStartsController.cs b/test/WebSites/RazorWebSite/Controllers/NestedViewStartsController.cs index d41a02f3ac..3e0ac10a83 100644 --- a/test/WebSites/RazorWebSite/Controllers/NestedViewStartsController.cs +++ b/test/WebSites/RazorWebSite/Controllers/NestedViewStartsController.cs @@ -11,15 +11,5 @@ namespace RazorWebSite.Controllers { return View("NestedViewStarts/Index"); } - - public ViewResult NestedViewStartUsingParentDirectives() - { - var model = new Person - { - Name = "Controller-Person" - }; - - return View("~/Views/NestedViewStartUsingParentDirectives/Nested/Index.cshtml", model); - } } } \ No newline at end of file diff --git a/test/WebSites/RazorWebSite/Views/Directives/Scoped/ViewInheritsBasePageFromViewStarts.cshtml b/test/WebSites/RazorWebSite/Views/Directives/Scoped/ViewInheritsBasePageFromGlobalImports.cshtml similarity index 100% rename from test/WebSites/RazorWebSite/Views/Directives/Scoped/ViewInheritsBasePageFromViewStarts.cshtml rename to test/WebSites/RazorWebSite/Views/Directives/Scoped/ViewInheritsBasePageFromGlobalImports.cshtml diff --git a/test/WebSites/RazorWebSite/Views/Directives/Scoped/_GlobalImport.cshtml b/test/WebSites/RazorWebSite/Views/Directives/Scoped/_GlobalImport.cshtml new file mode 100644 index 0000000000..18a5d81917 --- /dev/null +++ b/test/WebSites/RazorWebSite/Views/Directives/Scoped/_GlobalImport.cshtml @@ -0,0 +1,4 @@ +@inherits MyBasePage +@{ + Layout = "/Views/Directives/Scoped/_Layout.cshtml"; +} \ No newline at end of file diff --git a/test/WebSites/RazorWebSite/Views/Directives/Scoped/_ViewStart.cshtml b/test/WebSites/RazorWebSite/Views/Directives/Scoped/_ViewStart.cshtml index 18a5d81917..6cca527f44 100644 --- a/test/WebSites/RazorWebSite/Views/Directives/Scoped/_ViewStart.cshtml +++ b/test/WebSites/RazorWebSite/Views/Directives/Scoped/_ViewStart.cshtml @@ -1,4 +1,3 @@ -@inherits MyBasePage -@{ +@{ Layout = "/Views/Directives/Scoped/_Layout.cshtml"; } \ No newline at end of file diff --git a/test/WebSites/RazorWebSite/Views/Directives/ViewInheritsInjectAndUsingsFromViewStarts.cshtml b/test/WebSites/RazorWebSite/Views/Directives/ViewInheritsInjectAndUsingsFromGlobalImports.cshtml similarity index 100% rename from test/WebSites/RazorWebSite/Views/Directives/ViewInheritsInjectAndUsingsFromViewStarts.cshtml rename to test/WebSites/RazorWebSite/Views/Directives/ViewInheritsInjectAndUsingsFromGlobalImports.cshtml diff --git a/test/WebSites/RazorWebSite/Views/Directives/_ViewStart.cshtml b/test/WebSites/RazorWebSite/Views/Directives/_GlobalImport.cshtml similarity index 100% rename from test/WebSites/RazorWebSite/Views/Directives/_ViewStart.cshtml rename to test/WebSites/RazorWebSite/Views/Directives/_GlobalImport.cshtml diff --git a/test/WebSites/RazorWebSite/Views/NestedViewStartUsingParentDirectives/Nested/Index.cshtml b/test/WebSites/RazorWebSite/Views/NestedGlobalImports/Nested/Index.cshtml similarity index 100% rename from test/WebSites/RazorWebSite/Views/NestedViewStartUsingParentDirectives/Nested/Index.cshtml rename to test/WebSites/RazorWebSite/Views/NestedGlobalImports/Nested/Index.cshtml diff --git a/test/WebSites/RazorWebSite/Views/NestedGlobalImports/Nested/_GlobalImport.cshtml b/test/WebSites/RazorWebSite/Views/NestedGlobalImports/Nested/_GlobalImport.cshtml new file mode 100644 index 0000000000..f6e5e61d98 --- /dev/null +++ b/test/WebSites/RazorWebSite/Views/NestedGlobalImports/Nested/_GlobalImport.cshtml @@ -0,0 +1 @@ +@model Person diff --git a/test/WebSites/RazorWebSite/Views/NestedGlobalImports/Nested/_ViewStart.cshtml b/test/WebSites/RazorWebSite/Views/NestedGlobalImports/Nested/_ViewStart.cshtml new file mode 100644 index 0000000000..71018f3218 --- /dev/null +++ b/test/WebSites/RazorWebSite/Views/NestedGlobalImports/Nested/_ViewStart.cshtml @@ -0,0 +1 @@ +@MyInjectedHelper.Greet(Model) diff --git a/test/WebSites/RazorWebSite/Views/NestedViewStartUsingParentDirectives/_ViewStart.cshtml b/test/WebSites/RazorWebSite/Views/NestedGlobalImports/_GlobalImport.cshtml similarity index 100% rename from test/WebSites/RazorWebSite/Views/NestedViewStartUsingParentDirectives/_ViewStart.cshtml rename to test/WebSites/RazorWebSite/Views/NestedGlobalImports/_GlobalImport.cshtml diff --git a/test/WebSites/RazorWebSite/Views/NestedViewStartUsingParentDirectives/Nested/_ViewStart.cshtml b/test/WebSites/RazorWebSite/Views/NestedViewStartUsingParentDirectives/Nested/_ViewStart.cshtml deleted file mode 100644 index f5a5ff8954..0000000000 --- a/test/WebSites/RazorWebSite/Views/NestedViewStartUsingParentDirectives/Nested/_ViewStart.cshtml +++ /dev/null @@ -1,2 +0,0 @@ -@model Person -@MyInjectedHelper.Greet(Model) diff --git a/test/WebSites/RazorWebSite/Views/_ViewStart.cshtml b/test/WebSites/RazorWebSite/Views/_GlobalImport.cshtml similarity index 100% rename from test/WebSites/RazorWebSite/Views/_ViewStart.cshtml rename to test/WebSites/RazorWebSite/Views/_GlobalImport.cshtml diff --git a/test/WebSites/TagHelpersWebSite/Controllers/HomeController.cs b/test/WebSites/TagHelpersWebSite/Controllers/HomeController.cs index d0336d685c..d0bf9a61f4 100644 --- a/test/WebSites/TagHelpersWebSite/Controllers/HomeController.cs +++ b/test/WebSites/TagHelpersWebSite/Controllers/HomeController.cs @@ -42,7 +42,7 @@ namespace TagHelpersWebSite.Controllers public ViewResult ViewWithInheritedRemoveTagHelper() { - return View("/Views/RemoveTagHelperViewStart/ViewWithInheritedRemoveTagHelper.cshtml"); + return View("/Views/RemoveInheritedTagHelpers/ViewWithInheritedRemoveTagHelper.cshtml"); } } } \ No newline at end of file diff --git a/test/WebSites/TagHelpersWebSite/Views/Home/_ViewStart.cshtml b/test/WebSites/TagHelpersWebSite/Views/Home/_GlobalImport.cshtml similarity index 100% rename from test/WebSites/TagHelpersWebSite/Views/Home/_ViewStart.cshtml rename to test/WebSites/TagHelpersWebSite/Views/Home/_GlobalImport.cshtml diff --git a/test/WebSites/TagHelpersWebSite/Views/RemoveTagHelperViewStart/ViewWithInheritedRemoveTagHelper.cshtml b/test/WebSites/TagHelpersWebSite/Views/RemoveInheritedTagHelpers/ViewWithInheritedRemoveTagHelper.cshtml similarity index 100% rename from test/WebSites/TagHelpersWebSite/Views/RemoveTagHelperViewStart/ViewWithInheritedRemoveTagHelper.cshtml rename to test/WebSites/TagHelpersWebSite/Views/RemoveInheritedTagHelpers/ViewWithInheritedRemoveTagHelper.cshtml diff --git a/test/WebSites/TagHelpersWebSite/Views/RemoveInheritedTagHelpers/_GlobalImport.cshtml b/test/WebSites/TagHelpersWebSite/Views/RemoveInheritedTagHelpers/_GlobalImport.cshtml new file mode 100644 index 0000000000..015065f2a9 --- /dev/null +++ b/test/WebSites/TagHelpersWebSite/Views/RemoveInheritedTagHelpers/_GlobalImport.cshtml @@ -0,0 +1,2 @@ +@removeTagHelper "TagHelpersWebSite.TagHelpers.RootViewStartTagHelper, TagHelpersWebSite" +@addTagHelper "TagHelpersWebSite.TagHelpers.NestedViewStartTagHelper, TagHelpersWebSite" diff --git a/test/WebSites/TagHelpersWebSite/Views/RemoveInheritedTagHelpers/_ViewStart.cshtml b/test/WebSites/TagHelpersWebSite/Views/RemoveInheritedTagHelpers/_ViewStart.cshtml new file mode 100644 index 0000000000..da1c3b0b5d --- /dev/null +++ b/test/WebSites/TagHelpersWebSite/Views/RemoveInheritedTagHelpers/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "~/Views/Shared/_LayoutWithRootTagHelper.cshtml"; +} diff --git a/test/WebSites/TagHelpersWebSite/Views/RemoveTagHelperViewStart/_ViewStart.cshtml b/test/WebSites/TagHelpersWebSite/Views/RemoveTagHelperViewStart/_ViewStart.cshtml deleted file mode 100644 index 056a617609..0000000000 --- a/test/WebSites/TagHelpersWebSite/Views/RemoveTagHelperViewStart/_ViewStart.cshtml +++ /dev/null @@ -1,5 +0,0 @@ -@{ - Layout = "~/Views/Shared/_LayoutWithRootTagHelper.cshtml"; -} -@removeTagHelper "TagHelpersWebSite.TagHelpers.RootViewStartTagHelper, TagHelpersWebSite" -@addTagHelper "TagHelpersWebSite.TagHelpers.NestedViewStartTagHelper, TagHelpersWebSite" diff --git a/test/WebSites/TagHelpersWebSite/Views/_GlobalImport.cshtml b/test/WebSites/TagHelpersWebSite/Views/_GlobalImport.cshtml new file mode 100644 index 0000000000..145195fc78 --- /dev/null +++ b/test/WebSites/TagHelpersWebSite/Views/_GlobalImport.cshtml @@ -0,0 +1 @@ +@addTagHelper "TagHelpersWebSite.TagHelpers.RootViewStartTagHelper, TagHelpersWebSite" diff --git a/test/WebSites/TagHelpersWebSite/Views/_ViewStart.cshtml b/test/WebSites/TagHelpersWebSite/Views/_ViewStart.cshtml index 9e8805a27f..c4284f6c20 100644 --- a/test/WebSites/TagHelpersWebSite/Views/_ViewStart.cshtml +++ b/test/WebSites/TagHelpersWebSite/Views/_ViewStart.cshtml @@ -1,4 +1,3 @@ @{ Layout = "/Views/Shared/_Layout.cshtml"; } -@addTagHelper "TagHelpersWebSite.TagHelpers.RootViewStartTagHelper, TagHelpersWebSite" diff --git a/test/WebSites/ViewComponentWebSite/Views/_ViewStart.cshtml b/test/WebSites/ViewComponentWebSite/Views/_GlobalImport.cshtml similarity index 100% rename from test/WebSites/ViewComponentWebSite/Views/_ViewStart.cshtml rename to test/WebSites/ViewComponentWebSite/Views/_GlobalImport.cshtml