Dispose FileWatcher, MvcRazorHost, and ChunkTree

This commit is contained in:
Brennan 2015-11-30 15:59:16 -08:00
parent a424e3e278
commit 3062eea7d0
10 changed files with 385 additions and 327 deletions

View File

@ -73,5 +73,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
return chunkTree;
}
public void Dispose()
{
_chunkTreeCache.Dispose();
}
}
}

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
/// <summary>
/// A cache for parsed <see cref="ChunkTree"/>s.
/// </summary>
public interface IChunkTreeCache
public interface IChunkTreeCache : IDisposable
{
/// <summary>
/// Get an existing <see cref="ChunkTree"/>, or create and add a new one if it is

View File

@ -1,6 +1,7 @@
// 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.AspNet.Razor.CodeGenerators;
@ -9,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <summary>
/// Specifies the contracts for a Razor host that parses Razor files and generates C# code.
/// </summary>
public interface IMvcRazorHost
public interface IMvcRazorHost : IDisposable
{
/// <summary>
/// Parses and generates the contents of a Razor file represented by <paramref name="inputStream"/>.

View File

@ -326,6 +326,11 @@ namespace Microsoft.AspNet.Mvc.Razor
});
}
public void Dispose()
{
_chunkTreeCache.Dispose();
}
private IReadOnlyList<ChunkTree> GetInheritedChunkTrees(string sourceFileName)
{
var inheritedChunkTrees = GetInheritedChunkTreeResults(sourceFileName)

View File

@ -35,8 +35,6 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
return;
}
var fileProvider = new PhysicalFileProvider(context.ProjectContext.ProjectDirectory);
MemoryCache memoryCache;
lock (_memoryCacheLookupLock)
{
@ -57,6 +55,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
}
}
using (var fileProvider = new PhysicalFileProvider(context.ProjectContext.ProjectDirectory))
{
var viewCompiler = new RazorPreCompiler(
context,
fileProvider,
@ -67,6 +67,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
viewCompiler.CompileViews();
}
}
/// <inheritdoc />
public void AfterCompile(AfterCompileContext context)

View File

@ -277,7 +277,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
using (var stream = fileInfo.FileInfo.CreateReadStream())
{
var host = GetRazorHost();
using (var host = GetRazorHost())
{
var results = host.GenerateCode(fileInfo.RelativePath, stream);
if (results.Success)
@ -309,6 +310,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
return new PrecompilationCacheEntry(diagnostics);
}
}
}
return null;
}

View File

@ -262,7 +262,8 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
// Point the IFileProvider root to a different subfolder
var fileProvider = new PhysicalFileProvider(Path.GetFullPath("./Properties"));
using (var fileProvider = new PhysicalFileProvider(Path.GetFullPath("./Properties")))
{
var filePathResult = new VirtualFileResult(path, "text/plain")
{
FileProvider = fileProvider,
@ -278,6 +279,7 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(expectedMessage, ex.Message);
Assert.Equal(path, ex.FileName);
}
}
[Theory]
[InlineData("/SubFolder/SubFolderTestFile.txt")]
@ -294,7 +296,8 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
// Point the IFileProvider root to a different subfolder
var fileProvider = new PhysicalFileProvider(Path.GetFullPath("./Properties"));
using (var fileProvider = new PhysicalFileProvider(Path.GetFullPath("./Properties")))
{
var filePathResult = new VirtualFileResult(path, "text/plain")
{
FileProvider = fileProvider,
@ -305,6 +308,7 @@ namespace Microsoft.AspNet.Mvc
// Act & Assert
Assert.ThrowsAsync<DirectoryNotFoundException>(() => filePathResult.ExecuteResultAsync(context));
}
}
private static IServiceCollection CreateServices()
{

View File

@ -31,7 +31,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
new UsingChunk { Namespace = "AppNamespace.Model" },
};
var cache = new DefaultChunkTreeCache(fileProvider);
var host = new MvcRazorHost(cache);
using (var host = new MvcRazorHost(cache))
{
var utility = new ChunkInheritanceUtility(host, cache, defaultChunks);
// Act
@ -107,6 +108,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
Assert.Equal(viewImportsPath, chunkTreeResult.FilePath);
});
}
}
[Fact]
public void GetInheritedChunks_ReturnsEmptySequenceIfNoGlobalsArePresent()
@ -117,7 +119,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
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);
using (var host = new MvcRazorHost(cache))
{
var defaultChunks = new Chunk[]
{
new InjectChunk("MyTestHtmlHelper", "Html"),
@ -131,6 +134,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
// Assert
Assert.Empty(chunkTrees);
}
}
[Fact]
public void MergeInheritedChunks_MergesDefaultInheritedChunks()
@ -140,7 +144,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
fileProvider.AddFile(@"/Views/_ViewImports.cshtml",
"@inject DifferentHelper<TModel> Html");
var cache = new DefaultChunkTreeCache(fileProvider);
var host = new MvcRazorHost(cache);
using (var host = new MvcRazorHost(cache))
{
var defaultChunks = new Chunk[]
{
new InjectChunk("MyTestHtmlHelper", "Html"),
@ -178,4 +183,5 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
chunk => Assert.Same(defaultChunks[0], chunk));
}
}
}
}

