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; return chunkTree;
} }
public void Dispose()
{
_chunkTreeCache.Dispose();
}
} }
} }

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
/// <summary> /// <summary>
/// A cache for parsed <see cref="ChunkTree"/>s. /// A cache for parsed <see cref="ChunkTree"/>s.
/// </summary> /// </summary>
public interface IChunkTreeCache public interface IChunkTreeCache : IDisposable
{ {
/// <summary> /// <summary>
/// Get an existing <see cref="ChunkTree"/>, or create and add a new one if it is /// 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. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO; using System.IO;
using Microsoft.AspNet.Razor.CodeGenerators; using Microsoft.AspNet.Razor.CodeGenerators;
@ -9,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <summary> /// <summary>
/// Specifies the contracts for a Razor host that parses Razor files and generates C# code. /// Specifies the contracts for a Razor host that parses Razor files and generates C# code.
/// </summary> /// </summary>
public interface IMvcRazorHost public interface IMvcRazorHost : IDisposable
{ {
/// <summary> /// <summary>
/// Parses and generates the contents of a Razor file represented by <paramref name="inputStream"/>. /// 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) private IReadOnlyList<ChunkTree> GetInheritedChunkTrees(string sourceFileName)
{ {
var inheritedChunkTrees = GetInheritedChunkTreeResults(sourceFileName) var inheritedChunkTrees = GetInheritedChunkTreeResults(sourceFileName)

View File

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

View File

@ -277,36 +277,38 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
using (var stream = fileInfo.FileInfo.CreateReadStream()) using (var stream = fileInfo.FileInfo.CreateReadStream())
{ {
var host = GetRazorHost(); using (var host = GetRazorHost())
var results = host.GenerateCode(fileInfo.RelativePath, stream);
if (results.Success)
{ {
var syntaxTree = SyntaxTreeGenerator.Generate( var results = host.GenerateCode(fileInfo.RelativePath, stream);
results.GeneratedCode,
fileInfo.FileInfo.PhysicalPath,
CompilationSettings);
var fullTypeName = results.GetMainClassName(host, syntaxTree);
if (fullTypeName != null) if (results.Success)
{ {
var razorFileInfo = new RazorFileInfo var syntaxTree = SyntaxTreeGenerator.Generate(
results.GeneratedCode,
fileInfo.FileInfo.PhysicalPath,
CompilationSettings);
var fullTypeName = results.GetMainClassName(host, syntaxTree);
if (fullTypeName != null)
{ {
RelativePath = fileInfo.RelativePath, var razorFileInfo = new RazorFileInfo
FullTypeName = fullTypeName {
}; RelativePath = fileInfo.RelativePath,
FullTypeName = fullTypeName
};
return new PrecompilationCacheEntry(razorFileInfo, syntaxTree); return new PrecompilationCacheEntry(razorFileInfo, syntaxTree);
}
} }
} else
else {
{ var diagnostics = results
var diagnostics = results .ParserErrors
.ParserErrors .Select(error => error.ToDiagnostics(fileInfo.FileInfo.PhysicalPath))
.Select(error => error.ToDiagnostics(fileInfo.FileInfo.PhysicalPath)) .ToList();
.ToList();
return new PrecompilationCacheEntry(diagnostics); return new PrecompilationCacheEntry(diagnostics);
}
} }
} }

View File

@ -262,21 +262,23 @@ namespace Microsoft.AspNet.Mvc
{ {
// Arrange // Arrange
// Point the IFileProvider root to a different subfolder // 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, var filePathResult = new VirtualFileResult(path, "text/plain")
}; {
FileProvider = fileProvider,
};
var expectedMessage = "Could not find file: " + path; var expectedMessage = "Could not find file: " + path;
var context = new ActionContext(GetHttpContext(), new RouteData(), new ActionDescriptor()); var context = new ActionContext(GetHttpContext(), new RouteData(), new ActionDescriptor());
// Act // Act
var ex = await Assert.ThrowsAsync<FileNotFoundException>(() => filePathResult.ExecuteResultAsync(context)); var ex = await Assert.ThrowsAsync<FileNotFoundException>(() => filePathResult.ExecuteResultAsync(context));
// Assert // Assert
Assert.Equal(expectedMessage, ex.Message); Assert.Equal(expectedMessage, ex.Message);
Assert.Equal(path, ex.FileName); Assert.Equal(path, ex.FileName);
}
} }
[Theory] [Theory]
@ -294,16 +296,18 @@ namespace Microsoft.AspNet.Mvc
{ {
// Arrange // Arrange
// Point the IFileProvider root to a different subfolder // 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, var filePathResult = new VirtualFileResult(path, "text/plain")
}; {
FileProvider = fileProvider,
};
var context = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); var context = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
// Act & Assert // Act & Assert
Assert.ThrowsAsync<DirectoryNotFoundException>(() => filePathResult.ExecuteResultAsync(context)); Assert.ThrowsAsync<DirectoryNotFoundException>(() => filePathResult.ExecuteResultAsync(context));
}
} }
private static IServiceCollection CreateServices() private static IServiceCollection CreateServices()

View File

@ -31,81 +31,83 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
new UsingChunk { Namespace = "AppNamespace.Model" }, new UsingChunk { Namespace = "AppNamespace.Model" },
}; };
var cache = new DefaultChunkTreeCache(fileProvider); var cache = new DefaultChunkTreeCache(fileProvider);
var host = new MvcRazorHost(cache); using (var host = new MvcRazorHost(cache))
var utility = new ChunkInheritanceUtility(host, cache, defaultChunks); {
var utility = new ChunkInheritanceUtility(host, cache, defaultChunks);
// Act // Act
var chunkTreeResults = utility.GetInheritedChunkTreeResults( var chunkTreeResults = utility.GetInheritedChunkTreeResults(
PlatformNormalizer.NormalizePath(@"Views\home\Index.cshtml")); PlatformNormalizer.NormalizePath(@"Views\home\Index.cshtml"));
// Assert // Assert
Assert.Collection(chunkTreeResults, Assert.Collection(chunkTreeResults,
chunkTreeResult => chunkTreeResult =>
{ {
var viewImportsPath = @"/Views/_ViewImports.cshtml"; var viewImportsPath = @"/Views/_ViewImports.cshtml";
Assert.Collection(chunkTreeResult.ChunkTree.Children, Assert.Collection(chunkTreeResult.ChunkTree.Children,
chunk => chunk =>
{ {
Assert.IsType<LiteralChunk>(chunk); Assert.IsType<LiteralChunk>(chunk);
Assert.Equal(viewImportsPath, chunk.Start.FilePath); Assert.Equal(viewImportsPath, chunk.Start.FilePath);
}, },
chunk => chunk =>
{ {
var injectChunk = Assert.IsType<InjectChunk>(chunk); var injectChunk = Assert.IsType<InjectChunk>(chunk);
Assert.Equal("MyHelper<TModel>", injectChunk.TypeName); Assert.Equal("MyHelper<TModel>", injectChunk.TypeName);
Assert.Equal("Helper", injectChunk.MemberName); Assert.Equal("Helper", injectChunk.MemberName);
Assert.Equal(viewImportsPath, chunk.Start.FilePath); Assert.Equal(viewImportsPath, chunk.Start.FilePath);
}, },
chunk => chunk =>
{ {
Assert.IsType<LiteralChunk>(chunk); Assert.IsType<LiteralChunk>(chunk);
Assert.Equal(viewImportsPath, chunk.Start.FilePath); Assert.Equal(viewImportsPath, chunk.Start.FilePath);
}, },
chunk => chunk =>
{ {
var setBaseTypeChunk = Assert.IsType<SetBaseTypeChunk>(chunk); var setBaseTypeChunk = Assert.IsType<SetBaseTypeChunk>(chunk);
Assert.Equal("MyBaseType", setBaseTypeChunk.TypeName); Assert.Equal("MyBaseType", setBaseTypeChunk.TypeName);
Assert.Equal(viewImportsPath, chunk.Start.FilePath); Assert.Equal(viewImportsPath, chunk.Start.FilePath);
}, },
chunk => chunk =>
{ {
Assert.IsType<LiteralChunk>(chunk); Assert.IsType<LiteralChunk>(chunk);
Assert.Equal(viewImportsPath, chunk.Start.FilePath); Assert.Equal(viewImportsPath, chunk.Start.FilePath);
}, },
chunk => chunk =>
{ {
Assert.IsType<StatementChunk>(chunk); Assert.IsType<StatementChunk>(chunk);
Assert.Equal(viewImportsPath, chunk.Start.FilePath); Assert.Equal(viewImportsPath, chunk.Start.FilePath);
}, },
chunk => chunk =>
{ {
Assert.IsType<LiteralChunk>(chunk); Assert.IsType<LiteralChunk>(chunk);
Assert.Equal(viewImportsPath, chunk.Start.FilePath); Assert.Equal(viewImportsPath, chunk.Start.FilePath);
}); });
Assert.Equal(viewImportsPath, chunkTreeResult.FilePath); Assert.Equal(viewImportsPath, chunkTreeResult.FilePath);
}, },
chunkTreeResult => chunkTreeResult =>
{ {
var viewImportsPath = "/Views/home/_ViewImports.cshtml"; var viewImportsPath = "/Views/home/_ViewImports.cshtml";
Assert.Collection(chunkTreeResult.ChunkTree.Children, Assert.Collection(chunkTreeResult.ChunkTree.Children,
chunk => chunk =>
{ {
Assert.IsType<LiteralChunk>(chunk); Assert.IsType<LiteralChunk>(chunk);
Assert.Equal(viewImportsPath, chunk.Start.FilePath); Assert.Equal(viewImportsPath, chunk.Start.FilePath);
}, },
chunk => chunk =>
{ {
var usingChunk = Assert.IsType<UsingChunk>(chunk); var usingChunk = Assert.IsType<UsingChunk>(chunk);
Assert.Equal("MyNamespace", usingChunk.Namespace); Assert.Equal("MyNamespace", usingChunk.Namespace);
Assert.Equal(viewImportsPath, chunk.Start.FilePath); Assert.Equal(viewImportsPath, chunk.Start.FilePath);
}, },
chunk => chunk =>
{ {
Assert.IsType<LiteralChunk>(chunk); Assert.IsType<LiteralChunk>(chunk);
Assert.Equal(viewImportsPath, chunk.Start.FilePath); Assert.Equal(viewImportsPath, chunk.Start.FilePath);
}); });
Assert.Equal(viewImportsPath, chunkTreeResult.FilePath); Assert.Equal(viewImportsPath, chunkTreeResult.FilePath);
}); });
}
} }
[Fact] [Fact]
@ -117,19 +119,21 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
fileProvider.AddFile(@"/Views/_Layout.cshtml", string.Empty); fileProvider.AddFile(@"/Views/_Layout.cshtml", string.Empty);
fileProvider.AddFile(@"/Views/home/_not-viewimports.cshtml", string.Empty); fileProvider.AddFile(@"/Views/home/_not-viewimports.cshtml", string.Empty);
var cache = new DefaultChunkTreeCache(fileProvider); var cache = new DefaultChunkTreeCache(fileProvider);
var host = new MvcRazorHost(cache); using (var host = new MvcRazorHost(cache))
var defaultChunks = new Chunk[]
{ {
var defaultChunks = new Chunk[]
{
new InjectChunk("MyTestHtmlHelper", "Html"), new InjectChunk("MyTestHtmlHelper", "Html"),
new UsingChunk { Namespace = "AppNamespace.Model" }, new UsingChunk { Namespace = "AppNamespace.Model" },
}; };
var utility = new ChunkInheritanceUtility(host, cache, defaultChunks); var utility = new ChunkInheritanceUtility(host, cache, defaultChunks);
// Act // Act
var chunkTrees = utility.GetInheritedChunkTreeResults(PlatformNormalizer.NormalizePath(@"Views\home\Index.cshtml")); var chunkTrees = utility.GetInheritedChunkTreeResults(PlatformNormalizer.NormalizePath(@"Views\home\Index.cshtml"));
// Assert // Assert
Assert.Empty(chunkTrees); Assert.Empty(chunkTrees);
}
} }
[Fact] [Fact]
@ -140,14 +144,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
fileProvider.AddFile(@"/Views/_ViewImports.cshtml", fileProvider.AddFile(@"/Views/_ViewImports.cshtml",
"@inject DifferentHelper<TModel> Html"); "@inject DifferentHelper<TModel> Html");
var cache = new DefaultChunkTreeCache(fileProvider); var cache = new DefaultChunkTreeCache(fileProvider);
var host = new MvcRazorHost(cache); using (var host = new MvcRazorHost(cache))
var defaultChunks = new Chunk[]
{ {
var defaultChunks = new Chunk[]
{
new InjectChunk("MyTestHtmlHelper", "Html"), new InjectChunk("MyTestHtmlHelper", "Html"),
new UsingChunk { Namespace = "AppNamespace.Model" }, new UsingChunk { Namespace = "AppNamespace.Model" },
}; };
var inheritedChunkTrees = new ChunkTree[] var inheritedChunkTrees = new ChunkTree[]
{ {
new ChunkTree new ChunkTree
{ {
Children = new Chunk[] Children = new Chunk[]
@ -163,19 +168,20 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
new UsingChunk { Namespace = "AppNamespace.Model" }, new UsingChunk { Namespace = "AppNamespace.Model" },
} }
} }
}; };
var utility = new ChunkInheritanceUtility(host, cache, defaultChunks); var utility = new ChunkInheritanceUtility(host, cache, defaultChunks);
var chunkTree = new ChunkTree(); var chunkTree = new ChunkTree();
// Act // Act
utility.MergeInheritedChunkTrees(chunkTree, inheritedChunkTrees, "dynamic"); utility.MergeInheritedChunkTrees(chunkTree, inheritedChunkTrees, "dynamic");
// Assert // Assert
Assert.Collection(chunkTree.Children, Assert.Collection(chunkTree.Children,
chunk => Assert.Same(defaultChunks[1], chunk), chunk => Assert.Same(defaultChunks[1], chunk),
chunk => Assert.Same(inheritedChunkTrees[0].Children[0], chunk), chunk => Assert.Same(inheritedChunkTrees[0].Children[0], chunk),
chunk => Assert.Same(defaultChunks[0], chunk)); chunk => Assert.Same(defaultChunks[0], chunk));
}
} }
} }
} }

