diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/IntegrationTests/CodeGenerationIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/IntegrationTests/CodeGenerationIntegrationTest.cs index 87ea8b68a4..b36e2dcad9 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/IntegrationTests/CodeGenerationIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/IntegrationTests/CodeGenerationIntegrationTest.cs @@ -1,61 +1,97 @@ // 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.Collections.Generic; -using System.IO; using System.Linq; -using System.Text.RegularExpressions; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.IntegrationTests; using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Razor; using Xunit; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.IntegrationTests { public class CodeGenerationIntegrationTest : IntegrationTestBase { - private static readonly RazorSourceDocument DefaultImports = MvcRazorTemplateEngine.GetDefaultImports(); + private readonly static CSharpCompilation DefaultBaseCompilation = MvcShim.BaseCompilation.WithAssemblyName("AppCode"); - private CSharpCompilation BaseCompilation => MvcShim.BaseCompilation.WithAssemblyName("AppCode"); + public CodeGenerationIntegrationTest() + : base(generateBaselines: null) + { + Configuration = RazorConfiguration.Create( + RazorLanguageVersion.Version_2_0, + "MVC-2.1", + new[] { new AssemblyExtension("MVC-2.1", typeof(ExtensionInitializer).Assembly) }); + } + + protected override CSharpCompilation BaseCompilation => DefaultBaseCompilation; + + protected override RazorConfiguration Configuration { get; } #region Runtime [Fact] public void UsingDirectives_Runtime() { - var compilation = BaseCompilation; + // Arrange + var projectItem = CreateProjectItemFromFile(); - RunRuntimeTest(compilation, new[] { "The using directive for 'System' appeared previously in this namespace" }); + // Act + var compiled = CompileToAssembly(projectItem, designTime: false, throwOnFailure: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + + var diagnostics = compiled.Compilation.GetDiagnostics().Where(d => d.Severity >= DiagnosticSeverity.Warning); + Assert.Equal("The using directive for 'System' appeared previously in this namespace", Assert.Single(diagnostics).GetMessage()); } [Fact] public void InvalidNamespaceAtEOF_Runtime() { - var compilation = BaseCompilation; - RunRuntimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToCSharp(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + + var diagnotics = compiled.CodeDocument.GetCSharpDocument().Diagnostics; + Assert.Equal("RZ1014", Assert.Single(diagnotics).Id); } [Fact] public void IncompleteDirectives_Runtime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyService { public string Html { get; set; } -}"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +}"); - RunRuntimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToCSharp(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + + // We expect this test to generate a bunch of errors. + Assert.True(compiled.CodeDocument.GetCSharpDocument().Diagnostics.Count > 0); } [Fact] public void InheritsViewModel_Runtime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Razor; @@ -70,16 +106,23 @@ public class MyModel { } -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunRuntimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void InheritsWithViewImports_Runtime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.RazorPages; @@ -94,71 +137,113 @@ public abstract class MyPageModel : Page public class MyModel { -}"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +}"); + AddProjectItemFromText(@"@inherits MyPageModel"); - RunRuntimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void MalformedPageDirective_Runtime() { - var compilation = BaseCompilation; + // Arrange + var projectItem = CreateProjectItemFromFile(); - RunRuntimeTest(compilation); + // Act + var compiled = CompileToCSharp(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + + var diagnotics = compiled.CodeDocument.GetCSharpDocument().Diagnostics; + Assert.Equal("RZ1016", Assert.Single(diagnotics).Id); } [Fact] public void Basic_Runtime() { - var compilation = BaseCompilation; + // Arrange + var projectItem = CreateProjectItemFromFile(); - RunRuntimeTest(compilation); + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void Sections_Runtime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" using Microsoft.AspNetCore.Mvc.ViewFeatures; public class InputTestTagHelper : {typeof(TagHelper).FullName} {{ public ModelExpression For {{ get; set; }} }} -"; +"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); + var projectItem = CreateProjectItemFromFile(); - RunRuntimeTest(compilation); + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void _ViewImports_Runtime() { - var compilation = BaseCompilation; + // Arrange + var projectItem = CreateProjectItemFromFile(); - RunRuntimeTest(compilation); + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void Inject_Runtime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyApp { public string MyProperty { get; set; } } -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunRuntimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void InjectWithModel_Runtime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyModel { @@ -172,16 +257,23 @@ public class MyService public class MyApp { public string MyProperty { get; set; } -}"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +}"); - RunRuntimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void InjectWithSemicolon_Runtime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyModel { @@ -196,90 +288,145 @@ public class MyService { public string Html { get; set; } } -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunRuntimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void Model_Runtime() { - var compilation = BaseCompilation; + // Arrange + var projectItem = CreateProjectItemFromFile(); - RunRuntimeTest(compilation); + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void ModelExpressionTagHelper_Runtime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" using Microsoft.AspNetCore.Mvc.ViewFeatures; public class InputTestTagHelper : {typeof(TagHelper).FullName} {{ public ModelExpression For {{ get; set; }} }} -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunRuntimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void RazorPages_Runtime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" public class DivTagHelper : {typeof(TagHelper).FullName} {{ }} -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunRuntimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void RazorPagesWithRouteTemplate_Runtime() { - var compilation = BaseCompilation; + // Arrange + var projectItem = CreateProjectItemFromFile(); - RunRuntimeTest(compilation); + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void RazorPagesWithoutModel_Runtime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" public class DivTagHelper : {typeof(TagHelper).FullName} {{ }} -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunRuntimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void PageWithNamespace_Runtime() { - var compilation = BaseCompilation; - RunRuntimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void ViewWithNamespace_Runtime() { - var compilation = BaseCompilation; - RunRuntimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void ViewComponentTagHelper_Runtime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" public class TestViewComponent {{ public string Invoke(string firstName) @@ -293,19 +440,33 @@ public class AllTagHelper : {typeof(TagHelper).FullName} {{ public string Bar {{ get; set; }} }} -"; +"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - - RunRuntimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); } [Fact] public void RazorPageWithNoLeadingPageDirective_Runtime() { - var compilation = BaseCompilation; + // Arrange + var projectItem = CreateProjectItemFromFile(); - RunRuntimeTest(compilation); + // Act + var compiled = CompileToCSharp(projectItem, designTime: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + + var diagnotics = compiled.CodeDocument.GetCSharpDocument().Diagnostics; + Assert.Equal("RZ3906", Assert.Single(diagnotics).Id); } #endregion @@ -314,37 +475,68 @@ public class AllTagHelper : {typeof(TagHelper).FullName} [Fact] public void UsingDirectives_DesignTime() { - var compilation = BaseCompilation; + // Arrange + var projectItem = CreateProjectItemFromFile(); - RunDesignTimeTest(compilation, new[] { "The using directive for 'System' appeared previously in this namespace" }); + // Act + var compiled = CompileToAssembly(projectItem, designTime: true, throwOnFailure: false); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); + + var diagnostics = compiled.Compilation.GetDiagnostics().Where(d => d.Severity >= DiagnosticSeverity.Warning); + Assert.Equal("The using directive for 'System' appeared previously in this namespace", Assert.Single(diagnostics).GetMessage()); } [Fact] public void InvalidNamespaceAtEOF_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToCSharp(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); + + var diagnotics = compiled.CodeDocument.GetCSharpDocument().Diagnostics; + Assert.Equal("RZ1014", Assert.Single(diagnotics).Id); } [Fact] public void IncompleteDirectives_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyService { public string Html { get; set; } -} -"; +}"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); + var projectItem = CreateProjectItemFromFile(); - RunDesignTimeTest(compilation); + // Act + var compiled = CompileToCSharp(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); + + // We expect this test to generate a bunch of errors. + Assert.True(compiled.CodeDocument.GetCSharpDocument().Diagnostics.Count > 0); } [Fact] public void InheritsViewModel_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Razor; @@ -355,29 +547,31 @@ public class MyBasePageForViews : RazorPage throw new System.NotImplementedException(); } } - public class MyModel { } -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunDesignTimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void InheritsWithViewImports_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.RazorPages; -public class MyModel -{ - -} - public abstract class MyPageModel : Page { public override Task ExecuteAsync() @@ -385,69 +579,124 @@ public abstract class MyPageModel : Page throw new System.NotImplementedException(); } } -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +public class MyModel +{ - RunDesignTimeTest(compilation); +}"); + + AddProjectItemFromText(@"@inherits MyPageModel"); + + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void MalformedPageDirective_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToCSharp(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); + + var diagnotics = compiled.CodeDocument.GetCSharpDocument().Diagnostics; + Assert.Equal("RZ1016", Assert.Single(diagnotics).Id); } [Fact] public void Basic_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void Sections_DesignTime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" using Microsoft.AspNetCore.Mvc.ViewFeatures; public class InputTestTagHelper : {typeof(TagHelper).FullName} {{ public ModelExpression For {{ get; set; }} }} -"; +"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); + var projectItem = CreateProjectItemFromFile(); - RunDesignTimeTest(compilation); + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void _ViewImports_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void Inject_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyApp { public string MyProperty { get; set; } } -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunDesignTimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void InjectWithModel_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyModel { @@ -461,129 +710,208 @@ public class MyService public class MyApp { public string MyProperty { get; set; } -} -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +}"); - RunDesignTimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void InjectWithSemicolon_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyModel { } -public class MyService -{ - public string Html { get; set; } -} - public class MyApp { public string MyProperty { get; set; } } -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - RunDesignTimeTest(compilation); +public class MyService +{ + public string Html { get; set; } +} +"); + + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void Model_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void MultipleModels_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class ThisShouldBeGenerated { -}"; +}"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); + var projectItem = CreateProjectItemFromFile(); - RunDesignTimeTest(compilation); + // Act + var compiled = CompileToCSharp(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); + + var diagnotics = compiled.CodeDocument.GetCSharpDocument().Diagnostics; + Assert.Equal("RZ2001", Assert.Single(diagnotics).Id); } [Fact] public void ModelExpressionTagHelper_DesignTime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" using Microsoft.AspNetCore.Mvc.ViewFeatures; public class InputTestTagHelper : {typeof(TagHelper).FullName} {{ public ModelExpression For {{ get; set; }} }} -"; +"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); + var projectItem = CreateProjectItemFromFile(); - RunDesignTimeTest(compilation); + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void RazorPages_DesignTime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" public class DivTagHelper : {typeof(TagHelper).FullName} {{ }} -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunDesignTimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void RazorPagesWithRouteTemplate_DesignTime() { - var compilation = BaseCompilation; + // Arrange + var projectItem = CreateProjectItemFromFile(); - RunDesignTimeTest(compilation); + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void RazorPagesWithoutModel_DesignTime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" public class DivTagHelper : {typeof(TagHelper).FullName} {{ }} -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunDesignTimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void PageWithNamespace_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void ViewWithNamespace_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void ViewComponentTagHelper_DesignTime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" public class TestViewComponent {{ public string Invoke(string firstName) @@ -597,153 +925,37 @@ public class AllTagHelper : {typeof(TagHelper).FullName} {{ public string Bar {{ get; set; }} }} -"; +"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - - RunDesignTimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void RazorPageWithNoLeadingPageDirective_DesignTime() { - var compilation = BaseCompilation; + // Arrange + var projectItem = CreateProjectItemFromFile(); - RunDesignTimeTest(compilation); + // Act + var compiled = CompileToCSharp(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); + + var diagnotics = compiled.CodeDocument.GetCSharpDocument().Diagnostics; + Assert.Equal("RZ3906", Assert.Single(diagnotics).Id); } + #endregion - - private void RunRuntimeTest( - CSharpCompilation baseCompilation, - IEnumerable expectedWarnings = null) - { - Assert.Empty(baseCompilation.GetDiagnostics()); - - // Arrange - var engine = CreateEngine(baseCompilation); - var projectItem = CreateProjectItem(); - - // Act - var document = engine.Process(projectItem); - - // Assert - AssertDocumentNodeMatchesBaseline(document.GetDocumentIntermediateNode()); - AssertCSharpDocumentMatchesBaseline(document.GetCSharpDocument()); - AssertDocumentCompiles(document, baseCompilation, expectedWarnings); - } - - private void RunDesignTimeTest( - CSharpCompilation baseCompilation, - IEnumerable expectedWarnings = null) - { - Assert.Empty(baseCompilation.GetDiagnostics()); - - // Arrange - var engine = CreateEngine(baseCompilation); - var projectItem = CreateProjectItem(); - - // Act - var document = engine.ProcessDesignTime(projectItem); - - // Assert - AssertDocumentNodeMatchesBaseline(document.GetDocumentIntermediateNode()); - AssertCSharpDocumentMatchesBaseline(document.GetCSharpDocument()); - AssertSourceMappingsMatchBaseline(document); - AssertDocumentCompiles(document, baseCompilation, expectedWarnings); - } - - private void AssertDocumentCompiles( - RazorCodeDocument document, - CSharpCompilation baseCompilation, - IEnumerable expectedWarnings = null) - { - var cSharp = document.GetCSharpDocument().GeneratedCode; - - var syntaxTree = CSharpSyntaxTree.ParseText(cSharp); - var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - - var references = baseCompilation.References.Concat(new[] { baseCompilation.ToMetadataReference() }); - var compilation = CSharpCompilation.Create("CodeGenerationTestAssembly", new[] { syntaxTree }, references, options); - - var diagnostics = compilation.GetDiagnostics(); - - var warnings = diagnostics.Where(d => d.Severity >= DiagnosticSeverity.Warning); - - if (expectedWarnings == null) - { - Assert.Empty(warnings); - } - else - { - Assert.Equal(expectedWarnings, warnings.Select(e => e.GetMessage())); - } - } - - protected RazorProjectEngine CreateEngine(CSharpCompilation compilation) - { - var references = compilation.References.Concat(new[] { compilation.ToMetadataReference() }); - - return CreateProjectEngine(b => - { - RazorExtensions.Register(b); - - var existingImportFeature = b.Features.OfType().Single(); - b.SetImportFeature(new NormalizedDefaultImportFeature(existingImportFeature)); - - b.Features.Add(GetMetadataReferenceFeature(references)); - b.Features.Add(new CompilationTagHelperFeature()); - }); - } - - private static IRazorEngineFeature GetMetadataReferenceFeature(IEnumerable references) - { - return new DefaultMetadataReferenceFeature() - { - References = references.ToList() - }; - } - - private class NormalizedDefaultImportFeature : RazorProjectEngineFeatureBase, IImportProjectFeature - { - private IImportProjectFeature _existingFeature; - - public NormalizedDefaultImportFeature(IImportProjectFeature existingFeature) - { - _existingFeature = existingFeature; - } - - protected override void OnInitialized() - { - _existingFeature.ProjectEngine = ProjectEngine; - } - - public IReadOnlyList GetImports(RazorProjectItem projectItem) - { - var normalizedImports = new List(); - var imports = _existingFeature.GetImports(projectItem); - foreach (var import in imports) - { - var text = string.Empty; - using (var stream = import.Read()) - using (var reader = new StreamReader(stream)) - { - text = reader.ReadToEnd().Trim(); - } - - // It's important that we normalize the newlines in the default imports. The default imports will - // be created with Environment.NewLine, but we need to normalize to `\r\n` so that the indices - // are the same on xplat. - var normalizedText = Regex.Replace(text, "(? - DesignTimeDirective - - DirectiveToken - (10:0,10 [19] InheritsWithViewImports_Imports0.cshtml) - MyPageModel DirectiveToken - (231:7,8 [62] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper DirectiveToken - (294:7,71 [4] ) - Html DirectiveToken - (308:8,8 [54] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper @@ -24,6 +23,7 @@ Document - DirectiveToken - (617:12,14 [96] ) - Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor DirectiveToken - (729:13,14 [87] ) - Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor DirectiveToken - (832:14,14 [87] ) - Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor + DirectiveToken - (10:0,10 [19] TestFiles\IntegrationTests\CodeGenerationIntegrationTest\_ViewImports.cshtml) - MyPageModel DirectiveToken - (14:1,7 [7] InheritsWithViewImports.cshtml) - MyModel CSharpCode - IntermediateToken - - CSharp - #pragma warning disable 0414 diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Imports0.cshtml b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Imports0.cshtml deleted file mode 100644 index fdc08de01d..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Imports0.cshtml +++ /dev/null @@ -1 +0,0 @@ -@inherits MyPageModel diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Runtime.codegen.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Runtime.codegen.cs index 00a6cb3a1d..c1ea0b5c3e 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Runtime.codegen.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Runtime.codegen.cs @@ -14,7 +14,7 @@ namespace AspNetCore using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; [global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"d196fc1c66d46d35e35af9b01c737e12bcce6782", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports.cshtml")] - [global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"30a9e14edcefbf4970c45de29d8870cf1a9b90b5", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Imports0.cshtml")] + [global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"f311ecbb5c4d63980a59c24af5ffe8baa1c3f99a", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/_ViewImports.cshtml")] public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_InheritsWithViewImports : MyPageModel { #pragma warning disable 1998 diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/IntegrationTests/CodeGenerationIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/IntegrationTests/CodeGenerationIntegrationTest.cs index 27b93fd264..bb605fed72 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/IntegrationTests/CodeGenerationIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/IntegrationTests/CodeGenerationIntegrationTest.cs @@ -1,55 +1,78 @@ // 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.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.IntegrationTests; using Microsoft.AspNetCore.Razor.TagHelpers; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Emit; -using Microsoft.CodeAnalysis.Razor; -using Microsoft.Extensions.DependencyModel; using Xunit; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.IntegrationTests { public class CodeGenerationIntegrationTest : IntegrationTestBase { - private static readonly RazorSourceDocument DefaultImports = MvcRazorTemplateEngine.GetDefaultImports(); + private readonly static CSharpCompilation DefaultBaseCompilation = MvcShim.BaseCompilation.WithAssemblyName("AppCode"); - private CSharpCompilation BaseCompilation => MvcShim.BaseCompilation.WithAssemblyName("AppCode"); + public CodeGenerationIntegrationTest() + : base(generateBaselines: null) + { + Configuration = RazorConfiguration.Create( + RazorLanguageVersion.Version_1_1, + "MVC-1.1", + new[] { new AssemblyExtension("MVC-1.1", typeof(ExtensionInitializer).Assembly) }); + } + + protected override CSharpCompilation BaseCompilation => DefaultBaseCompilation; + + protected override RazorConfiguration Configuration { get; } [Fact] public void InvalidNamespaceAtEOF_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToCSharp(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); + + var diagnotics = compiled.CodeDocument.GetCSharpDocument().Diagnostics; + Assert.Equal("RZ1007", Assert.Single(diagnotics).Id); } [Fact] public void IncompleteDirectives_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyService { public string Html { get; set; } -} -"; +}"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - RunDesignTimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToCSharp(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); + + // We expect this test to generate a bunch of errors. + Assert.True(compiled.CodeDocument.GetCSharpDocument().Diagnostics.Count > 0); } [Fact] public void InheritsViewModel_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Razor; @@ -60,20 +83,28 @@ public class MyBasePageForViews : RazorPage throw new System.NotImplementedException(); } } - public class MyModel { } -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - RunDesignTimeTest(compilation); +"); + + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void InheritsWithViewImports_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Razor; @@ -88,61 +119,102 @@ public class MyBasePageForViews : RazorPage public class MyModel { -} -"; +}"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - RunDesignTimeTest(compilation); + AddProjectItemFromText(@"@inherits MyBasePageForViews"); + + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void Basic_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void Sections_DesignTime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" using Microsoft.AspNetCore.Mvc.ViewFeatures; public class InputTestTagHelper : {typeof(TagHelper).FullName} {{ public ModelExpression For {{ get; set; }} }} -"; +"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); + var projectItem = CreateProjectItemFromFile(); - RunDesignTimeTest(compilation); + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void _ViewImports_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void Inject_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyApp { public string MyProperty { get; set; } } -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - RunDesignTimeTest(compilation); +"); + + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void InjectWithModel_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyModel { @@ -156,75 +228,119 @@ public class MyService public class MyApp { public string MyProperty { get; set; } -} -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - RunDesignTimeTest(compilation); +}"); + + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void InjectWithSemicolon_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class MyModel { } -public class MyService -{ - public string Html { get; set; } -} - public class MyApp { public string MyProperty { get; set; } } -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - RunDesignTimeTest(compilation); + +public class MyService +{ + public string Html { get; set; } +} +"); + + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void Model_DesignTime() { - var compilation = BaseCompilation; - RunDesignTimeTest(compilation); + // Arrange + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void MultipleModels_DesignTime() { - var appCode = @" + // Arrange + AddCSharpSyntaxTree(@" public class ThisShouldBeGenerated { -}"; +}"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - RunDesignTimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToCSharp(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); + + var diagnotics = compiled.CodeDocument.GetCSharpDocument().Diagnostics; + Assert.Equal("RZ2001", Assert.Single(diagnotics).Id); } [Fact] public void ModelExpressionTagHelper_DesignTime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" using Microsoft.AspNetCore.Mvc.ViewFeatures; public class InputTestTagHelper : {typeof(TagHelper).FullName} {{ public ModelExpression For {{ get; set; }} }} -"; - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); +"); - RunDesignTimeTest(compilation); + var projectItem = CreateProjectItemFromFile(); + + // Act + var compiled = CompileToAssembly(projectItem, designTime: true); + + // Assert + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); + AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument()); + AssertSourceMappingsMatchBaseline(compiled.CodeDocument); } [Fact] public void ViewComponentTagHelper_DesignTime() { - var appCode = $@" + // Arrange + AddCSharpSyntaxTree($@" public class TestViewComponent {{ public string Invoke(string firstName) @@ -238,126 +354,17 @@ public class AllTagHelper : {typeof(TagHelper).FullName} {{ public string Bar {{ get; set; }} }} -"; +"); - var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode)); - - RunDesignTimeTest(compilation); - } - - private void RunDesignTimeTest( - CSharpCompilation baseCompilation, - IEnumerable expectedErrors = null) - { - Assert.Empty(baseCompilation.GetDiagnostics()); - - // Arrange - var engine = CreateEngine(baseCompilation); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act - var document = engine.ProcessDesignTime(projectItem); + var compiled = CompileToAssembly(projectItem, designTime: true); // Assert - AssertDocumentNodeMatchesBaseline(document.GetDocumentIntermediateNode()); - AssertCSharpDocumentMatchesBaseline(document.GetCSharpDocument()); - AssertSourceMappingsMatchBaseline(document); - AssertDocumentCompiles(document, baseCompilation, expectedErrors); - } - - private void AssertDocumentCompiles( - RazorCodeDocument document, - CSharpCompilation baseCompilation, - IEnumerable expectedErrors = null) - { - var cSharp = document.GetCSharpDocument().GeneratedCode; - - var syntaxTree = CSharpSyntaxTree.ParseText(cSharp); - var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - - var references = baseCompilation.References.Concat(new[] { baseCompilation.ToMetadataReference() }); - var compilation = CSharpCompilation.Create("CodeGenerationTestAssembly", new[] { syntaxTree }, references, options); - - var diagnostics = compilation.GetDiagnostics(); - - var errors = diagnostics.Where(d => d.Severity >= DiagnosticSeverity.Warning); - - if (expectedErrors == null) - { - Assert.Empty(errors.Select(e => e.GetMessage())); - } - else - { - Assert.Equal(expectedErrors, errors.Select(e => e.GetMessage())); - } - } - - protected RazorProjectEngine CreateEngine(CSharpCompilation compilation) - { - var references = compilation.References.Concat(new[] { compilation.ToMetadataReference() }); - - return CreateProjectEngine(b => - { - RazorExtensions.Register(b); - RazorExtensions.RegisterViewComponentTagHelpers(b); - - var existingImportFeature = b.Features.OfType().Single(); - b.SetImportFeature(new NormalizedDefaultImportFeature(existingImportFeature)); - - b.Features.Add(GetMetadataReferenceFeature(references)); - b.Features.Add(new CompilationTagHelperFeature()); - }); - } - - private static IRazorEngineFeature GetMetadataReferenceFeature(IEnumerable references) - { - return new DefaultMetadataReferenceFeature() - { - References = references.ToList() - }; - } - - private class NormalizedDefaultImportFeature : RazorProjectEngineFeatureBase, IImportProjectFeature - { - private IImportProjectFeature _existingFeature; - - public NormalizedDefaultImportFeature(IImportProjectFeature existingFeature) - { - _existingFeature = existingFeature; - } - - protected override void OnInitialized() - { - _existingFeature.ProjectEngine = ProjectEngine; - } - - public IReadOnlyList GetImports(RazorProjectItem projectItem) - { - var normalizedImports = new List(); - var imports = _existingFeature.GetImports(projectItem); - foreach (var import in imports) - { - var text = string.Empty; - using (var stream = import.Read()) - using (var reader = new StreamReader(stream)) - { - text = reader.ReadToEnd().Trim(); - } - - // It's important that we normalize the newlines in the default imports. The default imports will - // be created with Environment.NewLine, but we need to normalize to `\r\n` so that the indices - // are the same on xplat. - var normalizedText = Regex.Replace(text, "(? - DesignTimeDirective - - DirectiveToken - (10:0,10 [26] InheritsWithViewImports_Imports0.cshtml) - MyBasePageForViews DirectiveToken - (231:7,8 [62] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper DirectiveToken - (294:7,71 [4] ) - Html DirectiveToken - (308:8,8 [54] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper @@ -22,6 +21,7 @@ Document - DirectiveToken - (507:11,8 [70] ) - global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider DirectiveToken - (578:11,79 [23] ) - ModelExpressionProvider DirectiveToken - (617:12,14 [96] ) - Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor + DirectiveToken - (10:0,10 [26] TestFiles\IntegrationTests\CodeGenerationIntegrationTest\_ViewImports.cshtml) - MyBasePageForViews DirectiveToken - (7:0,7 [7] InheritsWithViewImports.cshtml) - MyModel CSharpCode - IntermediateToken - - CSharp - #pragma warning disable 0414 diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Imports0.cshtml b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Imports0.cshtml deleted file mode 100644 index 4c14f15bad..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/InheritsWithViewImports_Imports0.cshtml +++ /dev/null @@ -1 +0,0 @@ -@inherits MyBasePageForViews diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/BasicIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/BasicIntegrationTest.cs index 4f9facbdfd..9ae793d5a1 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/BasicIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/BasicIntegrationTest.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { // Arrange var projectEngine = CreateProjectEngine(); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act var codeDocument = projectEngine.Process(projectItem); @@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { // Arrange var projectEngine = CreateProjectEngine(); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act var codeDocument = projectEngine.Process(projectItem); @@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests b.AddDirective(DirectiveDescriptor.CreateDirective("test", DirectiveKind.SingleLine)); }); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act var codeDocument = projectEngine.Process(projectItem); @@ -53,65 +53,5 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests // Assert AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode()); } - - [Fact] - public void BuildEngine_CallProcess() - { - // Arrange - var projectEngine = CreateProjectEngine(); - var projectItem = new TestRazorProjectItem("Index.cshtml"); - - // Act - var codeDocument = projectEngine.Process(projectItem); - - // Assert - Assert.NotNull(codeDocument.GetSyntaxTree()); - Assert.NotNull(codeDocument.GetDocumentIntermediateNode()); - } - - [Fact] - public void CSharpDocument_Runtime_PreservesParserErrors() - { - // Arrange - var projectEngine = CreateProjectEngine(); - var projectItem = new TestRazorProjectItem("test.cshtml") - { - Content = "@!!!" - }; - - var expected = RazorDiagnosticFactory.CreateParsing_UnexpectedCharacterAtStartOfCodeBlock( - new SourceSpan(new SourceLocation("test.cshtml", 1, 0, 1), contentLength: 1), - "!"); - - // Act - var codeDocument = projectEngine.Process(projectItem); - - // Assert - var csharpDocument = codeDocument.GetCSharpDocument(); - var error = Assert.Single(csharpDocument.Diagnostics); - Assert.Equal(expected, error); - } - - [Fact] - public void CSharpDocument_DesignTime_PreservesParserErrors() - { - // Arrange - var projectEngine = CreateProjectEngine(); - var projectItem = new TestRazorProjectItem("test.cshtml") - { - Content = "@{" - }; - - var expected = RazorDiagnosticFactory.CreateParsing_ExpectedEndOfBlockBeforeEOF( - new SourceSpan(new SourceLocation("test.cshtml", 1, 0, 1), contentLength: 1), Resources.BlockName_Code, "}", "{"); - - // Act - var codeDocument = projectEngine.ProcessDesignTime(projectItem); - - // Assert - var csharpDocument = codeDocument.GetCSharpDocument(); - var error = Assert.Single(csharpDocument.Diagnostics); - Assert.Equal(expected, error); - } } } diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/CodeGenerationIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/CodeGenerationIntegrationTest.cs index 34ddc3fb94..6ead22dfae 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/CodeGenerationIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/CodeGenerationIntegrationTest.cs @@ -10,6 +10,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { public class CodeGenerationIntegrationTest : IntegrationTestBase { + #region Runtime [Fact] public void IncompleteDirectives_Runtime() @@ -23,12 +24,6 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests RunTimeTest(); } - [Fact] - public void BasicImports_Runtime() - { - RunTimeTest(); - } - [Fact] public void UnfinishedExpressionInCode_Runtime() { @@ -460,12 +455,6 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests DesignTimeTest(); } - [Fact] - public void BasicImports_DesignTime() - { - DesignTimeTest(); - } - [Fact] public void UnfinishedExpressionInCode_DesignTime() { @@ -896,7 +885,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests SectionDirective.Register(builder); }); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act var codeDocument = projectEngine.ProcessDesignTime(projectItem); @@ -922,7 +911,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests SectionDirective.Register(builder); }); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act var codeDocument = projectEngine.Process(projectItem); @@ -947,7 +936,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests SectionDirective.Register(builder); }); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); var imports = GetImports(projectEngine, projectItem); // Act @@ -973,7 +962,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests SectionDirective.Register(builder); }); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); var imports = GetImports(projectEngine, projectItem); // Act diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/ExtensibleDirectiveTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/ExtensibleDirectiveTest.cs index 9e722b53b9..e26ce3b6fd 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/ExtensibleDirectiveTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/ExtensibleDirectiveTest.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests builder.AddDirective(DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddNamespaceToken())); }); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act var codeDocument = engine.ProcessDesignTime(projectItem); diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/HtmlAttributeIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/HtmlAttributeIntegrationTest.cs index 7683ca94ae..7fd26ffd80 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/HtmlAttributeIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/HtmlAttributeIntegrationTest.cs @@ -11,28 +11,26 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests public void HtmlWithDataDashAttribute() { // Arrange - var projectEngine = CreateProjectEngine(); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act - var codeDocument = projectEngine.Process(projectItem); + var compiled = CompileToCSharp(projectItem); // Assert - AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode()); + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); } [Fact] public void HtmlWithConditionalAttribute() { // Arrange - var projectEngine = CreateProjectEngine(); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act - var codeDocument = projectEngine.Process(projectItem); + var compiled = CompileToCSharp(projectItem); // Assert - AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode()); + AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode()); } } } diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TagHelpersIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TagHelpersIntegrationTest.cs index 7d7ce390ec..c8cd0e4146 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TagHelpersIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TagHelpersIntegrationTest.cs @@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests }; var projectEngine = CreateProjectEngine(builder => builder.AddTagHelpers(descriptors)); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act var codeDocument = projectEngine.Process(projectItem); @@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests }; var projectEngine = CreateProjectEngine(builder => builder.AddTagHelpers(descriptors)); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act var codeDocument = projectEngine.Process(projectItem); @@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests }; var projectEngine = CreateProjectEngine(builder => builder.AddTagHelpers(descriptors)); - var projectItem = CreateProjectItem(); + var projectItem = CreateProjectItemFromFile(); // Act var codeDocument = projectEngine.Process(projectItem); diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports.cshtml b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports.cshtml deleted file mode 100644 index bf4d925aea..0000000000 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports.cshtml +++ /dev/null @@ -1 +0,0 @@ -