View File

@ -21,7 +21,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
var mockFileProvider = new Mock<TestFileProvider> { CallBase = true };
var fileProvider = mockFileProvider.Object;
fileProvider.AddFile(path, "test content");
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider);
using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider))
{
var expected = new ChunkTree();
// Act
@ -33,6 +34,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
Assert.Same(expected, result2);
mockFileProvider.Verify(f => f.GetFileInfo(It.IsAny<string>()), Times.Once());
}
}
[Fact]
public void GetOrAdd_ReturnsNullValues_IfFileDoesNotExistInFileProvider()
@ -41,7 +43,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
var path = @"Views\_ViewStart.cshtml";
var mockFileProvider = new Mock<TestFileProvider> { CallBase = true };
var fileProvider = mockFileProvider.Object;
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider);
using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider))
{
var expected = new ChunkTree();
// Act
@ -53,6 +56,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
Assert.Null(result2);
mockFileProvider.Verify(f => f.GetFileInfo(It.IsAny<string>()), Times.Once());
}
}
[Fact]
public void GetOrAdd_UpdatesCache_IfFileExpirationTriggerExpires()
@ -61,7 +65,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
var path = @"Views\Home\_ViewStart.cshtml";
var fileProvider = new TestFileProvider();
fileProvider.AddFile(path, "test content");
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider);
using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider))
{
var expected1 = new ChunkTree();
var expected2 = new ChunkTree();
@ -78,6 +83,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
// Assert 2
Assert.Same(expected2, result2);
}
}
[Fact]
public void GetOrAdd_UpdatesCacheWithNullValue_IfFileWasDeleted()
@ -86,7 +92,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
var path = @"Views\Home\_ViewStart.cshtml";
var fileProvider = new TestFileProvider();
fileProvider.AddFile(path, "test content");
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider);
using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider))
{
var expected1 = new ChunkTree();
// Act 1
@ -103,6 +110,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
// Assert 2
Assert.Null(result2);
}
}
[Fact]
public void GetOrAdd_UpdatesCacheWithValue_IfFileWasAdded()
@ -110,7 +118,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
// Arrange
var path = @"Views\Home\_ViewStart.cshtml";
var fileProvider = new TestFileProvider();
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider);
using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider))
{
var expected = new ChunkTree();
// Act 1
@ -127,6 +136,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
// Assert 2
Assert.Same(expected, result2);
}
}
[Fact]
public void GetOrAdd_ExpiresEntriesAfterOneMinute()
@ -140,7 +150,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
clock.SetupGet(c => c.UtcNow)
.Returns(() => utcNow);
var options = new MemoryCacheOptions { Clock = clock.Object };
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider, options);
using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider, options))
{
var chunkTree1 = new ChunkTree();
var chunkTree2 = new ChunkTree();
@ -165,4 +176,5 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
Assert.Same(chunkTree2, result3);
}
}
}
}

View File

