Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,39 @@
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.CodeAnalysis;
using Roslyn.Test.Utilities;

namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests;

public class CodeGenerationIntegrationTest(bool designTime = false)
: IntegrationTestBase(layer: TestProject.Layer.Compiler)
public class CodeGenerationIntegrationTest : IntegrationTestBase
{
private readonly bool designTime;

public CodeGenerationIntegrationTest(bool designTime = false)
: base(layer: TestProject.Layer.Compiler)
{
this.designTime = designTime;
BaseCompilation = BaseCompilation.AddReferences(
MetadataReference.CreateFromFile(typeof(TestTagHelperDescriptors).Assembly.Location));
}

[IntegrationTestFact]
public void SingleLineControlFlowStatements() => RunTest();

[IntegrationTestFact]
public void CSharp8() => RunTest();
public void CSharp8()
{
// C# 8 features are not available in .NET Framework without polyfills
// so the C# diagnostics would be different between .NET Framework and .NET Core.
SkipVerifyingCSharpDiagnostics = ExecutionConditionUtil.IsDesktop;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider making these optional params to the IntegrationTestFact? So we could have e.g. [IntegrationTestFact(SkipVerifyingCSharpDiagnostics = true)]

Copy link
Member Author

@jjonescz jjonescz May 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, haven't considered that, sounds interesting. But it looks like it would be much more difficult to propagate these options down to the IntegrationTestBase methods where I need them. If I understand correctly, I would need to add new properties to IntegrationTestFact, then add new parameters to constructors of each test class, then pass the properties as constructor arguments in IntegrationTestCase. Maybe there's a simpler way? But still, for end-user the effect would be just moving something like SkipVerifyingCSharpDiagnostics = ExecutionConditionUtil.IsDesktop; from the test body into the attribute which doesn't seem worth the effort here. So I think I will leave as is. But happy to change this in a follow up if I misunderstood :)


NullableEnable = true;

RunTest();
}

[IntegrationTestFact]
public void IncompleteDirectives() => RunTest();
Expand Down Expand Up @@ -64,7 +84,13 @@ public class CodeGenerationIntegrationTest(bool designTime = false)
public void NoLinePragmas() => RunTest();

[IntegrationTestFact]
public void NestedCSharp() => RunTest();
public void NestedCSharp()
{
// Trying to load the DLL results in "System.BadImageFormatException: Bad IL format."
SkipLoadingDll = true;

RunTest();
}

[IntegrationTestFact]
public void NestedCodeBlocks() => RunTest();
Expand Down Expand Up @@ -178,7 +204,13 @@ public class CodeGenerationIntegrationTest(bool designTime = false)
public void IncompleteTagHelper() => RunTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);

[IntegrationTestFact]
public void BasicTagHelpers() => RunTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
public void BasicTagHelpers()
{
// Trying to load the DLL results in "System.BadImageFormatException: Bad IL format."
SkipLoadingDll = true;

RunTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
}

[IntegrationTestFact]
public void BasicTagHelpers_Prefixed() => RunTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
Expand Down Expand Up @@ -220,7 +252,13 @@ public class CodeGenerationIntegrationTest(bool designTime = false)
public void MinimizedTagHelpers() => RunTagHelpersTest(TestTagHelperDescriptors.MinimizedTagHelpers_Descriptors);

[IntegrationTestFact]
public void NestedScriptTagTagHelpers() => RunTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
public void NestedScriptTagTagHelpers()
{
// Trying to load the DLL results in "System.BadImageFormatException: Bad IL format."
SkipLoadingDll = true;

RunTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
}

[IntegrationTestFact]
public void SymbolBoundAttributes() => RunTagHelpersTest(TestTagHelperDescriptors.SymbolBoundTagHelperDescriptors);
Expand All @@ -244,7 +282,14 @@ public class CodeGenerationIntegrationTest(bool designTime = false)
public void AttributeDirective() => RunTest();

[IntegrationTestFact]
public void SwitchExpression_RecursivePattern() => RunTest();
public void SwitchExpression_RecursivePattern()
{
// System.Index is not available in .NET Framework without polyfills
// so the C# diagnostics would be different between .NET Framework and .NET Core.
SkipVerifyingCSharpDiagnostics = ExecutionConditionUtil.IsDesktop;

RunTest();
}

[IntegrationTestFact]
public new void DesignTime() => RunTest();
Expand Down Expand Up @@ -275,15 +320,7 @@ private void RunTest([CallerMemberName] string testName = "")
private void DesignTimeTest(string testName)
{
// Arrange
var projectEngine = CreateProjectEngine(builder =>
{
builder.ConfigureDocumentClassifier(GetTestFileName(testName));

// Some of these tests use templates
builder.AddTargetExtension(new TemplateTargetExtension());

SectionDirective.Register(builder);
});
var projectEngine = CreateProjectEngine(RazorExtensions.Register);

var projectItem = CreateProjectItemFromFile(testName: testName);

Expand All @@ -297,20 +334,13 @@ private void DesignTimeTest(string testName)
AssertSourceMappingsMatchBaseline(codeDocument, testName);
AssertHtmlSourceMappingsMatchBaseline(codeDocument, testName);
AssertLinePragmas(codeDocument, designTime: true);
AssertCSharpDiagnosticsMatchBaseline(codeDocument, testName);
}