Hi there!

diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_DesignTime.codegen.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_DesignTime.codegen.cs deleted file mode 100644 index b6226ce38a..0000000000 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_DesignTime.codegen.cs +++ /dev/null @@ -1,37 +0,0 @@ -// -#pragma warning disable 1591 -namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles -{ - #line hidden -#line 1 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports0.cshtml" -using System.Globalization; - -#line default -#line hidden -#line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports0.cshtml" -using System.ComponentModel; - -#line default -#line hidden -#line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports1.cshtml" -using System.Text; - -#line default -#line hidden - public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_BasicImports_DesignTime : Hello - { - #pragma warning disable 219 - private void __RazorDirectiveTokenHelpers__() { - } - #pragma warning restore 219 - #pragma warning disable 0414 - private static System.Object __o = null; - #pragma warning restore 0414 - #pragma warning disable 1998 - public async System.Threading.Tasks.Task ExecuteAsync() - { - } - #pragma warning restore 1998 - } -} -#pragma warning restore 1591 diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_DesignTime.ir.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_DesignTime.ir.txt deleted file mode 100644 index fab53bac03..0000000000 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_DesignTime.ir.txt +++ /dev/null @@ -1,20 +0,0 @@ -Document - - NamespaceDeclaration - - Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles - UsingDirective - (1:0,1 [26] BasicImports_Imports0.cshtml) - System.Globalization - UsingDirective - (30:1,1 [27] BasicImports_Imports0.cshtml) - System.ComponentModel - UsingDirective - (23:1,1 [18] BasicImports_Imports1.cshtml) - System.Text - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_BasicImports_DesignTime - Hello - - DesignTimeDirective - - DirectiveToken - (69:2,10 [5] BasicImports_Imports0.cshtml) - Hello - CSharpCode - - IntermediateToken - - CSharp - #pragma warning disable 0414 - CSharpCode - - IntermediateToken - - CSharp - private static System.Object __o = null; - CSharpCode - - IntermediateToken - - CSharp - #pragma warning restore 0414 - MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync - HtmlContent - (0:0,0 [18] BasicImports.cshtml) - IntermediateToken - (0:0,0 [3] BasicImports.cshtml) - Html -