@ -55,9 +55,10 @@ namespace Microsoft.AspNet.Mvc.Razor
var rootedAppPath = $"{rootPrefix}SomeComputer/Location/Project/";
var rootedFilePath = $"{rootPrefix}SomeComputer/Location/Project/src/file.cshtml";
var chunkTreeCache = new DefaultChunkTreeCache(new TestFileProvider());
var host = new MvcRazorHost(
using (var host = new MvcRazorHost(
chunkTreeCache,
pathNormalizer: new DesignTimeRazorPathNormalizer(rootedAppPath));
pathNormalizer: new DesignTimeRazorPathNormalizer(rootedAppPath)))
{
var parser = new RazorParser(
host.CodeLanguage.CreateCodeParser(),
host.CreateMarkupParser(),
@ -71,6 +72,7 @@ namespace Microsoft.AspNet.Mvc.Razor
// Assert
Assert.Equal("src/file.cshtml", chunkInheritanceUtility.InheritedChunkTreePagePath, StringComparer.Ordinal);
}
}
[Theory]
[MemberData(nameof(NormalizeChunkInheritanceUtilityPaths_Data))]
@ -81,9 +83,10 @@ namespace Microsoft.AspNet.Mvc.Razor
var rootedAppPath = $"{rootPrefix}SomeComputer/Location/Project/";
var rootedFilePath = $"{rootPrefix}SomeComputer/Location/Project/src/file.cshtml";
var chunkTreeCache = new DefaultChunkTreeCache(new TestFileProvider());
var host = new MvcRazorHost(
using (var host = new MvcRazorHost(
chunkTreeCache,
pathNormalizer: new DesignTimeRazorPathNormalizer(rootedAppPath));
pathNormalizer: new DesignTimeRazorPathNormalizer(rootedAppPath)))
{
var chunkInheritanceUtility = new PathValidatingChunkInheritanceUtility(host, chunkTreeCache);
var codeGeneratorContext = new CodeGeneratorContext(
new ChunkGeneratorContext(
@ -102,30 +105,33 @@ namespace Microsoft.AspNet.Mvc.Razor
// Assert
Assert.Equal("src/file.cshtml", chunkInheritanceUtility.InheritedChunkTreePagePath, StringComparer.Ordinal);
}
}
[Fact]
public void MvcRazorHost_EnablesInstrumentationByDefault()
{
// Arrange
var fileProvider = new TestFileProvider();
var host = new MvcRazorHost(new DefaultChunkTreeCache(fileProvider));
using (var host = new MvcRazorHost(new DefaultChunkTreeCache(fileProvider)))
{
// Act
var instrumented = host.EnableInstrumentation;
// Assert
Assert.True(instrumented);
}
}
[Fact]
public void MvcRazorHost_GeneratesTagHelperModelExpressionCode_DesignTime()
{
// Arrange
var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{
DesignTimeMode = true
};
})
{
var expectedLineMappings = new[]
{
BuildLineMapping(
@ -168,6 +174,7 @@ namespace Microsoft.AspNet.Mvc.Razor
testName: "ModelExpressionTagHelper",
expectedLineMappings: expectedLineMappings);
}
}
[Theory]
[InlineData("Basic")]
@ -181,21 +188,23 @@ namespace Microsoft.AspNet.Mvc.Razor
{
// Arrange
var fileProvider = new TestFileProvider();
var host = new TestMvcRazorHost(new DefaultChunkTreeCache(fileProvider));
using (var host = new TestMvcRazorHost(new DefaultChunkTreeCache(fileProvider)))
{
// Act and Assert
RunRuntimeTest(host, scenarioName);
}
}
[Fact]
public void BasicVisitor_GeneratesCorrectLineMappings()
{
// Arrange
var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{
DesignTimeMode = true
};
})
{
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
@ -220,16 +229,18 @@ namespace Microsoft.AspNet.Mvc.Razor
// Act and Assert
RunDesignTimeTest(host, "Basic", expectedLineMappings);
}
}
[Fact]
public void MvcRazorHost_GeneratesCorrectLineMappingsAndUsingStatementsForViewImports()
{
// Arrange
var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{
DesignTimeMode = true
};
})
{
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
@ -246,16 +257,18 @@ namespace Microsoft.AspNet.Mvc.Razor
// Act and Assert
RunDesignTimeTest(host, "_ViewImports", expectedLineMappings);
}
}
[Fact]
public void InjectVisitor_GeneratesCorrectLineMappings()
{
// Arrange
var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{
DesignTimeMode = true
};
})
{
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
@ -280,16 +293,18 @@ namespace Microsoft.AspNet.Mvc.Razor
// Act and Assert
RunDesignTimeTest(host, "Inject", expectedLineMappings);
}
}
[Fact]
public void InjectVisitorWithModel_GeneratesCorrectLineMappings()
{
// Arrange
var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{
DesignTimeMode = true
};
})
{
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
@ -322,16 +337,18 @@ namespace Microsoft.AspNet.Mvc.Razor
// Act and Assert
RunDesignTimeTest(host, "InjectWithModel", expectedLineMappings);
}
}
[Fact]
public void InjectVisitorWithSemicolon_GeneratesCorrectLineMappings()
{
// Arrange
var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{
DesignTimeMode = true
};
})
{
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
@ -380,16 +397,18 @@ namespace Microsoft.AspNet.Mvc.Razor
// Act and Assert
RunDesignTimeTest(host, "InjectWithSemicolon", expectedLineMappings);
}
}
[Fact]
public void ModelVisitor_GeneratesCorrectLineMappings()
{
// Arrange
var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{
DesignTimeMode = true
};
})
{
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
@ -406,16 +425,18 @@ namespace Microsoft.AspNet.Mvc.Razor
// Act and Assert
RunDesignTimeTest(host, "Model", expectedLineMappings);
}
}
[Fact]
public void ModelVisitor_GeneratesLineMappingsForLastModel_WhenMultipleModelsArePresent()
{
// Arrange
var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{
DesignTimeMode = true
};
})
{
host.NamespaceImports.Clear();
var inputFile = "TestFiles/Input/MultipleModels.cshtml";
var outputFile = "TestFiles/Output/DesignTime/MultipleModels.cs";
@ -438,6 +459,7 @@ namespace Microsoft.AspNet.Mvc.Razor
Assert.Equal(expectedCode, results.GeneratedCode, ignoreLineEndingDifferences: true);
#endif
}
}
private static void RunRuntimeTest(
MvcRazorHost host,