From 7f303f9e0abe333623885a47a31c91c8872fa19d Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 13 Mar 2017 10:14:24 -0700 Subject: [PATCH] Add support for base path in FindHiearchicalItems Required for https://github.com/aspnet/Mvc/issues/5915 --- .../RazorProject.cs | 29 ++++- .../RazorProjectTest.cs | 121 ++++++++++++++++++ 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorProject.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorProject.cs index c93124ba7..536e25209 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/RazorProject.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorProject.cs @@ -39,8 +39,27 @@ public abstract class RazorProject /// e.g. /// /Views/Home/View.cshtml -> [ /Views/Home/FileName.cshtml, /Views/FileName.cshtml, /FileName.cshtml ] /// - public virtual IEnumerable FindHierarchicalItems(string path, string fileName) + public IEnumerable FindHierarchicalItems(string path, string fileName) { + return FindHierarchicalItems(basePath: "/", path: path, fileName: fileName); + } + + /// + /// Gets the sequence of files named that are applicable to the specified path. + /// + /// The base path. + /// The path of a project item. + /// The file name to seek. + /// A sequence of applicable instances. + /// + /// This method returns paths starting from the directory of and + /// traverses to the . + /// e.g. + /// (/Views, /Views/Home/View.cshtml) -> [ /Views/Home/FileName.cshtml, /Views/FileName.cshtml ] + /// + public virtual IEnumerable FindHierarchicalItems(string basePath, string path, string fileName) + { + EnsureValidPath(basePath); EnsureValidPath(path); if (string.IsNullOrEmpty(fileName)) { @@ -53,6 +72,11 @@ public virtual IEnumerable FindHierarchicalItems(string path, yield break; } + if (!path.StartsWith(basePath, StringComparison.OrdinalIgnoreCase)) + { + yield break; + } + StringBuilder builder; var fileNameIndex = path.LastIndexOf('/'); var length = path.Length; @@ -69,8 +93,9 @@ public virtual IEnumerable FindHierarchicalItems(string path, builder = new StringBuilder(path); } + var maxDepth = 255; var index = length; - while (index > 0 && (index = path.LastIndexOf('/', index - 1)) != -1) + while (maxDepth-- > 0 && index > basePath.Length && (index = path.LastIndexOf('/', index - 1)) != -1) { builder.Length = index + 1; builder.Append(fileName); diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorProjectTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorProjectTest.cs index 714af3d0c..6e433de85 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorProjectTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorProjectTest.cs @@ -188,6 +188,127 @@ public void FindHierarchicalItems_IncludesNonExistentFiles() }); } + [Theory] + [InlineData("/Areas")] + [InlineData("/Areas/")] + public void FindHierarchicalItems_WithBasePath(string basePath) + { + // Arrange + var path = "/Areas/MyArea/Views/Home/Test.cshtml"; + var items = new Dictionary + { + { "/Areas/MyArea/File.cshtml", CreateProjectItem("/Areas/MyArea/File.cshtml") }, + { "/File.cshtml", CreateProjectItem("/File.cshtml") }, + }; + var project = new TestRazorProject(items); + + // Act + var result = project.FindHierarchicalItems(basePath, path, "File.cshtml"); + + // Assert + Assert.Collection( + result, + item => + { + Assert.Equal("/Areas/MyArea/Views/Home/File.cshtml", item.Path); + Assert.False(item.Exists); + }, + item => + { + Assert.Equal("/Areas/MyArea/Views/File.cshtml", item.Path); + Assert.False(item.Exists); + }, + item => + { + Assert.Equal("/Areas/MyArea/File.cshtml", item.Path); + Assert.True(item.Exists); + }, + item => + { + Assert.Equal("/Areas/File.cshtml", item.Path); + Assert.False(item.Exists); + }); + } + + [Theory] + [InlineData("/Areas/MyArea/Views")] + [InlineData("/Areas/MyArea/Views/")] + public void FindHierarchicalItems_WithNestedBasePath(string basePath) + { + // Arrange + var path = "/Areas/MyArea/Views/Home/Test.cshtml"; + var items = new Dictionary + { + { "/Areas/MyArea/File.cshtml", CreateProjectItem("/Areas/MyArea/File.cshtml") }, + { "/File.cshtml", CreateProjectItem("/File.cshtml") }, + }; + var project = new TestRazorProject(items); + + // Act + var result = project.FindHierarchicalItems(basePath, path, "File.cshtml"); + + // Assert + Assert.Collection( + result, + item => + { + Assert.Equal("/Areas/MyArea/Views/Home/File.cshtml", item.Path); + Assert.False(item.Exists); + }, + item => + { + Assert.Equal("/Areas/MyArea/Views/File.cshtml", item.Path); + Assert.False(item.Exists); + }); + } + + [Theory] + [InlineData("/Areas/MyArea/Views/Home")] + [InlineData("/Areas/MyArea/Views/Home/")] + public void FindHierarchicalItems_WithFileAtBasePath(string basePath) + { + // Arrange + var path = "/Areas/MyArea/Views/Home/Test.cshtml"; + var items = new Dictionary + { + { "/Areas/MyArea/File.cshtml", CreateProjectItem("/Areas/MyArea/File.cshtml") }, + { "/File.cshtml", CreateProjectItem("/File.cshtml") }, + }; + var project = new TestRazorProject(items); + + // Act + var result = project.FindHierarchicalItems(basePath, path, "File.cshtml"); + + // Assert + Assert.Collection( + result, + item => + { + Assert.Equal("/Areas/MyArea/Views/Home/File.cshtml", item.Path); + Assert.False(item.Exists); + }); + } + + [Fact] + public void FindHierarchicalItems_ReturnsEmptySequenceIfPathIsNotASubPathOfBasePath() + { + // Arrange + var basePath = "/Pages"; + var path = "/Areas/MyArea/Views/Home/Test.cshtml"; + var items = new Dictionary + { + { "/Areas/MyArea/File.cshtml", CreateProjectItem("/Areas/MyArea/File.cshtml") }, + { "/File.cshtml", CreateProjectItem("/File.cshtml") }, + }; + var project = new TestRazorProject(items); + + // Act + var result = project.FindHierarchicalItems(basePath, path, "File.cshtml"); + + // Assert + Assert.Empty(result); + } + private RazorProjectItem CreateProjectItem(string path) { var projectItem = new Mock();