Merge branch 'release/2.2'

This commit is contained in:
Ryan Nowak 2018-10-01 17:05:44 -07:00
commit fb9a02ef5a
26 changed files with 1135 additions and 773 deletions

View File

@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.IntegrationTests
b.AddTargetExtension(new TemplateTargetExtension());
});
var projectItem = CreateProjectItem();
var projectItem = CreateProjectItemFromFile();
// Act
var document = engine.Process(projectItem);

View File

@ -10,7 +10,6 @@ Document -
UsingDirective - (178:6,1 [43] ) - Microsoft.AspNetCore.Mvc.ViewFeatures
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_InheritsWithViewImports - MyPageModel<MyModel> -
DesignTimeDirective -
DirectiveToken - (10:0,10 [19] InheritsWithViewImports_Imports0.cshtml) - MyPageModel<TModel>
DirectiveToken - (231:7,8 [62] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<TModel>
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<TModel>
DirectiveToken - (14:1,7 [7] InheritsWithViewImports.cshtml) - MyModel
CSharpCode -
IntermediateToken - - CSharp - #pragma warning disable 0414

View File

@ -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<MyModel>
{
#pragma warning disable 1998

View File

@ -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<TModel>
{
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<TModel> : 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<TModel> : RazorPage
public class MyModel
{
}
";
}");
var compilation = BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(appCode));
RunDesignTimeTest(compilation);
AddProjectItemFromText(@"@inherits MyBasePageForViews<TModel>");
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<TModel>
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<TModel>
{
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<TModel>
{
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<string> 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<string> 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<IImportProjectFeature>().Single();
b.SetImportFeature(new NormalizedDefaultImportFeature(existingImportFeature));
b.Features.Add(GetMetadataReferenceFeature(references));
b.Features.Add(new CompilationTagHelperFeature());
});
}
private static IRazorEngineFeature GetMetadataReferenceFeature(IEnumerable<MetadataReference> 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<RazorProjectItem> GetImports(RazorProjectItem projectItem)
{
var normalizedImports = new List<RazorProjectItem>();
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, "(?<!\r)\n", "\r\n", RegexOptions.None, TimeSpan.FromSeconds(10));
var normalizedImport = new TestRazorProjectItem(import.FilePath, import.PhysicalPath, import.RelativePhysicalPath, import.BasePath)
{
Content = normalizedText
};
normalizedImports.Add(normalizedImport);
}
return normalizedImports;
}
AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode());
AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument());
AssertSourceMappingsMatchBaseline(compiled.CodeDocument);
}
}
}

View File

@ -10,7 +10,6 @@ Document -
UsingDirective - (178:6,1 [43] ) - Microsoft.AspNetCore.Mvc.ViewFeatures
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_InheritsWithViewImports - MyBasePageForViews<MyModel> -
DesignTimeDirective -
DirectiveToken - (10:0,10 [26] InheritsWithViewImports_Imports0.cshtml) - MyBasePageForViews<TModel>
DirectiveToken - (231:7,8 [62] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<TModel>
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<TModel>
DirectiveToken - (7:0,7 [7] InheritsWithViewImports.cshtml) - MyModel
CSharpCode -
IntermediateToken - - CSharp - #pragma warning disable 0414

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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);

View File

@ -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());
}
}
}

View File

@ -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);

View File

@ -1,37 +0,0 @@
// <auto-generated/>
#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

View File

@ -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 - <p>
IntermediateToken - (3:0,3 [9] BasicImports.cshtml) - Html - Hi there!
IntermediateToken - (12:0,12 [4] BasicImports.cshtml) - Html - </p>
IntermediateToken - (16:0,16 [2] BasicImports.cshtml) - Html - \n

View File

@ -1,5 +0,0 @@
@using System.Globalization
@using System.ComponentModel
@inherits Hello
@("And also this")
<p>This will get ignored</p>

View File

@ -1,36 +0,0 @@
#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicImports.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "d5e5d28c0c504a7b0c5207a5230a5b7327ce5e09"
// <auto-generated/>
#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("<p>Hi there!</p>\r\n");
}
#pragma warning restore 1998
}
}
#pragma warning restore 1591

View File

@ -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 - <p>
IntermediateToken - (3:0,3 [9] BasicImports.cshtml) - Html - Hi there!
IntermediateToken - (12:0,12 [4] BasicImports.cshtml) - Html - </p>
IntermediateToken - (16:0,16 [2] BasicImports.cshtml) - Html - \n

View File