private void RunTimeTest(string testName)
{
// Arrange
var projectEngine = CreateProjectEngine(builder =>
{
builder.ConfigureDocumentClassifier(GetTestFileName(testName));

// Some of these tests use templates
builder.AddTargetExtension(new TemplateTargetExtension());

SectionDirective.Register(builder);
});
var projectEngine = CreateProjectEngine(RazorExtensions.Register);

var projectItem = CreateProjectItemFromFile(testName: testName);

Expand All @@ -321,6 +351,7 @@ private void RunTimeTest(string testName)
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode(), testName);
AssertCSharpDocumentMatchesBaseline(codeDocument.GetCSharpDocument(), testName);
AssertLinePragmas(codeDocument, designTime: false);
AssertCSharpDiagnosticsMatchBaseline(codeDocument, testName);
}

private void RunTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors, [CallerMemberName] string testName = "")
Expand All @@ -338,43 +369,32 @@ private void RunTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors, [Ca
private void RunRuntimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors, string testName)
{
// Arrange
var projectEngine = CreateProjectEngine(builder =>
{
builder.ConfigureDocumentClassifier(GetTestFileName(testName));

// Some of these tests use templates
builder.AddTargetExtension(new TemplateTargetExtension());

SectionDirective.Register(builder);
});
var projectEngine = CreateProjectEngine(RazorExtensions.Register);

var projectItem = CreateProjectItemFromFile(testName: testName);
var imports = GetImports(projectEngine, projectItem);

AddTagHelperStubs(descriptors);

// Act
var codeDocument = projectEngine.Process(RazorSourceDocument.ReadFrom(projectItem), FileKinds.Legacy, imports, descriptors.ToList());

// Assert
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode(), testName);
AssertCSharpDocumentMatchesBaseline(codeDocument.GetCSharpDocument(), testName);
AssertCSharpDiagnosticsMatchBaseline(codeDocument, testName);
}

private void RunDesignTimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors, string testName)
{
// Arrange
var projectEngine = CreateProjectEngine(builder =>
{
builder.ConfigureDocumentClassifier(GetTestFileName(testName));

// Some of these tests use templates
builder.AddTargetExtension(new TemplateTargetExtension());

SectionDirective.Register(builder);
});
var projectEngine = CreateProjectEngine(RazorExtensions.Register);

var projectItem = CreateProjectItemFromFile(testName: testName);
var imports = GetImports(projectEngine, projectItem);

AddTagHelperStubs(descriptors);

// Act
var codeDocument = projectEngine.ProcessDesignTime(RazorSourceDocument.ReadFrom(projectItem), FileKinds.Legacy, imports, descriptors.ToList());

Expand All @@ -384,6 +404,7 @@ private void RunDesignTimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descri
AssertHtmlDocumentMatchesBaseline(codeDocument.GetHtmlDocument(), testName);
AssertHtmlSourceMappingsMatchBaseline(codeDocument, testName);
AssertSourceMappingsMatchBaseline(codeDocument, testName);
AssertCSharpDiagnosticsMatchBaseline(codeDocument, testName);
}

private static ImmutableArray<RazorSourceDocument> GetImports(RazorProjectEngine projectEngine, RazorProjectItem projectItem)
Expand All @@ -397,4 +418,49 @@ private static ImmutableArray<RazorSourceDocument> GetImports(RazorProjectEngine

return importSourceDocuments;
}

