public void GetOrAdd_UpdatesCache_IfFileExpirationTriggerExpires() { // Arrange var path = @"Views\Home\_ViewStart.cshtml"; var fileProvider = new TestFileProvider(); fileProvider.AddFile(path, "test content"); using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider)) { var expected1 = new ChunkTree(); var expected2 = new ChunkTree(); // Act 1 var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected1); // Assert 1 Assert.Same(expected1, result1); // Act 2 fileProvider.GetChangeToken(path).HasChanged = true; var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => expected2); // Assert 2 Assert.Same(expected2, result2); } }
public void Merge_UsesTheLastInjectChunkOfAPropertyName() { // Arrange var merger = new InjectChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new Chunk[] { new LiteralChunk(), new InjectChunk("SomeOtherType", "Property"), new InjectChunk("DifferentPropertyType", "DifferentProperty"), new InjectChunk("SomeType", "Property"), }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Collection(chunkTree.Children, chunk => { var injectChunk = Assert.IsType <InjectChunk>(chunk); Assert.Equal("SomeType", injectChunk.TypeName); Assert.Equal("Property", injectChunk.MemberName); }, chunk => { var injectChunk = Assert.IsType <InjectChunk>(chunk); Assert.Equal("DifferentPropertyType", injectChunk.TypeName); Assert.Equal("DifferentProperty", injectChunk.MemberName); }); }
public void Merge_DoesNotAddMoreThanOneInstanceOfTheSameInheritedNamespace() { // Arrange var merger = new UsingChunkMerger(); var chunkTree = new ChunkTree(); var inheritedChunks = new Chunk[] { new LiteralChunk(), new UsingChunk { Namespace = "Microsoft.AspNet.Mvc" }, new UsingChunk { Namespace = "Microsoft.AspNet.Mvc" }, new UsingChunk { Namespace = "Microsoft.AspNet.Mvc.Razor" } }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Equal(2, chunkTree.Chunks.Count); var chunk = Assert.IsType <UsingChunk>(chunkTree.Chunks[0]); Assert.Equal("Microsoft.AspNet.Mvc", chunk.Namespace); chunk = Assert.IsType <UsingChunk>(chunkTree.Chunks[1]); Assert.Equal("Microsoft.AspNet.Mvc.Razor", chunk.Namespace); }
public void Merge_PicksLastBaseTypeChunkFromChunkTree() { // Arrange var merger = new SetBaseTypeChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new Chunk[] { new SetBaseTypeChunk { TypeName = "MyBase2" }, new LiteralChunk(), new SetBaseTypeChunk { TypeName = "MyBase1" }, }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert var chunk = Assert.Single(chunkTree.Chunks); var setBaseTypeChunk = Assert.IsType <SetBaseTypeChunk>(chunk); Assert.Equal("MyBase1", setBaseTypeChunk.TypeName); }
public void Merge_MatchesNamespacesInCaseSensitiveManner() { // Arrange var merger = new UsingChunkMerger(); var chunkTree = new ChunkTree(); var inheritedChunks = new[] { new UsingChunk { Namespace = "Microsoft.AspNet.Mvc" }, new UsingChunk { Namespace = "Microsoft.AspNet.mvc" } }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Equal(2, chunkTree.Chunks.Count); var chunk = Assert.IsType <UsingChunk>(chunkTree.Chunks[0]); Assert.Equal("Microsoft.AspNet.Mvc", chunk.Namespace); chunk = Assert.IsType <UsingChunk>(chunkTree.Chunks[1]); Assert.Equal("Microsoft.AspNet.mvc", chunk.Namespace); }
public void Merge_MatchesPropertyNameInCaseSensitiveManner() { // Arrange var merger = new InjectChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new[] { new InjectChunk("MyTypeB", "different-property"), new InjectChunk("MyType", "myproperty"), }; // Act merger.VisitChunk(new InjectChunk("MyType", "MyProperty")); merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Equal(2, chunkTree.Chunks.Count); var injectChunk = Assert.IsType <InjectChunk>(chunkTree.Chunks[0]); Assert.Equal("MyType", injectChunk.TypeName); Assert.Equal("myproperty", injectChunk.MemberName); injectChunk = Assert.IsType <InjectChunk>(chunkTree.Chunks[1]); Assert.Equal("MyTypeB", injectChunk.TypeName); Assert.Equal("different-property", injectChunk.MemberName); }
/// <inheritdoc /> public void MergeInheritedChunks(ChunkTree chunkTree, IReadOnlyList <Chunk> inheritedChunks) { if (chunkTree == null) { throw new ArgumentNullException(nameof(chunkTree)); } if (inheritedChunks == null) { throw new ArgumentNullException(nameof(inheritedChunks)); } if (!_isBaseTypeSet) { for (var i = inheritedChunks.Count - 1; i >= 0; i--) { var baseTypeChunk = inheritedChunks[i] as SetBaseTypeChunk; if (baseTypeChunk != null) { chunkTree.Chunks.Add(TransformChunk(baseTypeChunk)); break; } } } }
/// <summary> /// Merges <see cref="Chunk"/> inherited by default and <see cref="ChunkTree"/> instances produced by parsing /// <c>_ViewImports</c> files into the specified <paramref name="chunkTree"/>. /// </summary> /// <param name="chunkTree">The <see cref="ChunkTree"/> to merge in to.</param> /// <param name="inheritedChunkTrees"><see cref="IReadOnlyList{ChunkTree}"/> inherited from <c>_ViewImports</c> /// files.</param> /// <param name="defaultModel">The list of chunks to merge.</param> public void MergeInheritedChunkTrees( [NotNull] ChunkTree chunkTree, [NotNull] IReadOnlyList <ChunkTree> inheritedChunkTrees, string defaultModel) { var mergerMappings = GetMergerMappings(chunkTree, defaultModel); IChunkMerger merger; // We merge chunks into the ChunkTree in two passes. In the first pass, we traverse the ChunkTree visiting // a mapped IChunkMerger for types that are registered. foreach (var chunk in chunkTree.Chunks) { if (mergerMappings.TryGetValue(chunk.GetType(), out merger)) { merger.VisitChunk(chunk); } } // 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 ChunkTree or ignore the chunk based on the merging // rules. // Read the chunks outside in - that is chunks from the _ViewImports 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 = inheritedChunkTrees.SelectMany(tree => tree.Chunks) .Concat(_defaultInheritedChunks); foreach (var chunk in chunksToMerge) { if (mergerMappings.TryGetValue(chunk.GetType(), out merger)) { merger.Merge(chunkTree, chunk); } } }
/// <summary> /// Merges <see cref="Chunk"/> inherited by default and <see cref="ChunkTree"/> instances produced by parsing /// <c>_ViewImports</c> files into the specified <paramref name="chunkTree"/>. /// </summary> /// <param name="chunkTree">The <see cref="ChunkTree"/> to merge in to.</param> /// <param name="inheritedChunkTrees"><see cref="IReadOnlyList{ChunkTree}"/> inherited from <c>_ViewImports</c> /// files.</param> /// <param name="defaultModel">The default model <see cref="Type"/> name.</param> public void MergeInheritedChunkTrees( ChunkTree chunkTree, IReadOnlyList <ChunkTree> inheritedChunkTrees, string defaultModel) { if (chunkTree == null) { throw new ArgumentNullException(nameof(chunkTree)); } if (inheritedChunkTrees == null) { throw new ArgumentNullException(nameof(inheritedChunkTrees)); } var chunkMergers = GetChunkMergers(chunkTree, defaultModel); // We merge chunks into the ChunkTree in two passes. In the first pass, we traverse the ChunkTree visiting // a mapped IChunkMerger for types that are registered. foreach (var chunk in chunkTree.Chunks) { foreach (var merger in chunkMergers) { merger.VisitChunk(chunk); } } var inheritedChunks = _defaultInheritedChunks.Concat( inheritedChunkTrees.SelectMany(tree => tree.Chunks)).ToArray(); foreach (var merger in chunkMergers) { merger.MergeInheritedChunks(chunkTree, inheritedChunks); } }
private void AddImports(ChunkTree chunkTree, CSharpCodeWriter writer, IEnumerable <string> defaultImports) { // Write out using directives var usingVisitor = new CSharpUsingVisitor(writer, Context); foreach (var chunk in Tree.Children) { usingVisitor.Accept(chunk); } _pageUsings = new HashSet <string>(usingVisitor.ImportedUsings); foreach (var ns in defaultImports) { _pageUsings.Add(ns); } defaultImports = defaultImports.Except(usingVisitor.ImportedUsings); foreach (string import in defaultImports) { writer.WriteUsing(import); } var taskNamespace = typeof(Task).Namespace; // We need to add the task namespace but ONLY if it hasn't been added by the default imports or using imports yet. if (!defaultImports.Contains(taskNamespace) && !usingVisitor.ImportedUsings.Contains(taskNamespace)) { writer.WriteUsing(taskNamespace); } }
public void Merge_MatchesPropertyNameInCaseSensitiveManner() { // Arrange var merger = new InjectChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new[] { new InjectChunk("MyTypeB", "different-property"), new InjectChunk("MyType", "myproperty"), }; // Act merger.VisitChunk(new InjectChunk("MyType", "MyProperty")); merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Equal(2, chunkTree.Children.Count); var injectChunk = Assert.IsType<InjectChunk>(chunkTree.Children[0]); Assert.Equal("MyType", injectChunk.TypeName); Assert.Equal("myproperty", injectChunk.MemberName); injectChunk = Assert.IsType<InjectChunk>(chunkTree.Children[1]); Assert.Equal("MyTypeB", injectChunk.TypeName); Assert.Equal("different-property", injectChunk.MemberName); }
public void Merge_IgnoresNamespacesThatHaveBeenVisitedDuringMerge() { // Arrange var merger = new UsingChunkMerger(); var chunkTree = new ChunkTree(); // Act merger.Merge(chunkTree, new UsingChunk { Namespace = "Microsoft.AspNet.Mvc" }); merger.Merge(chunkTree, new UsingChunk { Namespace = "Microsoft.AspNet.Mvc" }); merger.Merge(chunkTree, new UsingChunk { Namespace = "Microsoft.AspNet.Mvc.Razor" }); // Assert Assert.Equal(2, chunkTree.Chunks.Count); var chunk = Assert.IsType <UsingChunk>(chunkTree.Chunks[0]); Assert.Equal("Microsoft.AspNet.Mvc", chunk.Namespace); chunk = Assert.IsType <UsingChunk>(chunkTree.Chunks[1]); Assert.Equal("Microsoft.AspNet.Mvc.Razor", chunk.Namespace); }
public void Merge_AddsNamespacesThatHaveNotBeenVisitedInChunkTree() { // Arrange var expected = "MyApp.Models"; var merger = new UsingChunkMerger(); var chunkTree = new ChunkTree(); var inheritedChunks = new Chunk[] { new UsingChunk { Namespace = expected }, }; // Act merger.VisitChunk(new UsingChunk { Namespace = "Microsoft.AspNet.Mvc" }); merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert var chunk = Assert.Single(chunkTree.Chunks); var usingChunk = Assert.IsType <UsingChunk>(chunk); Assert.Equal(expected, usingChunk.Namespace); }
public void GetOrAdd_UpdatesCacheWithNullValue_IfFileWasDeleted() { // Arrange var path = @"Views\Home\_ViewStart.cshtml"; var fileProvider = new TestFileProvider(); fileProvider.AddFile(path, "test content"); using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider)) { var expected1 = new ChunkTree(); // Act 1 var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected1); // Assert 1 Assert.Same(expected1, result1); // Act 2 fileProvider.DeleteFile(path); fileProvider.GetChangeToken(path).HasChanged = true; var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); // Assert 2 Assert.Null(result2); } }
/// <summary> /// Returns the type name of the Model specified via a <see cref="ModelChunk"/> in the /// <paramref name="chunkTree"/> if specified or the default model type. /// </summary> /// <param name="chunkTree">The <see cref="ChunkTree"/> to scan for <see cref="ModelChunk"/>s in.</param> /// <param name="defaultModelName">The <see cref="Type"/> name of the default model.</param> /// <returns>The model type name for the generated page.</returns> public static string GetModelTypeName( [NotNull] ChunkTree chunkTree, [NotNull] string defaultModelName) { var modelChunk = GetModelChunk(chunkTree); return(modelChunk != null ? modelChunk.ModelType : defaultModelName); }
protected override void AcceptTreeCore(ChunkTree tree) { base.AcceptTreeCore(tree); if (_modelChunk != null) { WriteModelChunkLineMapping(); } }
/// <summary> /// Returns the <see cref="ModelChunk"/> used to determine the model name for the page generated /// using the specified <paramref name="chunkTree"/> /// </summary> /// <param name="chunkTree">The <see cref="ChunkTree"/> to scan for <see cref="ModelChunk"/>s in.</param> /// <returns>The last <see cref="ModelChunk"/> in the <see cref="ChunkTree"/> if found, <c>null</c> otherwise. /// </returns> public static ModelChunk GetModelChunk([NotNull] ChunkTree chunkTree) { // If there's more than 1 model chunk there will be a Razor error BUT we want intellisense to show up on // the current model chunk that the user is typing. return(chunkTree .Chunks .OfType <ModelChunk>() .LastOrDefault()); }
public void MergeInheritedChunks_MergesDefaultInheritedChunks() { // Arrange var fileProvider = new TestFileProvider(); fileProvider.AddFile(@"/Views/_ViewImports.cshtml", "@inject DifferentHelper<TModel> Html"); var cache = new DefaultChunkTreeCache(fileProvider); using (var host = new MvcRazorHost(cache)) { var defaultChunks = new Chunk[] { new InjectChunk("MyTestHtmlHelper", "Html"), new UsingChunk { Namespace = "AppNamespace.Model" }, }; var inheritedChunkTrees = new ChunkTree[] { new ChunkTree { Children = new Chunk[] { new UsingChunk { Namespace = "InheritedNamespace" }, new LiteralChunk { Text = "some text" } } }, new ChunkTree { Children = new Chunk[] { new UsingChunk { Namespace = "AppNamespace.Model" }, } } }; var utility = new ChunkInheritanceUtility(host, cache, defaultChunks); var chunkTree = new ChunkTree(); // Act utility.MergeInheritedChunkTrees(chunkTree, inheritedChunkTrees, "dynamic"); // Assert Assert.Collection(chunkTree.Children, chunk => Assert.Same(defaultChunks[1], chunk), chunk => Assert.Same(inheritedChunkTrees[0].Children[0], chunk), chunk => Assert.Same(defaultChunks[0], chunk)); } }
/// <summary> /// Instantiates a new <see cref="GeneratorResults"/> instance. /// </summary> /// <param name="parserResults">The results of parsing a document.</param> /// <param name="codeGeneratorResult">The results of generating code for the document.</param> /// <param name="chunkTree">A <see cref="ChunkTree"/> for the document.</param> public GeneratorResults([NotNull] ParserResults parserResults, [NotNull] CodeGeneratorResult codeGeneratorResult, [NotNull] ChunkTree chunkTree) : this(parserResults.Document, parserResults.TagHelperDescriptors, parserResults.ErrorSink, codeGeneratorResult, chunkTree) { }
private static IChunkMerger[] GetChunkMergers(ChunkTree chunkTree, string defaultModel) { var modelType = ChunkHelper.GetModelTypeName(chunkTree, defaultModel); return(new IChunkMerger[] { new UsingChunkMerger(), new SetBaseTypeChunkMerger(modelType) }); }
/// <inheritdoc /> public void Merge([NotNull] ChunkTree chunkTree, [NotNull] Chunk chunk) { var namespaceChunk = ChunkHelper.EnsureChunk <UsingChunk>(chunk); if (!_currentUsings.Contains(namespaceChunk.Namespace)) { _currentUsings.Add(namespaceChunk.Namespace); chunkTree.Chunks.Add(namespaceChunk); } }
/// <inheritdoc /> public void Merge([NotNull] ChunkTree chunkTree, [NotNull] Chunk chunk) { var injectChunk = ChunkHelper.EnsureChunk <InjectChunk>(chunk); if (!_addedMemberNames.Contains(injectChunk.MemberName)) { _addedMemberNames.Add(injectChunk.MemberName); chunkTree.Chunks.Add(TransformChunk(injectChunk)); } }
public void MergeInheritedChunks_MergesDefaultInheritedChunks() { // Arrange var fileProvider = new TestFileProvider(); fileProvider.AddFile(PlatformNormalizer.NormalizePath(@"Views\_ViewImports.cshtml"), "@inject DifferentHelper<TModel> Html"); var cache = new DefaultChunkTreeCache(fileProvider); var host = new MvcRazorHost(cache); var defaultChunks = new Chunk[] { new InjectChunk("MyTestHtmlHelper", "Html"), new UsingChunk { Namespace = "AppNamespace.Model" }, }; var inheritedChunkTrees = new ChunkTree[] { new ChunkTree { Chunks = new Chunk[] { new UsingChunk { Namespace = "InheritedNamespace" }, new LiteralChunk { Text = "some text" } } }, new ChunkTree { Chunks = new Chunk[] { new UsingChunk { Namespace = "AppNamespace.Model" }, } } }; var utility = new ChunkInheritanceUtility(host, cache, defaultChunks); var chunkTree = new ChunkTree(); // Act utility.MergeInheritedChunkTrees(chunkTree, inheritedChunkTrees, "dynamic"); // Assert Assert.Equal(3, chunkTree.Chunks.Count); Assert.Same(inheritedChunkTrees[0].Chunks[0], chunkTree.Chunks[0]); Assert.Same(inheritedChunkTrees[1].Chunks[0], chunkTree.Chunks[1]); Assert.Same(defaultChunks[0], chunkTree.Chunks[2]); }
/// <summary> /// Instantiates a new <see cref="GeneratorResults"/> instance. /// </summary> /// <param name="document">The <see cref="Block"/> for the syntax tree.</param> /// <param name="tagHelperDescriptors"> /// The <see cref="TagHelperDescriptor"/>s that apply to the current Razor document. /// </param> /// <param name="errorSink"> /// The <see cref="ErrorSink"/> used to collect <see cref="RazorError"/>s encountered when parsing the /// current Razor document. /// </param> /// <param name="codeGeneratorResult">The results of generating code for the document.</param> /// <param name="chunkTree">A <see cref="ChunkTree"/> for the document.</param> public GeneratorResults([NotNull] Block document, [NotNull] IEnumerable <TagHelperDescriptor> tagHelperDescriptors, [NotNull] ErrorSink errorSink, [NotNull] CodeGeneratorResult codeGeneratorResult, [NotNull] ChunkTree chunkTree) : base(document, tagHelperDescriptors, errorSink) { GeneratedCode = codeGeneratorResult.Code; DesignTimeLineMappings = codeGeneratorResult.DesignTimeLineMappings; ChunkTree = chunkTree; }
private static Dictionary <Type, IChunkMerger> GetMergerMappings(ChunkTree chunkTree, string defaultModel) { var modelType = ChunkHelper.GetModelTypeName(chunkTree, defaultModel); return(new Dictionary <Type, IChunkMerger> { { typeof(UsingChunk), new UsingChunkMerger() }, { typeof(InjectChunk), new InjectChunkMerger(modelType) }, { typeof(SetBaseTypeChunk), new SetBaseTypeChunkMerger(modelType) } }); }
/// <inheritdoc /> public void Merge([NotNull] ChunkTree chunkTree, [NotNull] Chunk chunk) { if (!_isBaseTypeSet) { var baseTypeChunk = ChunkHelper.EnsureChunk <SetBaseTypeChunk>(chunk); // The base type can set exactly once and the first one we encounter wins. _isBaseTypeSet = true; chunkTree.Chunks.Add(TransformChunk(baseTypeChunk)); } }
public void AcceptTree(ChunkTree tree) { if (Context.Host.DesignTimeMode) { using (Writer.BuildMethodDeclaration("private", "void", "@" + DesignTimeHelperMethodName)) { using (Writer.BuildDisableWarningScope(DisableVariableNamingWarnings)) { AcceptTreeCore(tree); } } } }
public void Merge_IgnoresChunkIfChunkWithMatchingPropertyNameWasVisitedInChunkTree() { // Arrange var merger = new InjectChunkMerger("dynamic"); var chunkTree = new ChunkTree(); // Act merger.VisitChunk(new InjectChunk("MyTypeA", "MyProperty")); merger.Merge(chunkTree, new InjectChunk("MyTypeB", "MyProperty")); // Assert Assert.Empty(chunkTree.Chunks); }
/// <summary> /// Returns the <see cref="ModelChunk"/> used to determine the model name for the page generated /// using the specified <paramref name="chunkTree"/> /// </summary> /// <param name="chunkTree">The <see cref="ChunkTree"/> to scan for <see cref="ModelChunk"/>s in.</param> /// <returns>The last <see cref="ModelChunk"/> in the <see cref="ChunkTree"/> if found, <c>null</c> otherwise. /// </returns> public static ModelChunk GetModelChunk(ChunkTree chunkTree) { if (chunkTree == null) { throw new ArgumentNullException(nameof(chunkTree)); } // If there's more than 1 model chunk there will be a Razor error BUT we want intellisense to show up on // the current model chunk that the user is typing. return(chunkTree .Children .OfType <ModelChunk>() .LastOrDefault()); }
/// <summary> /// Initializes a new instance of <see cref="ChunkTreeResult"/>. /// </summary> /// <param name="chunkTree">The <see cref="AspNetCore.Razor.Chunks.ChunkTree"/> generated from the file at the /// given <paramref name="filePath"/>.</param> /// <param name="filePath">The path to the file that generated the given <paramref name="chunkTree"/>.</param> public ChunkTreeResult(ChunkTree chunkTree, string filePath) { if (chunkTree == null) { throw new ArgumentNullException(nameof(chunkTree)); } if (filePath == null) { throw new ArgumentNullException(nameof(filePath)); } ChunkTree = chunkTree; FilePath = filePath; }
public void Merge_ReplacesTModelTokensWithModel() { // Arrange var merger = new InjectChunkMerger("MyTestModel2"); var chunkTree = new ChunkTree(); // Act merger.Merge(chunkTree, new InjectChunk("MyHelper<TModel>", "MyProperty")); // Assert var chunk = Assert.Single(chunkTree.Chunks); var injectChunk = Assert.IsType <InjectChunk>(chunk); Assert.Equal("MyHelper<MyTestModel2>", injectChunk.TypeName); Assert.Equal("MyProperty", injectChunk.MemberName); }
public void Merge_IgnoresChunkIfChunkWithMatchingPropertyNameWasVisitedInChunkTree() { // Arrange var merger = new InjectChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new[] { new InjectChunk("MyTypeB", "MyProperty") }; // Act merger.VisitChunk(new InjectChunk("MyTypeA", "MyProperty")); merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Empty(chunkTree.Children); }
public void Merge_IgnoresSetBaseTypeChunksIfChunkTreeContainsOne() { // Arrange var merger = new SetBaseTypeChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new[] { new SetBaseTypeChunk { TypeName = "MyBaseType2" } }; // Act merger.VisitChunk(new SetBaseTypeChunk { TypeName = "MyBaseType1" }); merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Empty(chunkTree.Children); }
public void Merge_IgnoresNamespacesThatHaveBeenVisitedInChunkTree() { // Arrange var merger = new UsingChunkMerger(); var chunkTree = new ChunkTree(); var inheritedChunks = new Chunk[] { new UsingChunk { Namespace = "Microsoft.AspNetCore.Mvc" }, new InjectChunk("Foo", "Bar") }; // Act merger.VisitChunk(new UsingChunk { Namespace = "Microsoft.AspNetCore.Mvc" }); merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Empty(chunkTree.Children); }
public void Merge_SetsBaseTypeIfItHasNotBeenSetInChunkTree() { // Arrange var expected = "MyApp.Razor.MyBaseType"; var merger = new SetBaseTypeChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new[] { new SetBaseTypeChunk { TypeName = expected } }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert var chunk = Assert.Single(chunkTree.Children); var setBaseTypeChunk = Assert.IsType<SetBaseTypeChunk>(chunk); Assert.Equal(expected, setBaseTypeChunk.TypeName); }
public void Merge_PicksLastBaseTypeChunkFromChunkTree() { // Arrange var merger = new SetBaseTypeChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new Chunk[] { new SetBaseTypeChunk { TypeName = "MyBase2" }, new LiteralChunk(), new SetBaseTypeChunk { TypeName = "MyBase1" }, }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert var chunk = Assert.Single(chunkTree.Children); var setBaseTypeChunk = Assert.IsType<SetBaseTypeChunk>(chunk); Assert.Equal("MyBase1", setBaseTypeChunk.TypeName); }
public void Merge_AddsNamespacesThatHaveNotBeenVisitedInChunkTree() { // Arrange var expected = "MyApp.Models"; var merger = new UsingChunkMerger(); var chunkTree = new ChunkTree(); var inheritedChunks = new Chunk[] { new UsingChunk { Namespace = expected }, }; // Act merger.VisitChunk(new UsingChunk { Namespace = "Microsoft.AspNetCore.Mvc" }); merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert var chunk = Assert.Single(chunkTree.Children); var usingChunk = Assert.IsType<UsingChunk>(chunk); Assert.Equal(expected, usingChunk.Namespace); }
public void Merge_AddsChunkIfChunkWithMatchingPropertyNameWasNotVisitedInChunkTree() { // Arrange var expectedType = "MyApp.MyHelperType"; var expectedProperty = "MyHelper"; var merger = new InjectChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new[] { new InjectChunk(expectedType, expectedProperty) }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert var chunk = Assert.Single(chunkTree.Children); var injectChunk = Assert.IsType<InjectChunk>(chunk); Assert.Equal(expectedType, injectChunk.TypeName); Assert.Equal(expectedProperty, injectChunk.MemberName); }
public void GetOrAdd_ReturnsCachedEntriesOnSubsequentCalls() { // Arrange var path = @"Views\_ViewStart.cshtml"; var mockFileProvider = new Mock<TestFileProvider> { CallBase = true }; var fileProvider = mockFileProvider.Object; fileProvider.AddFile(path, "test content"); using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider)) { var expected = new ChunkTree(); // Act var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected); var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); // Assert Assert.Same(expected, result1); Assert.Same(expected, result2); mockFileProvider.Verify(f => f.GetFileInfo(It.IsAny<string>()), Times.Once()); } }
/// <inheritdoc /> public void MergeInheritedChunks(ChunkTree chunkTree, IReadOnlyList<Chunk> inheritedChunks) { if (chunkTree == null) { throw new ArgumentNullException(nameof(chunkTree)); } if (inheritedChunks == null) { throw new ArgumentNullException(nameof(inheritedChunks)); } var namespaceChunks = inheritedChunks.OfType<UsingChunk>(); foreach (var namespaceChunk in namespaceChunks) { if (_currentUsings.Add(namespaceChunk.Namespace)) { chunkTree.Children.Add(namespaceChunk); } } }
/// <summary> /// Instantiates a new <see cref="GeneratorResults"/> instance. /// </summary> /// <param name="parserResults">The results of parsing a document.</param> /// <param name="codeGeneratorResult">The results of generating code for the document.</param> /// <param name="chunkTree">A <see cref="ChunkTree"/> for the document.</param> public GeneratorResults(ParserResults parserResults, CodeGeneratorResult codeGeneratorResult, ChunkTree chunkTree) : this(parserResults.Document, parserResults.TagHelperDescriptors, parserResults.ErrorSink, codeGeneratorResult, chunkTree) { if (parserResults == null) { throw new ArgumentNullException(nameof(parserResults)); } if (codeGeneratorResult == null) { throw new ArgumentNullException(nameof(codeGeneratorResult)); } if (chunkTree == null) { throw new ArgumentNullException(nameof(chunkTree)); } }
public void Merge_DoesNotAddMoreThanOneInstanceOfTheSameInheritedNamespace() { // Arrange var merger = new UsingChunkMerger(); var chunkTree = new ChunkTree(); var inheritedChunks = new Chunk[] { new LiteralChunk(), new UsingChunk { Namespace = "Microsoft.AspNetCore.Mvc" }, new UsingChunk { Namespace = "Microsoft.AspNetCore.Mvc" }, new UsingChunk { Namespace = "Microsoft.AspNetCore.Mvc.Razor" } }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Equal(2, chunkTree.Children.Count); var chunk = Assert.IsType<UsingChunk>(chunkTree.Children[0]); Assert.Equal("Microsoft.AspNetCore.Mvc", chunk.Namespace); chunk = Assert.IsType<UsingChunk>(chunkTree.Children[1]); Assert.Equal("Microsoft.AspNetCore.Mvc.Razor", chunk.Namespace); }
public void GetOrAdd_ExpiresEntriesAfterOneMinute() { // Arrange var path = @"Views\Home\_ViewStart.cshtml"; var fileProvider = new TestFileProvider(); fileProvider.AddFile(path, "some content"); var clock = new Mock<ISystemClock>(); var utcNow = DateTimeOffset.UtcNow; clock.SetupGet(c => c.UtcNow) .Returns(() => utcNow); var options = new MemoryCacheOptions { Clock = clock.Object }; using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider, options)) { var chunkTree1 = new ChunkTree(); var chunkTree2 = new ChunkTree(); // Act 1 var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => chunkTree1); // Assert 1 Assert.Same(chunkTree1, result1); // Act 2 utcNow = utcNow.AddSeconds(59); var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); // Assert 2 Assert.Same(chunkTree1, result2); // Act 3 utcNow = utcNow.AddSeconds(65); var result3 = chunkTreeCache.GetOrAdd(path, fileInfo => chunkTree2); // Assert 3 Assert.Same(chunkTree2, result3); } }
/// <summary> /// Instantiates a new <see cref="GeneratorResults"/> instance. /// </summary> /// <param name="document">The <see cref="Block"/> for the syntax tree.</param> /// <param name="tagHelperDescriptors"> /// The <see cref="TagHelperDescriptor"/>s that apply to the current Razor document. /// </param> /// <param name="errorSink"> /// The <see cref="ErrorSink"/> used to collect <see cref="RazorError"/>s encountered when parsing the /// current Razor document. /// </param> /// <param name="codeGeneratorResult">The results of generating code for the document.</param> /// <param name="chunkTree">A <see cref="ChunkTree"/> for the document.</param> public GeneratorResults(Block document, IEnumerable<TagHelperDescriptor> tagHelperDescriptors, ErrorSink errorSink, CodeGeneratorResult codeGeneratorResult, ChunkTree chunkTree) : base(document, tagHelperDescriptors, errorSink) { if (document == null) { throw new ArgumentNullException(nameof(document)); } if (tagHelperDescriptors == null) { throw new ArgumentNullException(nameof(tagHelperDescriptors)); } if (errorSink == null) { throw new ArgumentNullException(nameof(errorSink)); } if (codeGeneratorResult == null) { throw new ArgumentNullException(nameof(codeGeneratorResult)); } if (chunkTree == null) { throw new ArgumentNullException(nameof(chunkTree)); } GeneratedCode = codeGeneratorResult.Code; DesignTimeLineMappings = codeGeneratorResult.DesignTimeLineMappings; ChunkTree = chunkTree; }
public void GetOrAdd_UpdatesCacheWithValue_IfFileWasAdded() { // Arrange var path = @"Views\Home\_ViewStart.cshtml"; var fileProvider = new TestFileProvider(); using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider)) { var expected = new ChunkTree(); // Act 1 var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); // Assert 1 Assert.Null(result1); // Act 2 fileProvider.AddFile(path, "test content"); fileProvider.GetChangeToken(path).HasChanged = true; var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => expected); // Assert 2 Assert.Same(expected, result2); } }
public void GetTagHelperDescriptors_ReturnsExpectedDirectiveDescriptors( ChunkTree[] chunkTrees, TagHelperDirectiveDescriptor[] expectedDirectiveDescriptors) { // Arrange var builder = new BlockBuilder { Type = BlockType.Comment }; var block = new Block(builder); IList<TagHelperDirectiveDescriptor> descriptors = null; var resolver = new Mock<ITagHelperDescriptorResolver>(); resolver.Setup(r => r.Resolve(It.IsAny<TagHelperDescriptorResolutionContext>())) .Callback((TagHelperDescriptorResolutionContext context) => { descriptors = context.DirectiveDescriptors; }) .Returns(Enumerable.Empty<TagHelperDescriptor>()) .Verifiable(); var baseParser = new RazorParser( new CSharpCodeParser(), new HtmlMarkupParser(), tagHelperDescriptorResolver: resolver.Object); var parser = new TestableMvcRazorParser(baseParser, chunkTrees, defaultInheritedChunks: new Chunk[0]); // Act parser.GetTagHelperDescriptorsPublic(block, errorSink: new ErrorSink()).ToArray(); // Assert Assert.NotNull(descriptors); Assert.Equal(expectedDirectiveDescriptors.Length, descriptors.Count); for (var i = 0; i < expectedDirectiveDescriptors.Length; i++) { var expected = expectedDirectiveDescriptors[i]; var actual = descriptors[i]; Assert.Equal(expected.DirectiveText, actual.DirectiveText, StringComparer.Ordinal); Assert.Equal(SourceLocation.Zero, actual.Location); Assert.Equal(expected.DirectiveType, actual.DirectiveType); } }
public void Merge_MatchesNamespacesInCaseSensitiveManner() { // Arrange var merger = new UsingChunkMerger(); var chunkTree = new ChunkTree(); var inheritedChunks = new[] { new UsingChunk { Namespace = "Microsoft.AspNetCore.Mvc" }, new UsingChunk { Namespace = "Microsoft.AspNetCore.mvc" } }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Equal(2, chunkTree.Children.Count); var chunk = Assert.IsType<UsingChunk>(chunkTree.Children[0]); Assert.Equal("Microsoft.AspNetCore.Mvc", chunk.Namespace); chunk = Assert.IsType<UsingChunk>(chunkTree.Children[1]); Assert.Equal("Microsoft.AspNetCore.mvc", chunk.Namespace); }
private static IChunkMerger[] GetChunkMergers(ChunkTree chunkTree, string defaultModel) { var modelType = ChunkHelper.GetModelTypeName(chunkTree, defaultModel); return new IChunkMerger[] { new UsingChunkMerger(), new InjectChunkMerger(modelType), new SetBaseTypeChunkMerger(modelType) }; }
public void Merge_ResolvesModelNameInTypesWithTModelToken() { // Arrange var merger = new InjectChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new[] { new InjectChunk("MyHelper<TModel>", "MyProperty") }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert var chunk = Assert.Single(chunkTree.Children); var injectChunk = Assert.IsType<InjectChunk>(chunk); Assert.Equal("MyHelper<dynamic>", injectChunk.TypeName); Assert.Equal("MyProperty", injectChunk.MemberName); }
public void Merge_UsesTheLastInjectChunkOfAPropertyName() { // Arrange var merger = new InjectChunkMerger("dynamic"); var chunkTree = new ChunkTree(); var inheritedChunks = new Chunk[] { new LiteralChunk(), new InjectChunk("SomeOtherType", "Property"), new InjectChunk("DifferentPropertyType", "DifferentProperty"), new InjectChunk("SomeType", "Property"), }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert Assert.Collection(chunkTree.Children, chunk => { var injectChunk = Assert.IsType<InjectChunk>(chunk); Assert.Equal("SomeType", injectChunk.TypeName); Assert.Equal("Property", injectChunk.MemberName); }, chunk => { var injectChunk = Assert.IsType<InjectChunk>(chunk); Assert.Equal("DifferentPropertyType", injectChunk.TypeName); Assert.Equal("DifferentProperty", injectChunk.MemberName); }); }
public void GetOrAdd_ReturnsNullValues_IfFileDoesNotExistInFileProvider() { // Arrange var path = @"Views\_ViewStart.cshtml"; var mockFileProvider = new Mock<TestFileProvider> { CallBase = true }; var fileProvider = mockFileProvider.Object; using (var chunkTreeCache = new DefaultChunkTreeCache(fileProvider)) { var expected = new ChunkTree(); // Act var result1 = chunkTreeCache.GetOrAdd(path, fileInfo => expected); var result2 = chunkTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); // Assert Assert.Null(result1); Assert.Null(result2); mockFileProvider.Verify(f => f.GetFileInfo(It.IsAny<string>()), Times.Once()); } }
/// <summary> /// Merges <see cref="Chunk"/> inherited by default and <see cref="ChunkTree"/> instances produced by parsing /// <c>_ViewImports</c> files into the specified <paramref name="chunkTree"/>. /// </summary> /// <param name="chunkTree">The <see cref="ChunkTree"/> to merge in to.</param> /// <param name="inheritedChunkTrees"><see cref="IReadOnlyList{ChunkTree}"/> inherited from <c>_ViewImports</c> /// files.</param> /// <param name="defaultModel">The default model <see cref="Type"/> name.</param> public void MergeInheritedChunkTrees( ChunkTree chunkTree, IReadOnlyList<ChunkTree> inheritedChunkTrees, string defaultModel) { if (chunkTree == null) { throw new ArgumentNullException(nameof(chunkTree)); } if (inheritedChunkTrees == null) { throw new ArgumentNullException(nameof(inheritedChunkTrees)); } var chunkMergers = GetChunkMergers(chunkTree, defaultModel); // We merge chunks into the ChunkTree in two passes. In the first pass, we traverse the ChunkTree visiting // a mapped IChunkMerger for types that are registered. foreach (var chunk in chunkTree.Children) { foreach (var merger in chunkMergers) { merger.VisitChunk(chunk); } } var inheritedChunks = _defaultInheritedChunks.Concat( inheritedChunkTrees.SelectMany(tree => tree.Children)).ToArray(); foreach (var merger in chunkMergers) { merger.MergeInheritedChunks(chunkTree, inheritedChunks); } }
public void MergeInheritedChunks_MergesDefaultInheritedChunks() { // Arrange var fileProvider = new TestFileProvider(); fileProvider.AddFile(@"/Views/_ViewImports.cshtml", "@inject DifferentHelper<TModel> Html"); var cache = new DefaultChunkTreeCache(fileProvider); using (var host = new MvcRazorHost(cache, new TagHelperDescriptorResolver(designTime: false))) { var defaultChunks = new Chunk[] { new InjectChunk("MyTestHtmlHelper", "Html"), new UsingChunk { Namespace = "AppNamespace.Model" }, }; var inheritedChunkTrees = new ChunkTree[] { new ChunkTree { Children = new Chunk[] { new UsingChunk { Namespace = "InheritedNamespace" }, new LiteralChunk { Text = "some text" } } }, new ChunkTree { Children = new Chunk[] { new UsingChunk { Namespace = "AppNamespace.Model" }, } } }; var utility = new ChunkInheritanceUtility(host, cache, defaultChunks); var chunkTree = new ChunkTree(); // Act utility.MergeInheritedChunkTrees(chunkTree, inheritedChunkTrees, "dynamic"); // Assert Assert.Collection(chunkTree.Children, chunk => Assert.Same(defaultChunks[1], chunk), chunk => Assert.Same(inheritedChunkTrees[0].Children[0], chunk), chunk => Assert.Same(defaultChunks[0], chunk)); } }
public void Merge_ReplacesTModelTokensWithModel() { // Arrange var merger = new InjectChunkMerger("MyTestModel2"); var chunkTree = new ChunkTree(); var inheritedChunks = new[] { new InjectChunk("MyHelper<TModel>", "MyProperty") }; // Act merger.MergeInheritedChunks(chunkTree, inheritedChunks); // Assert var chunk = Assert.Single(chunkTree.Children); var injectChunk = Assert.IsType<InjectChunk>(chunk); Assert.Equal("MyHelper<MyTestModel2>", injectChunk.TypeName); Assert.Equal("MyProperty", injectChunk.MemberName); }