@ -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<SyntaxTree>();
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();
}
}
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -13,11 +13,14 @@ using System.Runtime.Remoting.Messaging;
#else
using System.Threading;
#endif
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Razor;
using Xunit;
using Xunit.Sdk;
using Microsoft.AspNetCore.Razor.Language.Legacy;
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
{
@ -28,15 +31,81 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
private static readonly AsyncLocal<string> _fileName = new AsyncLocal<string>();
#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<SyntaxTree>(),
referenceAssemblies,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
}
protected IntegrationTestBase(bool? generateBaselines = null)
{
TestProjectRoot = TestProject.GetProjectDirectory(GetType());
if (generateBaselines.HasValue)
{
GenerateBaselines = generateBaselines.Value;
}
}
/// <summary>
/// Gets the <see cref="CSharpCompilation"/> that will be used as the 'app' compilation.
/// </summary>
protected virtual CSharpCompilation BaseCompilation => DefaultBaseCompilation;
/// <summary>
/// Gets the parse options applied when using <see cref="AddCSharpSyntaxTree(string, string)"/>.
/// </summary>
protected virtual CSharpParseOptions CSharpParseOptions { get; } = new CSharpParseOptions(LanguageVersion.Latest);
/// <summary>
/// Gets the compilation options applied when compiling assemblies.
/// </summary>
protected virtual CSharpCompilationOptions CSharpCompilationOptions { get; } = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
/// <summary>
/// Gets a list of CSharp syntax trees used that are considered part of the 'app'.
/// </summary>
protected virtual List<CSharpSyntaxTree> CSharpSyntaxTrees { get; } = new List<CSharpSyntaxTree>();
/// <summary>
/// Gets the <see cref="RazorConfiguration"/> that will be used for code generation.
/// </summary>
protected virtual RazorConfiguration Configuration { get; } = RazorConfiguration.Default;
protected virtual bool DesignTime { get; } = false;
/// <summary>
/// Gets the
/// </summary>
internal VirtualRazorProjectFileSystem FileSystem { get; } = new VirtualRazorProjectFileSystem();
/// <summary>
/// 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.
/// </summary>
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<RazorProjectEngineBuilder> 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<IImportProjectFeature>().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<RazorProjectEngineBuilder> 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<RazorProjectEngineBuilder> 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<ITagHelperFeature>().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<IImportProjectFeature>().FirstOrDefault();
b.Features.Add(new NormalizedDefaultImportFeature(importFeature, LineEnding));
b.Features.Remove(importFeature);
});
}
protected void AssertDocumentNodeMatchesBaseline(DocumentIntermediateNode document)
@ -318,96 +552,93 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
.Replace("\t", "\\t");
}
private static string NormalizeNewLines(string content)
private string NormalizeNewLines(string content)
{
return Regex.Replace(content, "(?<!\r)\n", "\r\n", RegexOptions.None, TimeSpan.FromSeconds(10));
return NormalizeNewLines(content, LineEnding);
}
private class IntegrationTestProjectEngine : DefaultRazorProjectEngine
private static string NormalizeNewLines(string content, string lineEnding)
{
public IntegrationTestProjectEngine(
RazorProjectEngine innerEngine)
: base(innerEngine.Configuration, innerEngine.Engine, innerEngine.FileSystem, innerEngine.ProjectFeatures)
return Regex.Replace(content, "(?<!\r)\n", lineEnding, RegexOptions.None, TimeSpan.FromSeconds(10));
}
// This is to prevent you from accidentally checking in with GenerateBaselines = true
[Fact]
public void GenerateBaselinesMustBeFalse()
{
Assert.False(GenerateBaselines, "GenerateBaselines should be set back to false before you check in!");
}
private class ConfigureCodeRenderingPhase : RazorEnginePhaseBase
{
public ConfigureCodeRenderingPhase(string lineEnding)
{
LineEnding = lineEnding;
}
protected override void ProcessCore(RazorCodeDocument codeDocument)
public string LineEnding { get; }
protected override void ExecuteCore(RazorCodeDocument codeDocument)
{
// This will ensure that we're not putting any randomly generated data in a baseline.
codeDocument.Items[CodeRenderingContext.SuppressUniqueIds] = "test";
// This is to make tests work cross platform.
codeDocument.Items[CodeRenderingContext.NewLineString] = "\r\n";
base.ProcessCore(codeDocument);
codeDocument.Items[CodeRenderingContext.NewLineString] = LineEnding;
}
}
private class IntegrationTestImportFeature : RazorProjectEngineFeatureBase, IImportProjectFeature
// 'Default' imports won't have normalized line-endings, which is unfriendly for testing.
private class NormalizedDefaultImportFeature : RazorProjectEngineFeatureBase, IImportProjectFeature
{
private Assembly _assembly;
private IImportProjectFeature _existingImportFeature;
private readonly IImportProjectFeature _inner;
private readonly string _lineEnding;
public IntegrationTestImportFeature(Assembly assembly, IImportProjectFeature existingImportFeature)
public NormalizedDefaultImportFeature(IImportProjectFeature inner, string lineEnding)
{
_assembly = assembly;
_existingImportFeature = existingImportFeature;
_inner = inner;
_lineEnding = lineEnding;
}
protected override void OnInitialized()
{
_existingImportFeature.ProjectEngine = ProjectEngine;
if (_inner != null)
{
_inner.ProjectEngine = ProjectEngine;
}
}
public IReadOnlyList<RazorProjectItem> GetImports(RazorProjectItem projectItem)
{
var imports = new List<RazorProjectItem>();
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<RazorProjectItem>();
}
imports.AddRange(_existingImportFeature.GetImports(projectItem));
var normalizedImports = new List<RazorProjectItem>();
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<RazorProjectItem> EnumerateItems(string basePath)
{
return Enumerable.Empty<RazorProjectItem>();
}
public override RazorProjectItem GetItem(string path)
{
return new NotFoundProjectItem(string.Empty, path);
}
public override IEnumerable<RazorProjectItem> FindHierarchicalItems(string basePath, string path, string fileName)
{
return Enumerable.Empty<RazorProjectItem>();
return normalizedImports;
}
}
}

View File

@ -9,6 +9,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Language\Microsoft.AspNetCore.Razor.Language.csproj" />
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor\Microsoft.CodeAnalysis.Razor.csproj" />
</ItemGroup>
<ItemGroup>