- IntermediateToken - (3:0,3 [9] BasicImports.cshtml) - Html - Hi there! - IntermediateToken - (12:0,12 [4] BasicImports.cshtml) - Html -

- IntermediateToken - (16:0,16 [2] BasicImports.cshtml) - Html - \n diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_DesignTime.mappings.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_DesignTime.mappings.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports0.cshtml b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports0.cshtml deleted file mode 100644 index b71f569138..0000000000 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports0.cshtml +++ /dev/null @@ -1,5 +0,0 @@ -@using System.Globalization -@using System.ComponentModel -@inherits Hello -@("And also this") -

This will get ignored

diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports1.cshtml b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports1.cshtml deleted file mode 100644 index bac4a38695..0000000000 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports1.cshtml +++ /dev/null @@ -1,2 +0,0 @@ -@section ignored { } -@using System.Text; diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Runtime.codegen.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Runtime.codegen.cs deleted file mode 100644 index ed71849611..0000000000 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Runtime.codegen.cs +++ /dev/null @@ -1,36 +0,0 @@ -#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "d5e5d28c0c504a7b0c5207a5230a5b7327ce5e09" -// -#pragma warning disable 1591 -[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_BasicImports_Runtime), @"default", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports.cshtml")] -namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles -{ - #line hidden -#line 1 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports0.cshtml" -using System.Globalization; - -#line default -#line hidden -#line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports0.cshtml" -using System.ComponentModel; - -#line default -#line hidden -#line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports1.cshtml" -using System.Text; - -#line default -#line hidden - [global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"d5e5d28c0c504a7b0c5207a5230a5b7327ce5e09", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports.cshtml")] - [global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"39c22940acc420eb6d46b6ff85ef6d5a2ab35fa1", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports0.cshtml")] - [global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"ede697b6fb90aac312d589ae96a93289cdeb506f", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Imports1.cshtml")] - public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_BasicImports_Runtime : Hello - { - #pragma warning disable 1998 - public async System.Threading.Tasks.Task ExecuteAsync() - { - WriteLiteral("

