From f4a6d634b550dcdaf923f36aaf91b19b2b3d47e1 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 12 Oct 2015 11:44:13 -0700 Subject: [PATCH] Modify ViewStartUtility to generate always view paths with forward slashes Fixes #3294 --- .../ViewHierarchyUtility.cs | 24 +++- .../ErrorPageTests.cs | 5 +- .../ViewEngineTests.cs | 2 +- .../ViewEngineController.ViewWithPaths.txt | 2 +- .../Directives/ChunkInheritanceUtilityTest.cs | 20 +-- .../ViewHierarchyUtilityTest.cs | 120 +++++++++++++----- .../Compilation/CompilerCacheTest.cs | 7 +- 7 files changed, 121 insertions(+), 59 deletions(-) diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/ViewHierarchyUtility.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/ViewHierarchyUtility.cs index 676fa65e06..2497f75aca 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/ViewHierarchyUtility.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/ViewHierarchyUtility.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; namespace Microsoft.AspNet.Mvc.Razor { @@ -77,18 +78,31 @@ namespace Microsoft.AspNet.Mvc.Razor // 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)) + var builder = new StringBuilder(relativePath); + builder.Replace('\\', '/'); + + if (builder.Length > 0 && builder[0] != '/') { - relativePath = Path.GetDirectoryName(relativePath); - var path = Path.Combine(relativePath, fileName); - locations.Add(path); + builder.Insert(0, '/'); + } + + var locations = new List(); + for (var index = builder.Length - 1; index >= 0; index--) + { + if (builder[index] == '/') + { + builder.Length = index + 1; + builder.Append(fileName); + + locations.Add(builder.ToString()); + } } return locations; diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ErrorPageTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ErrorPageTests.cs index a14bffb202..8160d5c05e 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ErrorPageTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ErrorPageTests.cs @@ -58,10 +58,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); Assert.Equal(expectedMediaType, response.Content.Headers.ContentType); var content = await response.Content.ReadAsStringAsync(); - Assert.Contains( - PlatformNormalizer.NormalizePath(@"Views\ErrorFromViewImports\_ViewImports.cshtml"), - content); - + Assert.Contains("/Views/ErrorFromViewImports/_ViewImports.cshtml", content); Assert.Contains(expectedMessage, content); } } diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs index 96184eeda7..9b85bf697e 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewEngineTests.cs @@ -400,7 +400,7 @@ Partial that does not specify Layout ResourceFile.UpdateFile(_assembly, outputFile, expectedContent, responseContent); #else Assert.Equal( - PlatformNormalizer.NormalizePath(expectedContent), + expectedContent, responseContent, ignoreLineEndingDifferences: true); #endif diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/ViewEngineController.ViewWithPaths.txt b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/ViewEngineController.ViewWithPaths.txt index 026ea62426..b8a7c4a5fd 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/ViewEngineController.ViewWithPaths.txt +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/ViewEngineController.ViewWithPaths.txt @@ -3,7 +3,7 @@ /Views/ViewWithPaths/Index.cshtml - Views\ViewWithPaths\_ViewStart.cshtml + /Views/ViewWithPaths/_ViewStart.cshtml /Views/ViewWithPaths/Index.cshtml 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 0fec67f0bf..57e0fc7b26 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs @@ -13,10 +13,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives { // Arrange var fileProvider = new TestFileProvider(); - fileProvider.AddFile(PlatformNormalizer.NormalizePath(@"Views\accounts\_ViewImports.cshtml"), "@using AccountModels"); - fileProvider.AddFile(PlatformNormalizer.NormalizePath(@"Views\Shared\_ViewImports.cshtml"), "@inject SharedHelper Shared"); - fileProvider.AddFile(PlatformNormalizer.NormalizePath(@"Views\home\_ViewImports.cshtml"), "@using MyNamespace"); - fileProvider.AddFile(PlatformNormalizer.NormalizePath(@"Views\_ViewImports.cshtml"), + fileProvider.AddFile(@"/Views/accounts/_ViewImports.cshtml", "@using AccountModels"); + fileProvider.AddFile(@"/Views/Shared/_ViewImports.cshtml", "@inject SharedHelper Shared"); + fileProvider.AddFile(@"/Views/home/_ViewImports.cshtml", "@using MyNamespace"); + fileProvider.AddFile(@"/Views/_ViewImports.cshtml", @"@inject MyHelper Helper @inherits MyBaseType @@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives Assert.Collection(chunkTreeResults, chunkTreeResult => { - var viewImportsPath = PlatformNormalizer.NormalizePath(@"Views\_ViewImports.cshtml"); + var viewImportsPath = @"/Views/_ViewImports.cshtml"; Assert.Collection(chunkTreeResult.ChunkTree.Chunks, chunk => { @@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives }, chunkTreeResult => { - var viewImportsPath = PlatformNormalizer.NormalizePath(@"Views\home\_ViewImports.cshtml"); + var viewImportsPath = "/Views/home/_ViewImports.cshtml"; Assert.Collection(chunkTreeResult.ChunkTree.Chunks, chunk => { @@ -113,9 +113,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives { // Arrange var fileProvider = new TestFileProvider(); - fileProvider.AddFile(@"_ViewImports.cs", string.Empty); - fileProvider.AddFile(PlatformNormalizer.NormalizePath(@"Views\_Layout.cshtml"), string.Empty); - fileProvider.AddFile(PlatformNormalizer.NormalizePath(@"Views\home\_not-viewimports.cshtml"), string.Empty); + fileProvider.AddFile(@"/_ViewImports.cs", string.Empty); + fileProvider.AddFile(@"/Views/_Layout.cshtml", string.Empty); + fileProvider.AddFile(@"/Views/home/_not-viewimports.cshtml", string.Empty); var cache = new DefaultChunkTreeCache(fileProvider); var host = new MvcRazorHost(cache); var defaultChunks = new Chunk[] @@ -137,7 +137,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives { // Arrange var fileProvider = new TestFileProvider(); - fileProvider.AddFile(PlatformNormalizer.NormalizePath(@"Views\_ViewImports.cshtml"), + fileProvider.AddFile(@"/Views/_ViewImports.cshtml", "@inject DifferentHelper Html"); var cache = new DefaultChunkTreeCache(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 index 4ade3731ef..702e4f5a37 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ViewHierarchyUtilityTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ViewHierarchyUtilityTest.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; -using Microsoft.AspNet.Testing; using Microsoft.AspNet.Testing.xunit; using Xunit; @@ -43,9 +42,34 @@ namespace Microsoft.AspNet.Mvc.Razor // Arrange var expected = new[] { - PlatformNormalizer.NormalizePath(@"Views\Home\_ViewStart.cshtml"), - PlatformNormalizer.NormalizePath(@"Views\_ViewStart.cshtml"), - @"_ViewStart.cshtml" + "/Views/Home/_ViewStart.cshtml", + "/Views/_ViewStart.cshtml", + "/_ViewStart.cshtml" + }; + + // Act + var result = ViewHierarchyUtility.GetViewStartLocations(inputPath); + + // Assert + Assert.Equal(expected, result); + } + + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux, + SkipReason = "Back slashes only work as path separators on Windows")] + [OSSkipCondition(OperatingSystems.MacOSX, + SkipReason = "Back slashes only work as path separators on Windows")] + [InlineData(@"~/Views\Home\MyView.cshtml")] + [InlineData(@"Views\Home\MyView.cshtml")] + public void GetViewStartLocations_ReturnsPotentialViewStartLocations_PathsContainBackSlash( + string inputPath) + { + // Arrange + var expected = new[] + { + "/Views/Home/_ViewStart.cshtml", + "/Views/_ViewStart.cshtml", + "/_ViewStart.cshtml" }; // Act @@ -64,9 +88,9 @@ namespace Microsoft.AspNet.Mvc.Razor // Arrange var expected = new[] { - PlatformNormalizer.NormalizePath(@"Views\Home\_ViewImports.cshtml"), - PlatformNormalizer.NormalizePath(@"Views\_ViewImports.cshtml"), - @"_ViewImports.cshtml" + "/Views/Home/_ViewImports.cshtml", + "/Views/_ViewImports.cshtml", + "/_ViewImports.cshtml" }; // Act @@ -85,8 +109,8 @@ namespace Microsoft.AspNet.Mvc.Razor // Arrange var expected = new[] { - PlatformNormalizer.NormalizePath(@"Views\_ViewStart.cshtml"), - @"_ViewStart.cshtml" + "/Views/_ViewStart.cshtml", + "/_ViewStart.cshtml" }; // Act @@ -105,9 +129,9 @@ namespace Microsoft.AspNet.Mvc.Razor // Arrange var expected = new[] { - PlatformNormalizer.NormalizePath(@"Views\Home\_ViewImports.cshtml"), - PlatformNormalizer.NormalizePath(@"Views\_ViewImports.cshtml"), - @"_ViewImports.cshtml" + "/Views/Home/_ViewImports.cshtml", + "/Views/_ViewImports.cshtml", + "/_ViewImports.cshtml" }; // Act @@ -126,8 +150,8 @@ namespace Microsoft.AspNet.Mvc.Razor // Arrange var expected = new[] { - PlatformNormalizer.NormalizePath(@"Views\_ViewImports.cshtml"), - @"_ViewImports.cshtml" + "/Views/_ViewImports.cshtml", + "/_ViewImports.cshtml" }; // Act @@ -145,14 +169,42 @@ namespace Microsoft.AspNet.Mvc.Razor // Arrange var expected = new[] { - PlatformNormalizer.NormalizePath(@"Areas\MyArea\Sub\Views\Admin\_ViewStart.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\MyArea\Sub\Views\_ViewStart.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\MyArea\Sub\_ViewStart.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\MyArea\_ViewStart.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\_ViewStart.cshtml"), - @"_ViewStart.cshtml", + "/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); + var viewPath = $"Areas/MyArea/Sub/Views/Admin/{fileName}"; + + // Act + var result = ViewHierarchyUtility.GetViewStartLocations(viewPath); + + // Assert + Assert.Equal(expected, result); + } + + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux, + SkipReason = "Back slashes only work as path separators on Windows")] + [OSSkipCondition(OperatingSystems.MacOSX, + SkipReason = "Back slashes only work as path separators on Windows")] + [InlineData("Test.cshtml")] + [InlineData("ViewStart.cshtml")] + public void GetViewStartLocations_ReturnsPotentialViewStartLocations_ForPathsWithBackSlashes(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 = $"Areas\\MyArea\\Sub\\Views\\Admin/{fileName}"; // Act var result = ViewHierarchyUtility.GetViewStartLocations(viewPath); @@ -170,14 +222,14 @@ namespace Microsoft.AspNet.Mvc.Razor // Arrange var expected = new[] { - PlatformNormalizer.NormalizePath(@"Areas\MyArea\Sub\Views\Admin\_ViewImports.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\MyArea\Sub\Views\_ViewImports.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\MyArea\Sub\_ViewImports.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\MyArea\_ViewImports.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\_ViewImports.cshtml"), - @"_ViewImports.cshtml", + "/Areas/MyArea/Sub/Views/Admin/_ViewImports.cshtml", + "/Areas/MyArea/Sub/Views/_ViewImports.cshtml", + "/Areas/MyArea/Sub/_ViewImports.cshtml", + "/Areas/MyArea/_ViewImports.cshtml", + "/Areas/_ViewImports.cshtml", + "/_ViewImports.cshtml", }; - var viewPath = Path.Combine("Areas", "MyArea", "Sub", "Views", "Admin", fileName); + var viewPath = $"Areas/MyArea/Sub/Views/Admin/{fileName}"; // Act var result = ViewHierarchyUtility.GetViewImportsLocations(viewPath); @@ -194,13 +246,13 @@ namespace Microsoft.AspNet.Mvc.Razor // Arrange var expected = new[] { - PlatformNormalizer.NormalizePath(@"Areas\MyArea\Sub\Views\_ViewStart.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\MyArea\Sub\_ViewStart.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\MyArea\_ViewStart.cshtml"), - PlatformNormalizer.NormalizePath(@"Areas\_ViewStart.cshtml"), - @"_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); + var viewPath = $"Areas/MyArea/Sub/Views/Admin/{fileName}"; // Act var result = ViewHierarchyUtility.GetViewStartLocations(viewPath); diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilerCacheTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilerCacheTest.cs index 9fe3259524..1b11ba0010 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilerCacheTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/CompilerCacheTest.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; using Moq; using Xunit; @@ -21,9 +20,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation public static TheoryData ViewImportsPaths => new TheoryData { - Path.Combine("Views", "Home", "_ViewImports.cshtml"), - Path.Combine("Views", "_ViewImports.cshtml"), - "_ViewImports.cshtml", + "/Views/Home/_ViewImports.cshtml", + "/Views/_ViewImports.cshtml", + "/_ViewImports.cshtml", }; [Fact]