View File

@ -21,17 +21,19 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
var mockFileProvider = new Mock<TestFileProvider> { CallBase = true }; var mockFileProvider = new Mock<TestFileProvider> { CallBase = true };
var fileProvider = mockFileProvider.Object; var fileProvider = mockFileProvider.Object;
fileProvider.AddFile(path, "test content"); fileProvider.AddFile(path, "test content");
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider); using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider))
var expected = new ChunkTree(); {
var expected = new ChunkTree();
// Act // Act
var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected); var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected);
var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); });
// Assert // Assert
Assert.Same(expected, result1); Assert.Same(expected, result1);
Assert.Same(expected, result2); Assert.Same(expected, result2);
mockFileProvider.Verify(f => f.GetFileInfo(It.IsAny<string>()), Times.Once()); mockFileProvider.Verify(f => f.GetFileInfo(It.IsAny<string>()), Times.Once());
}
} }
[Fact] [Fact]
@ -41,17 +43,19 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
var path = @"Views\_ViewStart.cshtml"; var path = @"Views\_ViewStart.cshtml";
var mockFileProvider = new Mock<TestFileProvider> { CallBase = true }; var mockFileProvider = new Mock<TestFileProvider> { CallBase = true };
var fileProvider = mockFileProvider.Object; var fileProvider = mockFileProvider.Object;
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider); using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider))
var expected = new ChunkTree(); {
var expected = new ChunkTree();
// Act // Act
var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected); var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected);
var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); });
// Assert // Assert
Assert.Null(result1); Assert.Null(result1);
Assert.Null(result2); Assert.Null(result2);
mockFileProvider.Verify(f => f.GetFileInfo(It.IsAny<string>()), Times.Once()); mockFileProvider.Verify(f => f.GetFileInfo(It.IsAny<string>()), Times.Once());
}
} }
[Fact] [Fact]
@ -61,22 +65,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
var path = @"Views\Home\_ViewStart.cshtml"; var path = @"Views\Home\_ViewStart.cshtml";
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
fileProvider.AddFile(path, "test content"); fileProvider.AddFile(path, "test content");
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider); using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider))
var expected1 = new ChunkTree(); {
var expected2 = new ChunkTree(); var expected1 = new ChunkTree();
var expected2 = new ChunkTree();
// Act 1 // Act 1
var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected1); var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected1);
// Assert 1 // Assert 1
Assert.Same(expected1, result1); Assert.Same(expected1, result1);
// Act 2 // Act 2
fileProvider.GetChangeToken(path).HasChanged = true; fileProvider.GetChangeToken(path).HasChanged = true;
var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => expected2); var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => expected2);
// Assert 2 // Assert 2
Assert.Same(expected2, result2); Assert.Same(expected2, result2);
}
} }
[Fact] [Fact]
@ -86,22 +92,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
var path = @"Views\Home\_ViewStart.cshtml"; var path = @"Views\Home\_ViewStart.cshtml";
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
fileProvider.AddFile(path, "test content"); fileProvider.AddFile(path, "test content");
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider); using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider))
var expected1 = new ChunkTree(); {
var expected1 = new ChunkTree();
// Act 1 // Act 1
var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected1); var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected1);
// Assert 1 // Assert 1
Assert.Same(expected1, result1); Assert.Same(expected1, result1);
// Act 2 // Act 2
fileProvider.DeleteFile(path); fileProvider.DeleteFile(path);
fileProvider.GetChangeToken(path).HasChanged = true; fileProvider.GetChangeToken(path).HasChanged = true;
var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); });
// Assert 2 // Assert 2
Assert.Null(result2); Assert.Null(result2);
}
} }
[Fact] [Fact]
@ -110,22 +118,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
// Arrange // Arrange
var path = @"Views\Home\_ViewStart.cshtml"; var path = @"Views\Home\_ViewStart.cshtml";
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var chunkTreeCache = new DefaultChunkTreeCache(fileProvider); using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider))
var expected = new ChunkTree(); {
var expected = new ChunkTree();
// Act 1 // Act 1
var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); });
// Assert 1 // Assert 1
Assert.Null(result1); Assert.Null(result1);
// Act 2 // Act 2
fileProvider.AddFile(path, "test content"); fileProvider.AddFile(path, "test content");
fileProvider.GetChangeToken(path).HasChanged = true; fileProvider.GetChangeToken(path).HasChanged = true;
var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => expected); var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => expected);
// Assert 2 // Assert 2
Assert.Same(expected, result2); Assert.Same(expected, result2);
}
} }
[Fact] [Fact]
@ -140,29 +150,31 @@ namespace Microsoft.AspNet.Mvc.Razor.Host.Directives
clock.SetupGet(c => c.UtcNow) clock.SetupGet(c => c.UtcNow)
.Returns(() => utcNow); .Returns(() => utcNow);
var options = new MemoryCacheOptions { Clock = clock.Object }; 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(); var chunkTree1 = new ChunkTree();
var chunkTree2 = new ChunkTree();
// Act 1 // Act 1
var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => chunkTree1); var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => chunkTree1);
// Assert 1 // Assert 1
Assert.Same(chunkTree1, result1); Assert.Same(chunkTree1, result1);
// Act 2 // Act 2
utcNow = utcNow.AddSeconds(59); utcNow = utcNow.AddSeconds(59);
var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); });
// Assert 2 // Assert 2
Assert.Same(chunkTree1, result2); Assert.Same(chunkTree1, result2);
// Act 3 // Act 3
utcNow = utcNow.AddSeconds(65); utcNow = utcNow.AddSeconds(65);
var result3 = chunkTreeCache.GetOrAdd(path, fileInfo => chunkTree2); var result3 = chunkTreeCache.GetOrAdd(path, fileInfo => chunkTree2);
// Assert 3 // Assert 3
Assert.Same(chunkTree2, result3); Assert.Same(chunkTree2, result3);
}
} }
} }
} }