private void AddTagHelperStubs(IEnumerable<TagHelperDescriptor> descriptors)
{
var tagHelperClasses = descriptors.Select(descriptor =>
{
var typeName = descriptor.GetTypeName();
var namespaceSeparatorIndex = typeName.LastIndexOf('.');
if (namespaceSeparatorIndex >= 0)
{
var ns = typeName[..namespaceSeparatorIndex];
var c = typeName[(namespaceSeparatorIndex + 1)..];

return $$"""
namespace {{ns}}
{
class {{c}} {{getTagHelperBody(descriptor)}}
}
""";
}

return $$"""
class {{typeName}} {{getTagHelperBody(descriptor)}}
""";

static string getTagHelperBody(TagHelperDescriptor descriptor)
{
var attributes = descriptor.BoundAttributes.Select(attribute => $$"""
public {{attribute.TypeName}} {{attribute.GetPropertyName()}}
{
get => throw new System.NotImplementedException();
set { }
}
""");

return $$"""
: Microsoft.AspNetCore.Razor.TagHelpers.TagHelper
{
{{string.Join("\n", attributes)}}
}
""";
}
});

AddCSharpSyntaxTree(string.Join("\n", tagHelperClasses));
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
// <auto-generated/>
#pragma warning disable 1591
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
namespace AspNetCore
{
#line default
using TModel = global::System.Object;
using global::System;
using global::System.Collections.Generic;
using global::System.Linq;
using global::System.Threading.Tasks;
using global::Microsoft.AspNetCore.Mvc;
using global::Microsoft.AspNetCore.Mvc.Rendering;
using global::Microsoft.AspNetCore.Mvc.ViewFeatures;
#line default
#line hidden
public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_AddTagHelperDirective_DesignTime
[global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute("Identifier", "/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AddTagHelperDirective.cshtml")]
[global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
#nullable restore
public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_AddTagHelperDirective : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
#nullable disable
{
#pragma warning disable 219
private void __RazorDirectiveTokenHelpers__() {
Expand All @@ -23,10 +37,30 @@ private void __RazorDirectiveTokenHelpers__() {
private static object __o = null;
#pragma warning restore 0414
#pragma warning disable 1998
public async System.Threading.Tasks.Task ExecuteAsync()
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
}
#pragma warning restore 1998
#nullable restore
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; } = default!;
#nullable disable
#nullable restore
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; } = default!;
#nullable disable
#nullable restore
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; } = default!;
#nullable disable
#nullable restore
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; } = default!;
#nullable disable
#nullable restore
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<dynamic> Html { get; private set; } = default!;
#nullable disable
}
}
#pragma warning restore 1591
Original file line number Diff line number Diff line change
@@ -1,14 +1,42 @@
Document -
NamespaceDeclaration - - Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_AddTagHelperDirective_DesignTime - -
NamespaceDeclaration - - AspNetCore
UsingDirective - - TModel = global::System.Object
UsingDirective - (1:0,1 [20] ) - global::System
UsingDirective - (24:1,1 [40] ) - global::System.Collections.Generic
UsingDirective - (67:2,1 [25] ) - global::System.Linq
UsingDirective - (95:3,1 [36] ) - global::System.Threading.Tasks
UsingDirective - (134:4,1 [38] ) - global::Microsoft.AspNetCore.Mvc
UsingDirective - (175:5,1 [48] ) - global::Microsoft.AspNetCore.Mvc.Rendering
UsingDirective - (226:6,1 [51] ) - global::Microsoft.AspNetCore.Mvc.ViewFeatures
RazorCompiledItemMetadataAttribute -
CreateNewOnMetadataUpdateAttribute -
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_AddTagHelperDirective - global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic> -
DesignTimeDirective -
DirectiveToken - (287:7,8 [62] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<TModel>
DirectiveToken - (350:7,71 [4] ) - Html
DirectiveToken - (364:8,8 [54] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper
DirectiveToken - (419:8,63 [4] ) - Json
DirectiveToken - (433:9,8 [53] ) - global::Microsoft.AspNetCore.Mvc.IViewComponentHelper
DirectiveToken - (487:9,62 [9] ) - Component
DirectiveToken - (506:10,8 [43] ) - global::Microsoft.AspNetCore.Mvc.IUrlHelper
DirectiveToken - (550:10,52 [3] ) - Url
DirectiveToken - (563:11,8 [70] ) - global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider
DirectiveToken - (634:11,79 [23] ) - ModelExpressionProvider
DirectiveToken - (673:12,14 [104] ) - global::Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor
DirectiveToken - (793:13,14 [95] ) - global::Microsoft.AspNetCore.Mvc.Razor.TagHelpers.HeadTagHelper, Microsoft.AspNetCore.Mvc.Razor
DirectiveToken - (904:14,14 [95] ) - global::Microsoft.AspNetCore.Mvc.Razor.TagHelpers.BodyTagHelper, Microsoft.AspNetCore.Mvc.Razor
DirectiveToken - (14:0,14 [17] AddTagHelperDirective.cshtml) - "*, TestAssembly"
CSharpCode -
IntermediateToken - - CSharp - #pragma warning disable 0414
CSharpCode -
IntermediateToken - - CSharp - private static object __o = null;
CSharpCode -
IntermediateToken - - CSharp - #pragma warning restore 0414
MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync
MethodDeclaration - - public async override - global::System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (31:0,31 [2] AddTagHelperDirective.cshtml)
LazyIntermediateToken - (31:0,31 [2] AddTagHelperDirective.cshtml) - Html - \n
Inject -
Inject -
Inject -
Inject -
Inject -
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Source Location: (14:0,14 [17] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AddTagHelperDirective.cshtml)
|"*, TestAssembly"|
Generated Location: (551:12,37 [17] )
Generated Location: (1253:26,37 [17] )
|"*, TestAssembly"|

Loading