Hi there!

\r\n"); - } - #pragma warning restore 1998 - } -} -#pragma warning restore 1591 diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Runtime.ir.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Runtime.ir.txt deleted file mode 100644 index 014ca50451..0000000000 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports_Runtime.ir.txt +++ /dev/null @@ -1,16 +0,0 @@ -Document - - RazorCompiledItemAttribute - - NamespaceDeclaration - - Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles - UsingDirective - (1:0,1 [28] BasicImports_Imports0.cshtml) - System.Globalization - UsingDirective - (30:1,1 [29] BasicImports_Imports0.cshtml) - System.ComponentModel - UsingDirective - (23:1,1 [20] BasicImports_Imports1.cshtml) - System.Text - RazorSourceChecksumAttribute - - RazorSourceChecksumAttribute - - RazorSourceChecksumAttribute - - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_BasicImports_Runtime - Hello - - MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync - HtmlContent - (0:0,0 [18] BasicImports.cshtml) - IntermediateToken - (0:0,0 [3] BasicImports.cshtml) - Html -

- IntermediateToken - (3:0,3 [9] BasicImports.cshtml) - Html - Hi there! - IntermediateToken - (12:0,12 [4] BasicImports.cshtml) - Html -

- IntermediateToken - (16:0,16 [2] BasicImports.cshtml) - Html - \n diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/CompilationFailedException.cs b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/CompilationFailedException.cs new file mode 100644 index 0000000000..febd3f69c6 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/CompilationFailedException.cs @@ -0,0 +1,58 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests +{ + public class CompilationFailedException : XunitException + { + public CompilationFailedException(Compilation compilation, Diagnostic[] diagnostics) + { + Compilation = compilation; + Diagnostics = diagnostics; + } + + public Compilation Compilation { get; } + + public Diagnostic[] Diagnostics { get; } + + public override string Message + { + get + { + var builder = new StringBuilder(); + builder.AppendLine("Compilation failed: "); + + var syntaxTreesWithErrors = new HashSet(); + foreach (var diagnostic in Diagnostics) + { + builder.AppendLine(diagnostic.ToString()); + + if (diagnostic.Location.IsInSource) + { + syntaxTreesWithErrors.Add(diagnostic.Location.SourceTree); + } + } + + if (syntaxTreesWithErrors.Any()) + { + builder.AppendLine(); + builder.AppendLine(); + + foreach (var syntaxTree in syntaxTreesWithErrors) + { + builder.AppendLine($"File {syntaxTree.FilePath ?? "unknown"}:"); + builder.AppendLine(syntaxTree.GetText().ToString()); + } + } + + return builder.ToString(); + } + } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/CompiledAssembly.cs b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/CompiledAssembly.cs new file mode 100644 index 0000000000..4024de0f49 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/CompiledAssembly.cs @@ -0,0 +1,24 @@ +// 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.Reflection; +using Microsoft.CodeAnalysis; + +namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests +{ + public class CompiledAssembly + { + public CompiledAssembly(Compilation compilation, RazorCodeDocument codeDocument, Assembly assembly) + { + Compilation = compilation; + CodeDocument = codeDocument; + Assembly = assembly; + } + + public Assembly Assembly { get; } + + public RazorCodeDocument CodeDocument { get; } + + public Compilation Compilation { get; } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/CompiledCSharpCode.cs b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/CompiledCSharpCode.cs new file mode 100644 index 0000000000..9d10341428 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/CompiledCSharpCode.cs @@ -0,0 +1,21 @@ +// 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 Microsoft.CodeAnalysis; + +namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests +{ + public class CompiledCSharpCode + { + public CompiledCSharpCode(Compilation baseCompilation, RazorCodeDocument codeDocument) + { + BaseCompilation = baseCompilation; + CodeDocument = codeDocument; + } + + // A compilation that can be used *with* this code to compile an assembly + public Compilation BaseCompilation { get; set; } + + public RazorCodeDocument CodeDocument { get; set; } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntegrationTestBase.cs b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntegrationTestBase.cs index c8c41c1f77..ffd7b849bb 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntegrationTestBase.cs +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntegrationTestBase.cs @@ -18,6 +18,9 @@ using Microsoft.AspNetCore.Razor.Language.CodeGeneration; using Microsoft.AspNetCore.Razor.Language.Intermediate; using Xunit; using Xunit.Sdk; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { @@ -28,15 +31,81 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests private static readonly AsyncLocal _fileName = new AsyncLocal(); #endif - protected IntegrationTestBase() + private static readonly CSharpCompilation DefaultBaseCompilation; + + static IntegrationTestBase() { - TestProjectRoot = TestProject.GetProjectDirectory(GetType()); + var referenceAssemblyRoots = new[] + { + typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly, // System.Runtime + }; + + var referenceAssemblies = referenceAssemblyRoots + .SelectMany(assembly => assembly.GetReferencedAssemblies().Concat(new[] { assembly.GetName() })) + .Distinct() + .Select(Assembly.Load) + .Select(assembly => MetadataReference.CreateFromFile(assembly.Location)) + .ToList(); + DefaultBaseCompilation = CSharpCompilation.Create( + "TestAssembly", + Array.Empty(), + referenceAssemblies, + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); } + protected IntegrationTestBase(bool? generateBaselines = null) + { + TestProjectRoot = TestProject.GetProjectDirectory(GetType()); + + if (generateBaselines.HasValue) + { + GenerateBaselines = generateBaselines.Value; + } + } + + /// + /// Gets the that will be used as the 'app' compilation. + /// + protected virtual CSharpCompilation BaseCompilation => DefaultBaseCompilation; + + /// + /// Gets the parse options applied when using . + /// + protected virtual CSharpParseOptions CSharpParseOptions { get; } = new CSharpParseOptions(LanguageVersion.Latest); + + /// + /// Gets the compilation options applied when compiling assemblies. + /// + protected virtual CSharpCompilationOptions CSharpCompilationOptions { get; } = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + + /// + /// Gets a list of CSharp syntax trees used that are considered part of the 'app'. + /// + protected virtual List CSharpSyntaxTrees { get; } = new List(); + + /// + /// Gets the that will be used for code generation. + /// + protected virtual RazorConfiguration Configuration { get; } = RazorConfiguration.Default; + + protected virtual bool DesignTime { get; } = false; + + /// + /// Gets the + /// + internal VirtualRazorProjectFileSystem FileSystem { get; } = new VirtualRazorProjectFileSystem(); + + /// + /// Used to force a specific style of line-endings for testing. This matters for the baseline tests that exercise line mappings. + /// Even though we normalize newlines for testing, the difference between platforms affects the data through the *count* of + /// characters written. + /// + protected virtual string LineEnding { get; } = "\r\n"; + #if GENERATE_BASELINES - protected bool GenerateBaselines { get; set; } = true; + protected bool GenerateBaselines { get; } = true; #else - protected bool GenerateBaselines { get; set; } = false; + protected bool GenerateBaselines { get; } = false; #endif protected string TestProjectRoot { get; } @@ -60,34 +129,62 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests #endif } - protected virtual RazorProjectEngine CreateProjectEngine() => CreateProjectEngine(configure: null); - - protected virtual RazorProjectEngine CreateProjectEngine(Action configure) + protected virtual void ConfigureProjectEngine(RazorProjectEngineBuilder builder) { - if (FileName == null) - { - var message = $"{nameof(CreateProjectEngine)} should only be called from an integration test, ({nameof(FileName)} is null)."; - throw new InvalidOperationException(message); - } - - var assembly = GetType().GetTypeInfo().Assembly; - var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, IntegrationTestFileSystem.Default, b => - { - configure?.Invoke(b); - - var existingImportFeature = b.Features.OfType().Single(); - b.SetImportFeature(new IntegrationTestImportFeature(assembly, existingImportFeature)); - }); - var testProjectEngine = new IntegrationTestProjectEngine(projectEngine); - - return testProjectEngine; } - protected virtual RazorProjectItem CreateProjectItem() + protected CSharpSyntaxTree AddCSharpSyntaxTree(string text, string filePath = null) + { + var syntaxTree = (CSharpSyntaxTree)CSharpSyntaxTree.ParseText(text, CSharpParseOptions, path: filePath); + CSharpSyntaxTrees.Add(syntaxTree); + return syntaxTree; + } + + protected RazorProjectItem AddProjectItemFromText(string text, string filePath = "_ViewImports.cshtml") + { + var projectItem = CreateProjectItemFromText(text, filePath); + FileSystem.Add(projectItem); + return projectItem; + } + + private RazorProjectItem CreateProjectItemFromText(string text, string filePath) + { + // Consider the file path to be relative to the 'FileName' of the test. + var workingDirectory = Path.GetDirectoryName(FileName); + + // Since these paths are used in baselines, we normalize them to windows style. We + // use "" as the base path by convention to avoid baking in an actual file system + // path. + var basePath = ""; + var physicalPath = Path.Combine(workingDirectory, filePath).Replace('/', '\\'); + var relativePhysicalPath = physicalPath; + + // FilePaths in Razor are **always** are of the form '/a/b/c.cshtml' + filePath = physicalPath.Replace('\\', '/'); + if (!filePath.StartsWith("/")) + { + filePath = '/' + filePath; + } + + text = NormalizeNewLines(text); + + var projectItem = new TestRazorProjectItem( + basePath: basePath, + filePath: filePath, + physicalPath: physicalPath, + relativePhysicalPath: relativePhysicalPath) + { + Content = text, + }; + + return projectItem; + } + + protected RazorProjectItem CreateProjectItemFromFile(string filePath = null) { if (FileName == null) { - var message = $"{nameof(CreateProjectItem)} should only be called from an integration test, ({nameof(FileName)} is null)."; + var message = $"{nameof(CreateProjectItemFromFile)} should only be called from an integration test, ({nameof(FileName)} is null)."; throw new InvalidOperationException(message); } @@ -102,12 +199,149 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests var fileContent = testFile.ReadAllText(); var normalizedContent = NormalizeNewLines(fileContent); - var projectItem = new TestRazorProjectItem(sourceFileName) + var workingDirectory = Path.GetDirectoryName(FileName); + var fullPath = sourceFileName; + + // Normalize to forward-slash - these strings end up in the baselines. + fullPath = fullPath.Replace('\\', '/'); + sourceFileName = sourceFileName.Replace('\\', '/'); + + // FilePaths in Razor are **always** are of the form '/a/b/c.cshtml' + filePath = filePath ?? sourceFileName; + if (!filePath.StartsWith("/")) { - Content = normalizedContent, + filePath = '/' + filePath; + } + + var projectItem = new TestRazorProjectItem( + basePath: workingDirectory, + filePath: filePath, + physicalPath: fullPath, + relativePhysicalPath: sourceFileName) + { + Content = fileContent, + }; + + return projectItem; + } + + protected CompiledCSharpCode CompileToCSharp(string text, string path = "test.cshtml", bool? designTime = null) + { + var projectItem = CreateProjectItemFromText(text, path); + return CompileToCSharp(projectItem, designTime); + } + + protected CompiledCSharpCode CompileToCSharp(RazorProjectItem projectItem, bool? designTime = null) + { + var compilation = CreateCompilation(); + var references = compilation.References.Concat(new[] { compilation.ToMetadataReference(), }).ToArray(); + + var projectEngine = CreateProjectEngine(Configuration, references, ConfigureProjectEngine); + var codeDocument = (designTime ?? DesignTime) ? projectEngine.ProcessDesignTime(projectItem) : projectEngine.Process(projectItem); + + return new CompiledCSharpCode(CSharpCompilation.Create(compilation.AssemblyName + ".Views", references: references, options: CSharpCompilationOptions), codeDocument); + } + + protected CompiledAssembly CompileToAssembly(string text, string path = "test.cshtml", bool? designTime = null, bool throwOnFailure = true) + { + var compiled = CompileToCSharp(text, path, designTime); + return CompileToAssembly(compiled); + } + + protected CompiledAssembly CompileToAssembly(RazorProjectItem projectItem, bool? designTime = null, bool throwOnFailure = true) + { + var compiled = CompileToCSharp(projectItem, designTime); + return CompileToAssembly(compiled, throwOnFailure: throwOnFailure); + } + + protected CompiledAssembly CompileToAssembly(CompiledCSharpCode code, bool throwOnFailure = true) + { + var cSharpDocument = code.CodeDocument.GetCSharpDocument(); + if (cSharpDocument.Diagnostics.Any()) + { + var diagnosticsLog = string.Join(Environment.NewLine, cSharpDocument.Diagnostics.Select(d => d.ToString()).ToArray()); + throw new InvalidOperationException($"Aborting compilation to assembly because RazorCompiler returned nonempty diagnostics: {diagnosticsLog}"); + } + + var syntaxTrees = new[] + { + (CSharpSyntaxTree)CSharpSyntaxTree.ParseText(cSharpDocument.GeneratedCode, CSharpParseOptions, path: code.CodeDocument.Source.FilePath), }; - return projectItem; + var compilation = code.BaseCompilation.AddSyntaxTrees(syntaxTrees); + + var diagnostics = compilation + .GetDiagnostics() + .Where(d => d.Severity >= DiagnosticSeverity.Warning) + .ToArray(); + + if (diagnostics.Length > 0 && throwOnFailure) + { + throw new CompilationFailedException(compilation, diagnostics); + } + else if (diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error)) + { + return new CompiledAssembly(compilation, code.CodeDocument, assembly: null); + } + + using (var peStream = new MemoryStream()) + { + var emit = compilation.Emit(peStream); + diagnostics = emit + .Diagnostics + .Where(d => d.Severity >= DiagnosticSeverity.Warning) + .ToArray(); + if (diagnostics.Length > 0 && throwOnFailure) + { + throw new CompilationFailedException(compilation, diagnostics); + } + + return new CompiledAssembly(compilation, code.CodeDocument, Assembly.Load(peStream.ToArray())); + } + } + + private CSharpCompilation CreateCompilation() + { + var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTrees); + var diagnostics = compilation.GetDiagnostics().Where(d => d.Severity >= DiagnosticSeverity.Warning).ToArray(); + if (diagnostics.Length > 0) + { + throw new CompilationFailedException(compilation, diagnostics); + } + + return compilation; + } + + protected RazorProjectEngine CreateProjectEngine(Action configure = null) + { + var compilation = CreateCompilation(); + var references = compilation.References.Concat(new[] { compilation.ToMetadataReference(), }).ToArray(); + return CreateProjectEngine(Configuration, references, configure); + } + + private RazorProjectEngine CreateProjectEngine(RazorConfiguration configuration, MetadataReference[] references, Action configure) + { + return RazorProjectEngine.Create(configuration, FileSystem, b => + { + b.Phases.Insert(0, new ConfigureCodeRenderingPhase(LineEnding)); + + configure?.Invoke(b); + + // Allow the test to do custom things with tag helpers, but do the default thing most of the time. + if (!b.Features.OfType().Any()) + { + b.Features.Add(new CompilationTagHelperFeature()); + b.Features.Add(new DefaultMetadataReferenceFeature() + { + References = references, + }); + } + + // Decorate the import feature so we can normalize line endings. + var importFeature = b.Features.OfType().FirstOrDefault(); + b.Features.Add(new NormalizedDefaultImportFeature(importFeature, LineEnding)); + b.Features.Remove(importFeature); + }); } protected void AssertDocumentNodeMatchesBaseline(DocumentIntermediateNode document) @@ -225,96 +459,93 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests Assert.Equal(baseline, actual); } - private static string NormalizeNewLines(string content) + private string NormalizeNewLines(string content) { - return Regex.Replace(content, "(? GetImports(RazorProjectItem projectItem) { - var imports = new List(); - - while (true) + if (_inner == null) { - var importsFileName = Path.ChangeExtension(projectItem.FilePathWithoutExtension + "_Imports" + imports.Count.ToString(), ".cshtml"); - var importsFile = TestFile.Create(importsFileName, _assembly); - if (!importsFile.Exists()) - { - break; - } - - var importContent = importsFile.ReadAllText(); - var normalizedContent = NormalizeNewLines(importContent); - var importItem = new TestRazorProjectItem(importsFileName) - { - Content = normalizedContent - }; - imports.Add(importItem); + return Array.Empty(); } - imports.AddRange(_existingImportFeature.GetImports(projectItem)); + var normalizedImports = new List(); + var imports = _inner.GetImports(projectItem); + foreach (var import in imports) + { + if (import.Exists) + { + var text = string.Empty; + using (var stream = import.Read()) + using (var reader = new StreamReader(stream)) + { + text = reader.ReadToEnd().Trim(); + } - return imports; - } - } + // It's important that we normalize the newlines in the default imports. The default imports will + // be created with Environment.NewLine, but we need to normalize to `\r\n` so that the indices + // are the same on xplat. + var normalizedText = NormalizeNewLines(text, _lineEnding); + var normalizedImport = new TestRazorProjectItem(import.FilePath, import.PhysicalPath, import.RelativePhysicalPath, import.BasePath) + { + Content = normalizedText + }; - private class IntegrationTestFileSystem : RazorProjectFileSystem - { - public static IntegrationTestFileSystem Default = new IntegrationTestFileSystem(); + normalizedImports.Add(normalizedImport); + } + } - private IntegrationTestFileSystem() - { - } - - public override IEnumerable EnumerateItems(string basePath) - { - return Enumerable.Empty(); - } - - public override RazorProjectItem GetItem(string path) - { - return new NotFoundProjectItem(string.Empty, path); - } - - public override IEnumerable FindHierarchicalItems(string basePath, string path, string fileName) - { - return Enumerable.Empty(); + return normalizedImports; } } } diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Microsoft.AspNetCore.Razor.Test.Common.csproj b/test/Microsoft.AspNetCore.Razor.Test.Common/Microsoft.AspNetCore.Razor.Test.Common.csproj index d1f72c96bb..95f1644d40 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.Common/Microsoft.AspNetCore.Razor.Test.Common.csproj +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Microsoft.AspNetCore.Razor.Test.Common.csproj @@ -9,6 +9,7 @@ +