View File

@ -55,21 +55,23 @@ namespace Microsoft.AspNet.Mvc.Razor
var rootedAppPath = $"{rootPrefix}SomeComputer/Location/Project/"; var rootedAppPath = $"{rootPrefix}SomeComputer/Location/Project/";
var rootedFilePath = $"{rootPrefix}SomeComputer/Location/Project/src/file.cshtml"; var rootedFilePath = $"{rootPrefix}SomeComputer/Location/Project/src/file.cshtml";
var chunkTreeCache = new DefaultChunkTreeCache(new TestFileProvider()); var chunkTreeCache = new DefaultChunkTreeCache(new TestFileProvider());
var host = new MvcRazorHost( using (var host = new MvcRazorHost(
chunkTreeCache, chunkTreeCache,
pathNormalizer: new DesignTimeRazorPathNormalizer(rootedAppPath)); pathNormalizer: new DesignTimeRazorPathNormalizer(rootedAppPath)))
var parser = new RazorParser( {
host.CodeLanguage.CreateCodeParser(), var parser = new RazorParser(
host.CreateMarkupParser(), host.CodeLanguage.CreateCodeParser(),
tagHelperDescriptorResolver: null); host.CreateMarkupParser(),
var chunkInheritanceUtility = new PathValidatingChunkInheritanceUtility(host, chunkTreeCache); tagHelperDescriptorResolver: null);
host.ChunkInheritanceUtility = chunkInheritanceUtility; var chunkInheritanceUtility = new PathValidatingChunkInheritanceUtility(host, chunkTreeCache);
host.ChunkInheritanceUtility = chunkInheritanceUtility;
// Act // Act
host.DecorateRazorParser(parser, rootedFilePath); host.DecorateRazorParser(parser, rootedFilePath);
// Assert // Assert
Assert.Equal("src/file.cshtml", chunkInheritanceUtility.InheritedChunkTreePagePath, StringComparer.Ordinal); Assert.Equal("src/file.cshtml", chunkInheritanceUtility.InheritedChunkTreePagePath, StringComparer.Ordinal);
}
} }
[Theory] [Theory]
@ -81,26 +83,28 @@ namespace Microsoft.AspNet.Mvc.Razor
var rootedAppPath = $"{rootPrefix}SomeComputer/Location/Project/"; var rootedAppPath = $"{rootPrefix}SomeComputer/Location/Project/";
var rootedFilePath = $"{rootPrefix}SomeComputer/Location/Project/src/file.cshtml"; var rootedFilePath = $"{rootPrefix}SomeComputer/Location/Project/src/file.cshtml";
var chunkTreeCache = new DefaultChunkTreeCache(new TestFileProvider()); var chunkTreeCache = new DefaultChunkTreeCache(new TestFileProvider());
var host = new MvcRazorHost( using (var host = new MvcRazorHost(
chunkTreeCache, chunkTreeCache,
pathNormalizer: new DesignTimeRazorPathNormalizer(rootedAppPath)); pathNormalizer: new DesignTimeRazorPathNormalizer(rootedAppPath)))
var chunkInheritanceUtility = new PathValidatingChunkInheritanceUtility(host, chunkTreeCache); {
var codeGeneratorContext = new CodeGeneratorContext( var chunkInheritanceUtility = new PathValidatingChunkInheritanceUtility(host, chunkTreeCache);
new ChunkGeneratorContext( var codeGeneratorContext = new CodeGeneratorContext(
host, new ChunkGeneratorContext(
host.DefaultClassName, host,
host.DefaultNamespace, host.DefaultClassName,
rootedFilePath, host.DefaultNamespace,
shouldGenerateLinePragmas: true), rootedFilePath,
new ErrorSink()); shouldGenerateLinePragmas: true),
var codeGenerator = new CSharpCodeGenerator(codeGeneratorContext); new ErrorSink());
host.ChunkInheritanceUtility = chunkInheritanceUtility; var codeGenerator = new CSharpCodeGenerator(codeGeneratorContext);
host.ChunkInheritanceUtility = chunkInheritanceUtility;
// Act // Act
host.DecorateCodeGenerator(codeGenerator, codeGeneratorContext); host.DecorateCodeGenerator(codeGenerator, codeGeneratorContext);
// Assert // Assert
Assert.Equal("src/file.cshtml", chunkInheritanceUtility.InheritedChunkTreePagePath, StringComparer.Ordinal); Assert.Equal("src/file.cshtml", chunkInheritanceUtility.InheritedChunkTreePagePath, StringComparer.Ordinal);
}
} }
[Fact] [Fact]
@ -108,13 +112,14 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var host = new MvcRazorHost(new DefaultChunkTreeCache(fileProvider)); using (var host = new MvcRazorHost(new DefaultChunkTreeCache(fileProvider)))
{
// Act
var instrumented = host.EnableInstrumentation;
// Act // Assert
var instrumented = host.EnableInstrumentation; Assert.True(instrumented);
}
// Assert
Assert.True(instrumented);
} }
[Fact] [Fact]
@ -122,12 +127,13 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider)) using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{ {
DesignTimeMode = true DesignTimeMode = true
}; })
var expectedLineMappings = new[]
{ {
var expectedLineMappings = new[]
{
BuildLineMapping( BuildLineMapping(
documentAbsoluteIndex: 33, documentAbsoluteIndex: 33,
documentLineIndex: 2, documentLineIndex: 2,
@ -160,13 +166,14 @@ namespace Microsoft.AspNet.Mvc.Razor
generatedLineIndex: 53, generatedLineIndex: 53,
generatedCharacterIndex: 87, generatedCharacterIndex: 87,
contentLength: 5), contentLength: 5),
}; };
// Act and Assert // Act and Assert
RunDesignTimeTest( RunDesignTimeTest(
host, host,
testName: "ModelExpressionTagHelper", testName: "ModelExpressionTagHelper",
expectedLineMappings: expectedLineMappings); expectedLineMappings: expectedLineMappings);
}
} }
[Theory] [Theory]
@ -181,10 +188,11 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var host = new TestMvcRazorHost(new DefaultChunkTreeCache(fileProvider)); using (var host = new TestMvcRazorHost(new DefaultChunkTreeCache(fileProvider)))
{
// Act and Assert // Act and Assert
RunRuntimeTest(host, scenarioName); RunRuntimeTest(host, scenarioName);
}
} }
[Fact] [Fact]
@ -192,13 +200,14 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider)) using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{ {
DesignTimeMode = true DesignTimeMode = true
}; })
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{ {
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
BuildLineMapping( BuildLineMapping(
documentAbsoluteIndex: 13, documentAbsoluteIndex: 13,
documentLineIndex: 0, documentLineIndex: 0,
@ -215,10 +224,11 @@ namespace Microsoft.AspNet.Mvc.Razor
generatedLineIndex: 37, generatedLineIndex: 37,
generatedCharacterIndex: 6, generatedCharacterIndex: 6,
contentLength: 21), contentLength: 21),
}; };
// Act and Assert // Act and Assert
RunDesignTimeTest(host, "Basic", expectedLineMappings); RunDesignTimeTest(host, "Basic", expectedLineMappings);
}
} }
[Fact] [Fact]
@ -226,13 +236,14 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider)) using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{ {
DesignTimeMode = true DesignTimeMode = true
}; })
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{ {
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
BuildLineMapping( BuildLineMapping(
documentAbsoluteIndex: 8, documentAbsoluteIndex: 8,
documentLineIndex: 0, documentLineIndex: 0,
@ -241,10 +252,11 @@ namespace Microsoft.AspNet.Mvc.Razor
generatedLineIndex: 21, generatedLineIndex: 21,
generatedCharacterIndex: 8, generatedCharacterIndex: 8,
contentLength: 26), contentLength: 26),
}; };
// Act and Assert // Act and Assert
RunDesignTimeTest(host, "_ViewImports", expectedLineMappings); RunDesignTimeTest(host, "_ViewImports", expectedLineMappings);
}
} }
[Fact] [Fact]
@ -252,13 +264,14 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider)) using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{ {
DesignTimeMode = true DesignTimeMode = true
}; })
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{ {
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
BuildLineMapping( BuildLineMapping(
documentAbsoluteIndex: 1, documentAbsoluteIndex: 1,
documentLineIndex: 0, documentLineIndex: 0,
@ -275,10 +288,11 @@ namespace Microsoft.AspNet.Mvc.Razor
generatedLineIndex: 26, generatedLineIndex: 26,
generatedCharacterIndex: 8, generatedCharacterIndex: 8,
contentLength: 20), contentLength: 20),
}; };
// Act and Assert // Act and Assert
RunDesignTimeTest(host, "Inject", expectedLineMappings); RunDesignTimeTest(host, "Inject", expectedLineMappings);
}
} }
[Fact] [Fact]
@ -286,13 +300,14 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider)) using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{ {
DesignTimeMode = true DesignTimeMode = true
}; })
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{ {
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
BuildLineMapping( BuildLineMapping(
documentAbsoluteIndex: 7, documentAbsoluteIndex: 7,
documentLineIndex: 0, documentLineIndex: 0,
@ -317,10 +332,11 @@ namespace Microsoft.AspNet.Mvc.Razor
generatedLineIndex: 33, generatedLineIndex: 33,
generatedCharacterIndex: 8, generatedCharacterIndex: 8,
contentLength: 23), contentLength: 23),
}; };
// Act and Assert // Act and Assert
RunDesignTimeTest(host, "InjectWithModel", expectedLineMappings); RunDesignTimeTest(host, "InjectWithModel", expectedLineMappings);
}
} }
[Fact] [Fact]
@ -328,13 +344,14 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider)) using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{ {
DesignTimeMode = true DesignTimeMode = true
}; })
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{ {
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
BuildLineMapping( BuildLineMapping(
documentAbsoluteIndex: 7, documentAbsoluteIndex: 7,
documentLineIndex: 0, documentLineIndex: 0,
@ -375,10 +392,11 @@ namespace Microsoft.AspNet.Mvc.Razor
generatedLineIndex: 49, generatedLineIndex: 49,
generatedCharacterIndex: 8, generatedCharacterIndex: 8,
contentLength: 24), contentLength: 24),
}; };
// Act and Assert // Act and Assert
RunDesignTimeTest(host, "InjectWithSemicolon", expectedLineMappings); RunDesignTimeTest(host, "InjectWithSemicolon", expectedLineMappings);
}
} }
[Fact] [Fact]
@ -386,13 +404,14 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider)) using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{ {
DesignTimeMode = true DesignTimeMode = true
}; })
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{ {
host.NamespaceImports.Clear();
var expectedLineMappings = new[]
{
BuildLineMapping( BuildLineMapping(
documentAbsoluteIndex: 7, documentAbsoluteIndex: 7,
documentLineIndex: 0, documentLineIndex: 0,
@ -401,10 +420,11 @@ namespace Microsoft.AspNet.Mvc.Razor
generatedLineIndex: 11, generatedLineIndex: 11,
generatedCharacterIndex: 28, generatedCharacterIndex: 28,
contentLength: 30), contentLength: 30),
}; };
// Act and Assert // Act and Assert
RunDesignTimeTest(host, "Model", expectedLineMappings); RunDesignTimeTest(host, "Model", expectedLineMappings);
}
} }
[Fact] [Fact]
@ -412,31 +432,33 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider)) using (var host = new MvcRazorHostWithNormalizedNewLine(new DefaultChunkTreeCache(fileProvider))
{ {
DesignTimeMode = true DesignTimeMode = true
}; })
host.NamespaceImports.Clear();
var inputFile = "TestFiles/Input/MultipleModels.cshtml";
var outputFile = "TestFiles/Output/DesignTime/MultipleModels.cs";
var expectedCode = ResourceFile.ReadResource(_assembly, outputFile, sourceFile: false);
// Act
GeneratorResults results;
using (var stream = ResourceFile.GetResourceStream(_assembly, inputFile, sourceFile: true))
{ {
results = host.GenerateCode(inputFile, stream); host.NamespaceImports.Clear();
} var inputFile = "TestFiles/Input/MultipleModels.cshtml";
var outputFile = "TestFiles/Output/DesignTime/MultipleModels.cs";
var expectedCode = ResourceFile.ReadResource(_assembly, outputFile, sourceFile: false);
// Assert // Act
Assert.False(results.Success); GeneratorResults results;
var parserError = Assert.Single(results.ParserErrors); using (var stream = ResourceFile.GetResourceStream(_assembly, inputFile, sourceFile: true))
Assert.Equal("Only one 'model' statement is allowed in a file.", parserError.Message); {
results = host.GenerateCode(inputFile, stream);
}
// Assert
Assert.False(results.Success);
var parserError = Assert.Single(results.ParserErrors);
Assert.Equal("Only one 'model' statement is allowed in a file.", parserError.Message);
#if GENERATE_BASELINES #if GENERATE_BASELINES
ResourceFile.UpdateFile(_assembly, outputFile, expectedCode, results.GeneratedCode); ResourceFile.UpdateFile(_assembly, outputFile, expectedCode, results.GeneratedCode);
#else #else
Assert.Equal(expectedCode, results.GeneratedCode, ignoreLineEndingDifferences: true); Assert.Equal(expectedCode, results.GeneratedCode, ignoreLineEndingDifferences: true);
#endif #endif
}
} }
private static void RunRuntimeTest( private static void RunRuntimeTest(