// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.NET.Sdk.Razor.SourceGenerators
{
    public sealed class RazorSourceGeneratorTests : RazorSourceGeneratorTestsBase
    {
        [Fact]
        public async Task SourceGenerator_RazorFiles_Works()
        {
            // Arrange
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
            });

            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");

            Assert.Empty(result.Diagnostics);
            Assert.Single(result.GeneratedSources);
        }

        [Fact, WorkItem("https://github.com/dotnet/razor/issues/8610")]
        public async Task SourceGenerator_RazorFiles_Using_NestedClass()
        {
            // Arrange
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = """
                    @code {
                        public class MyModel { }
                    }
                    """,
                ["Shared/MyComponent.razor"] = """

                    <MyComponent Data="@Data" />

                    @code {
                        [Parameter]
                        public Pages.Index.MyModel? Data { get; set; }
                    }
                    """,
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project, options =>
            {
                options.TestGlobalOptions["build_property.RazorLangVersion"] = "7.0";
            });

            // Act
            var result = RunGenerator(compilation!, ref driver);

            // Assert
            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);
        }

        [Fact, WorkItem("https://github.com/dotnet/razor/issues/8610")]
        public async Task SourceGenerator_RazorFiles_UsingAlias_NestedClass()
        {
            // Arrange
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = """
                    @code {
                        public class MyModel { }
                    }
                    """,
                ["Shared/MyComponent.razor"] = """
                    @using MyAlias = Pages.Index.MyModel;

                    <MyComponent Data="@Data" />

                    @code {
                        [Parameter]
                        public MyAlias? Data { get; set; }
                    }
                    """,
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project, options =>
            {
                options.TestGlobalOptions["build_property.RazorLangVersion"] = "7.0";
            });

            // Act
            var result = RunGenerator(compilation!, ref driver);

            // Assert
            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);
        }

        [Fact]
        public async Task SourceGeneratorEvents_RazorFiles_Works()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
",
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""0de17e526cd536d59072aa0e924e99111b16b97a""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Counter</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
                e => Assert.Equal("ComputeRazorSourceGeneratorOptions", e.EventName),
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Counter.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/Index.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/Index.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/Counter.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/Counter.razor"),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromReferencesStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromReferencesStop", e.EventName),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Index.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Index.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Counter.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Counter.razor", "Runtime"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_razor.g.cs"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs")
            );
        }

        [Fact]
        public async Task IncrementalCompilation_DoesNotReexecuteSteps_WhenRazorFilesAreUnchanged()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(@"
#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591",
@"
#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""0de17e526cd536d59072aa0e924e99111b16b97a""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Counter</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Empty(eventListener.Events);
        }

        [Fact]
        public async Task IncrementalCompilation_WhenRazorFileMarkupChanges()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
",
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""0de17e526cd536d59072aa0e924e99111b16b97a""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Counter</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Empty(eventListener.Events);

            var updatedText = new TestAdditionalText("Pages/Counter.razor", SourceText.From("<h2>Counter</h2>", Encoding.UTF8));
            driver = driver.ReplaceAdditionalText(additionalTexts.First(f => f.Path == updatedText.Path), updatedText);

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result, (1,
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""e022c3eac864ad044e9b7d56f4c493ab4eab36da""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h2>Counter</h2>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
"));

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Counter.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/Counter.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/Counter.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Counter.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Counter.razor", "Runtime"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs")
            );
        }

        [Fact]
        public async Task IncrementalCompilation_RazorFiles_WhenNewTypeIsAdded()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
",
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""0de17e526cd536d59072aa0e924e99111b16b97a""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Counter</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            project = project.AddDocument("Person.cs", SourceText.From("""
                public class Person
                {
                    public string Name { get; set; }
                }
                """, Encoding.UTF8)).Project;
            compilation = await project.GetCompilationAsync();

            result = RunGenerator(compilation!, ref driver,
                // Person.cs(3,19): warning CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
                //     public string Name { get; set; }
                Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Name").WithArguments("property", "Name").WithLocation(3, 19)
            );

            result.VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName));
        }

        [Fact]
        public async Task IncrementalCompilation_RazorFiles_WhenCSharpTypeChanges()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            },
            new()
            {
                ["Person.cs"] = @"
public class Person
{
    public string Name { get; set; }
}"
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var expectedDiagnostics = new DiagnosticDescription[]
            {
                // Person.cs(4,19): warning CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
                //     public string Name { get; set; }
                Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Name").WithArguments("property", "Name").WithLocation(4, 19)
            };

            var result = RunGenerator(compilation!, ref driver, expectedDiagnostics)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
",
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""0de17e526cd536d59072aa0e924e99111b16b97a""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Counter</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver, expectedDiagnostics)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            project = project.Documents.First().WithText(SourceText.From(@"
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}", Encoding.UTF8)).Project;
            compilation = await project.GetCompilationAsync();

            result = RunGenerator(compilation!, ref driver, expectedDiagnostics)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName));
        }

        [Fact]
        public async Task IncrementalCompilation_RazorFiles_WhenChildComponentsAreAdded()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
",
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""0de17e526cd536d59072aa0e924e99111b16b97a""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Counter</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Empty(eventListener.Events);

            var updatedText = new TestAdditionalText("Pages/Counter.razor", SourceText.From(@"
<h2>Counter</h2>
<h3>Current count: @count</h3>
<button @onclick=""Click"">Click me</button>

@code
{
    private int count;

    public void Click() => count++;
}

", Encoding.UTF8));
            driver = driver.ReplaceAdditionalText(additionalTexts.First(f => f.Path == updatedText.Path), updatedText);

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result, (1,
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""1ea7ff43d3a2eecc5502dd3771378e334a8fb068""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h2>Counter</h2>\r\n"");
            __builder.OpenElement(1, ""h3"");
            __builder.AddContent(2, ""Current count: "");
#nullable restore
#line (3,21)-(3,26) 24 ""Pages/Counter.razor""
__builder.AddContent(3, count
#line default
#line hidden
#nullable disable
            );
            __builder.CloseElement();
            __builder.AddMarkupContent(4, ""\r\n"");
            __builder.AddMarkupContent(5, ""<button @onclick=\""Click\"">Click me</button>"");
        }
        #pragma warning restore 1998
#nullable restore
#line (7,2)-(11,1) ""Pages/Counter.razor""
    private int count;
    public void Click() => count++;
#line default
#line hidden
#nullable disable
    }
}
#pragma warning restore 1591
"));

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Counter.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/Counter.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/Counter.razor"),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Counter.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Counter.razor", "Runtime"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs")
            );
        }

        [Fact]
        public async Task IncrementalCompilation_RazorFiles_WhenNewComponentParameterIsAdded()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
",
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""0de17e526cd536d59072aa0e924e99111b16b97a""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Counter</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Empty(eventListener.Events);

            var updatedText = new TestAdditionalText("Pages/Counter.razor", SourceText.From(@"
<h2>Counter</h2>
<h3>Current count: @count</h3>
<button @onclick=""Click"">Click me</button>

@code
{
    private int count;

    public void Click() => count++;

    [Parameter] public int IncrementAmount { get; set; }
}

", Encoding.UTF8));
            driver = driver.ReplaceAdditionalText(additionalTexts.First(f => f.Path == updatedText.Path), updatedText);

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result, (1,
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""fb93b97dc0d4885250500697f41ffde70decc444""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h2>Counter</h2>\r\n"");
            __builder.OpenElement(1, ""h3"");
            __builder.AddContent(2, ""Current count: "");
#nullable restore
#line (3,21)-(3,26) 24 ""Pages/Counter.razor""
__builder.AddContent(3, count
#line default
#line hidden
#nullable disable
            );
            __builder.CloseElement();
            __builder.AddMarkupContent(4, ""\r\n"");
            __builder.AddMarkupContent(5, ""<button @onclick=\""Click\"">Click me</button>"");
        }
        #pragma warning restore 1998
#nullable restore
#line (7,2)-(13,1) ""Pages/Counter.razor""
    private int count;
    public void Click() => count++;
    [Parameter] public int IncrementAmount { get; set; }
#line default
#line hidden
#nullable disable
    }
}
#pragma warning restore 1591
"));

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
               e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Counter.razor"),
               e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Counter.razor"),
               e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/Counter.razor"),
               e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/Counter.razor"),
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName),
               e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Counter.razor"),
               e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Counter.razor"),
               e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"),
               e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"),
               e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"),
               e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"),
               e => e.AssertPair("RazorCodeGenerateStart", "Pages/Index.razor", "Runtime"),
               e => e.AssertPair("RazorCodeGenerateStop", "Pages/Index.razor", "Runtime"),
               e => e.AssertPair("RazorCodeGenerateStart", "Pages/Counter.razor", "Runtime"),
               e => e.AssertPair("RazorCodeGenerateStop", "Pages/Counter.razor", "Runtime"),
               e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs")
            );
        }

        [Fact]
        public async Task IncrementalCompilation_RazorFiles_WhenProjectReferencesChange()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] =
@"
@using SurveyPromptRootNamspace;
<h1>Hello world</h1>
<SurveyPrompt />
",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver,
                // Pages/Index.razor(2,8): error CS0246: The type or namespace name 'SurveyPromptRootNamspace' could not be found (are you missing a using directive or an assembly reference?)
                // using SurveyPromptRootNamspace;
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "SurveyPromptRootNamspace").WithArguments("SurveyPromptRootNamspace").WithLocation(2, 8)
            );

            result.VerifyPageOutput(
@"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""4745828f8a0ab77b58022ed5d1095a0242f2a7ee""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
#nullable restore
#line (2,2)-(2,33) ""Pages/Index.razor""
using SurveyPromptRootNamspace;

#nullable disable
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>\r\n"");
            __builder.OpenElement(1, ""SurveyPrompt"");
            __builder.CloseElement();
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
",
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""0de17e526cd536d59072aa0e924e99111b16b97a""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Counter</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");

            var diagnostic = Assert.Single(result.Diagnostics);
            Assert.Equal("RZ10012", diagnostic.Id);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            var surveyPromptAssembly = GetSurveyPromptMetadataReference(compilation!);
            compilation = compilation!.AddReferences(surveyPromptAssembly);

            result = RunGenerator(compilation, ref driver)
                        .VerifyOutputsMatch(result, (0,
@"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""4745828f8a0ab77b58022ed5d1095a0242f2a7ee""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
#nullable restore
#line (2,2)-(2,33) ""Pages/Index.razor""
using SurveyPromptRootNamspace;

#nullable disable
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>\r\n"");
            __builder.OpenComponent<global::SurveyPromptRootNamspace.SurveyPrompt>(1);
            __builder.CloseComponent();
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
"
));

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
                e => Assert.Equal("ComputeRazorSourceGeneratorOptions", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromReferencesStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromReferencesStop", e.EventName),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Index.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Index.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Counter.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Counter.razor", "Runtime"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_razor.g.cs")
            );

            // Verify caching
            eventListener.Clear();
            result = RunGenerator(compilation, ref driver);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);
            Assert.Empty(eventListener.Events);

            static MetadataReference GetSurveyPromptMetadataReference(Compilation currentCompilation)
            {
                var updatedCompilation = currentCompilation.RemoveAllSyntaxTrees()
                    .WithAssemblyName("SurveyPromptAssembly")
                    .AddSyntaxTrees(CSharpSyntaxTree.ParseText(@"
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
namespace SurveyPromptRootNamspace;
public class SurveyPrompt : ComponentBase
{
    protected override void BuildRenderTree(RenderTreeBuilder builder) {}
}"));
                var stream = new MemoryStream();
                var emitResult = updatedCompilation.Emit(stream);
                Assert.True(emitResult.Success);

                stream.Position = 0;
                return MetadataReference.CreateFromStream(stream);
            }
        }

        [Fact]
        public async Task IncrementalCompilation_RazorFiles_CssScopeRemoved()
        {
            // Compile with CssScope set.
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, _, options) = await GetDriverWithAdditionalTextAndProviderAsync(project, static options =>
            {
                options.AdditionalTextOptions["Pages/Index.razor"]["build_metadata.AdditionalFiles.CssScope"] = "test-css-scope";
            });

            var result = RunGenerator(compilation!, ref driver);
            result.Diagnostics.Verify();

            // CSS isolation is enabled.
            Assert.Contains("<h1 test-css-scope>Hello world</h1>", result.GeneratedSources.Single().SourceText.ToString());

            // Unset CssScope.
            options = options.Clone();
            options.AdditionalTextOptions["Pages/Index.razor"].Options.Remove("build_metadata.AdditionalFiles.CssScope");
            driver = driver.WithUpdatedAnalyzerConfigOptions(options);

            result = RunGenerator(compilation!, ref driver);
            result.Diagnostics.Verify();

            // CSS isolation is disabled.
            Assert.Contains("<h1>Hello world</h1>", result.GeneratedSources.Single().SourceText.ToString());
        }

        [Fact]
        public async Task SourceGenerator_CshtmlFiles_Works()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.cshtml"] = "<h1>Hello world</h1>",
                ["Views/Shared/_Layout.cshtml"] = "<h1>Layout</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Pages_Index), @""mvc.1.0.view"", @""/Pages/Index.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Pages/Index.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Pages_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Hello world</h1>"");
        }
        #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
",
@"#pragma checksum ""Views/Shared/_Layout.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""99939b97abcdd846bd8ea59f7a08dacbe060cb3e""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Views_Shared__Layout), @""mvc.1.0.view"", @""/Views/Shared/_Layout.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Views/Shared/_Layout.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Views_Shared__Layout : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Layout</h1>"");
        }
        #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
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
                e => Assert.Equal("ComputeRazorSourceGeneratorOptions", e.EventName),
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Index.cshtml"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Index.cshtml"),
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Views/Shared/_Layout.cshtml"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Views/Shared/_Layout.cshtml"),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromReferencesStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromReferencesStop", e.EventName),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Index.cshtml"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Index.cshtml"),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Views/Shared/_Layout.cshtml"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Views/Shared/_Layout.cshtml"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.cshtml"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.cshtml"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Views/Shared/_Layout.cshtml"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Views/Shared/_Layout.cshtml"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Index.cshtml", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Index.cshtml", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStart", "Views/Shared/_Layout.cshtml", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Views/Shared/_Layout.cshtml", "Runtime"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_cshtml.g.cs"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Views_Shared__Layout_cshtml.g.cs")
            );
        }

        [Fact, WorkItem("https://github.com/dotnet/razor/issues/7049")]
        public async Task SourceGenerator_CshtmlFiles_TagHelperInFunction()
        {
            // Arrange
            var project = CreateTestProject(new()
            {
                ["Pages/Index.cshtml"] = """
                @addTagHelper *, TestProject
                @{ await RenderMyRazor(); }
                @functions {
                    async Task RenderMyRazor()
                    {
                        <email>first tag helper</email>
                        <email>
                            second tag helper
                            <email>nested tag helper</email>
                        </email>
                    }
                }
                """,
            }, new()
            {
                ["EmailTagHelper.cs"] = """
                using Microsoft.AspNetCore.Razor.TagHelpers;
                public class EmailTagHelper : TagHelper
                {
                    public override void Process(TagHelperContext context, TagHelperOutput output)
                    {
                        output.TagName = "a";
                    }
                }
                """
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            // Act
            var result = RunGenerator(compilation!, ref driver,
                // Microsoft.NET.Sdk.Razor.SourceGenerators/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Pages_Index_cshtml.g.cs(68,167): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
                //         __tagHelperExecutionContext = __tagHelperScopeManager.Begin("email", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.StartTagAndEndTag, "test", async() => {
                Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(68, 167),
                // Microsoft.NET.Sdk.Razor.SourceGenerators/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Pages_Index_cshtml.g.cs(84,171): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
                //             __tagHelperExecutionContext = __tagHelperScopeManager.Begin("email", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.StartTagAndEndTag, "test", async() => {
                Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(84, 171)
            );

            // Assert
            Assert.Empty(result.Diagnostics);
            Assert.Single(result.GeneratedSources);
        }

        [Fact, WorkItem("https://github.com/dotnet/razor/issues/7049")]
        public async Task SourceGenerator_CshtmlFiles_TagHelperInFunction_ManualSuppression()
        {
            // Arrange
            var project = CreateTestProject(new()
            {
                ["Pages/Index.cshtml"] = """
                @addTagHelper *, TestProject

                @{ await RenderMyRazor(); }

                @functions {
                    #pragma warning disable 1998
                    async Task RenderMyRazor()
                    {
                        var lambdaWithUnnecessaryAsync1 = async () => { };
                        <email>first tag helper</email>
                        <email>
                            second tag helper
                            <email>nested tag helper</email>
                        </email>
                        var lambdaWithUnnecessaryAsync2 = async () => { };
                    }
                }
                """,
            }, new()
            {
                ["EmailTagHelper.cs"] = """
                using Microsoft.AspNetCore.Razor.TagHelpers;

                public class EmailTagHelper : TagHelper
                {
                    public override void Process(TagHelperContext context, TagHelperOutput output)
                    {
                        output.TagName = "a";
                    }
                }
                """
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            // Act
            var result = RunGenerator(compilation!, ref driver);

            // Assert
            Assert.Empty(result.Diagnostics);
            Assert.Single(result.GeneratedSources);
        }

        [Fact, WorkItem("https://github.com/dotnet/razor/issues/7049")]
        public async Task SourceGenerator_CshtmlFiles_TagHelperInBody()
        {
            // Arrange
            var project = CreateTestProject(new()
            {
                ["Pages/Index.cshtml"] = """
                @addTagHelper *, TestProject

                <email>tag helper</email>
                @section MySection {
                    <p>my section</p>
                }
                """,
            }, new()
            {
                ["EmailTagHelper.cs"] = """
                using Microsoft.AspNetCore.Razor.TagHelpers;

                public class EmailTagHelper : TagHelper
                {
                    public override void Process(TagHelperContext context, TagHelperOutput output)
                    {
                        output.TagName = "a";
                    }
                }
                """
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            // Act
            var result = RunGenerator(compilation!, ref driver);

            // Assert
            Assert.Empty(result.Diagnostics);
            Assert.Single(result.GeneratedSources);
        }

        [Fact]
        public async Task SourceGenerator_CshtmlFiles_WhenMarkupChanges()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.cshtml"] = "<h1>Hello world</h1>",
                ["Views/Shared/_Layout.cshtml"] = "<h1>Layout</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Pages_Index), @""mvc.1.0.view"", @""/Pages/Index.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Pages/Index.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Pages_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Hello world</h1>"");
        }
        #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
",
@"#pragma checksum ""Views/Shared/_Layout.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""99939b97abcdd846bd8ea59f7a08dacbe060cb3e""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Views_Shared__Layout), @""mvc.1.0.view"", @""/Views/Shared/_Layout.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Views/Shared/_Layout.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Views_Shared__Layout : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Layout</h1>"");
        }
        #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
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Empty(eventListener.Events);

            var updatedText = new TestAdditionalText("Views/Shared/_Layout.cshtml", SourceText.From("<h2>Updated Layout</h2>", Encoding.UTF8));
            driver = driver.ReplaceAdditionalText(additionalTexts.First(f => f.Path == updatedText.Path), updatedText);

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result, (1,
@"#pragma checksum ""Views/Shared/_Layout.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""525ae8e5f9273913494d7a628b965837c601aed4""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Views_Shared__Layout), @""mvc.1.0.view"", @""/Views/Shared/_Layout.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Views/Shared/_Layout.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Views_Shared__Layout : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h2>Updated Layout</h2>"");
        }
        #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
"));

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
               e => e.AssertSingleItem("ParseRazorDocumentStart", "Views/Shared/_Layout.cshtml"),
               e => e.AssertSingleItem("ParseRazorDocumentStop", "Views/Shared/_Layout.cshtml"),
               e => e.AssertSingleItem("RewriteTagHelpersStart", "Views/Shared/_Layout.cshtml"),
               e => e.AssertSingleItem("RewriteTagHelpersStop", "Views/Shared/_Layout.cshtml"),
               e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Views/Shared/_Layout.cshtml"),
               e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Views/Shared/_Layout.cshtml"),
               e => e.AssertPair("RazorCodeGenerateStart", "Views/Shared/_Layout.cshtml", "Runtime"),
               e => e.AssertPair("RazorCodeGenerateStop", "Views/Shared/_Layout.cshtml", "Runtime"),
               e => e.AssertSingleItem("AddSyntaxTrees", "Views_Shared__Layout_cshtml.g.cs")
            );
        }

        [Fact]
        public async Task SourceGenerator_CshtmlFiles_CSharpTypeChanges()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.cshtml"] = "<h1>Hello world</h1>",
                ["Views/Shared/_Layout.cshtml"] = "<h1>Layout</h1>",
            },
            new()
            {
                ["Person.cs"] = @"
public class Person
{
    public string Name { get; set; }
}"
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var expectedDiagnostics = new DiagnosticDescription[]
            {
                // Person.cs(4,19): warning CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
                //     public string Name { get; set; }
                Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Name").WithArguments("property", "Name").WithLocation(4, 19)
            };

            var result = RunGenerator(compilation!, ref driver, expectedDiagnostics)
                        .VerifyPageOutput(@"#pragma checksum ""Pages/Index.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Pages_Index), @""mvc.1.0.view"", @""/Pages/Index.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Pages/Index.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Pages_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Hello world</h1>"");
        }
        #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
",
@"#pragma checksum ""Views/Shared/_Layout.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""99939b97abcdd846bd8ea59f7a08dacbe060cb3e""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Views_Shared__Layout), @""mvc.1.0.view"", @""/Views/Shared/_Layout.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Views/Shared/_Layout.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Views_Shared__Layout : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Layout</h1>"");
        }
        #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
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver, expectedDiagnostics)
                       .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            project = project.Documents.First().WithText(SourceText.From(@"
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}", Encoding.UTF8)).Project;
            compilation = await project.GetCompilationAsync();

            result = RunGenerator(compilation!, ref driver, expectedDiagnostics)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName));

        }

        [Fact]
        public async Task SourceGenerator_CshtmlFiles_NewTagHelper()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.cshtml"] =
@"
@addTagHelper *, TestProject
<h2>Hello world</h2>",
                ["Views/Shared/_Layout.cshtml"] = "<h1>Layout</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""5d59ecd7b7cf7355d7f60234988be34b81a8b614""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Pages_Index), @""mvc.1.0.view"", @""/Pages/Index.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Pages/Index.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Pages_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""\r\n"");
            WriteLiteral(""<h2>Hello world</h2>"");
        }
        #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
",
@"#pragma checksum ""Views/Shared/_Layout.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""99939b97abcdd846bd8ea59f7a08dacbe060cb3e""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Views_Shared__Layout), @""mvc.1.0.view"", @""/Views/Shared/_Layout.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Views/Shared/_Layout.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Views_Shared__Layout : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Layout</h1>"");
        }
        #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
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            project = project.AddDocument("HeaderTagHelper.cs", SourceText.From(@"
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace MyApp;

[HtmlTargetElement(""h2"")]
public class HeaderTagHelper : TagHelper
{
    public override int Order => 0;

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.Attributes.Add(""role"", ""heading"");
    }
}", Encoding.UTF8)).Project;
            compilation = await project.GetCompilationAsync();

            result = RunGenerator(compilation!, ref driver);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Collection(eventListener.Events,
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.cshtml"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.cshtml"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Views/Shared/_Layout.cshtml"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Views/Shared/_Layout.cshtml"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Index.cshtml", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Index.cshtml", "Runtime"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_cshtml.g.cs")
           );
        }

        [Fact]
        public async Task SourceGenerator_CshtmlFiles_RazorDiagnostics_Fixed()
        {
            // Arrange
            var project = CreateTestProject(new()
            {
                ["Pages/Index.cshtml"] =
@"
@{
<h1>Malformed h1
}
</h1>",
                ["Views/Shared/_Layout.cshtml"] = "<h1>Layout</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""8e83eb537e49cadda4aff01de0bd33aa716fc633""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Pages_Index), @""mvc.1.0.view"", @""/Pages/Index.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Pages/Index.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Pages_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""\r\n"");
            WriteLiteral(""<h1>Malformed h1\r\n}\r\n</h1>"");
        }
        #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
",
@"#pragma checksum ""Views/Shared/_Layout.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""99939b97abcdd846bd8ea59f7a08dacbe060cb3e""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Views_Shared__Layout), @""mvc.1.0.view"", @""/Views/Shared/_Layout.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Views/Shared/_Layout.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Views_Shared__Layout : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Layout</h1>"");
        }
        #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
");

            var diagnostic = Assert.Single(result.Diagnostics);
            Assert.Equal("RZ1006", diagnostic.Id);
            Assert.Equal(2, result.GeneratedSources.Length);

            var updatedText = new TestAdditionalText("Pages/Index.cshtml", SourceText.From("<h1>Fixed header</h1>", Encoding.UTF8));
            driver = driver.ReplaceAdditionalText(additionalTexts.First(f => f.Path == updatedText.Path), updatedText);

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result, (0,
@"#pragma checksum ""Pages/Index.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""26b4fe0ac8d17fb3e02d5cba709603ceba9ae3ef""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Pages_Index), @""mvc.1.0.view"", @""/Pages/Index.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Pages/Index.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Pages_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Fixed header</h1>"");
        }
        #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
"));

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);
        }

        [Fact]
        public async Task SourceGenerator_CshtmlFiles_RazorDiagnostics_Introduced()
        {
            // Arrange
            var project = CreateTestProject(new()
            {
                ["Pages/Index.cshtml"] = "<h1>Valid h1</h1>",
                ["Views/Shared/_Layout.cshtml"] = "<h1>Layout</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver)
                            .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""7f59d2951cc70dc08bc4e89b90bd3c5ba68459b9""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Pages_Index), @""mvc.1.0.view"", @""/Pages/Index.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Pages/Index.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Pages_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Valid h1</h1>"");
        }
        #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
",
@"#pragma checksum ""Views/Shared/_Layout.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""99939b97abcdd846bd8ea59f7a08dacbe060cb3e""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Views_Shared__Layout), @""mvc.1.0.view"", @""/Views/Shared/_Layout.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Views/Shared/_Layout.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Views_Shared__Layout : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""<h1>Layout</h1>"");
        }
        #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
");

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            var updatedText = new TestAdditionalText("Pages/Index.cshtml", SourceText.From(@"
@{
<h1>Malformed h1
}
</h1>", Encoding.UTF8));
            driver = driver.ReplaceAdditionalText(additionalTexts.First(f => f.Path == updatedText.Path), updatedText);

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result, (0,
@"#pragma checksum ""Pages/Index.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""d088006c7620c4503e95ef0b4cd1dbc82362bf8b""
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Pages_Index), @""mvc.1.0.view"", @""/Pages/Index.cshtml"")]
namespace AspNetCoreGeneratedDocument
{
    #line default
    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
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Pages/Index.cshtml"")]
    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
    #nullable restore
    internal sealed class Pages_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
    #nullable disable
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral(""\r\n"");
            WriteLiteral(""<h1>Malformed h1\r\n}\r\n</h1>"");
        }
        #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
"));

            var diagnostic = Assert.Single(result.Diagnostics);
            Assert.Equal("RZ1006", diagnostic.Id);
            Assert.Equal(2, result.GeneratedSources.Length);
        }

        [Fact, WorkItem("https://github.com/dotnet/razor/issues/8281")]
        public async Task SourceGenerator_CshtmlFiles_ViewComponentTagHelper()
        {
            // Arrange
            var project = CreateTestProject(new()
            {
                ["Views/Home/Index.cshtml"] = """
                @addTagHelper *, TestProject
                <vc:test first-name="Jan" />
                """,
            }, new()
            {
                ["TestViewComponent.cs"] = """
                public class TestViewComponent
                {
                    public string Invoke(string firstName)
                    {
                        return firstName;
                    }
                }
                """,
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            // Act
            var result = RunGenerator(compilation!, ref driver);

            // Assert
            result.VerifyPageOutput(
                """
                #pragma checksum "Views/Home/Index.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "209ff2a910aa467bb7942ed3e6cb586652327a44"
                // <auto-generated/>
                #pragma warning disable 1591
                [assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Views_Home_Index), @"mvc.1.0.view", @"/Views/Home/Index.cshtml")]
                namespace AspNetCoreGeneratedDocument
                {
                    #line default
                    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
                    [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute("Identifier", "/Views/Home/Index.cshtml")]
                    [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
                    #nullable restore
                    internal sealed class Views_Home_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
                    #nullable disable
                    {
                        #line hidden
                        #pragma warning disable 0649
                        private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext __tagHelperExecutionContext;
                        #pragma warning restore 0649
                        private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner __tagHelperRunner = new global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner();
                        #pragma warning disable 0169
                        private string __tagHelperStringValueBuffer;
                        #pragma warning restore 0169
                        private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager __backed__tagHelperScopeManager = null;
                        private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager __tagHelperScopeManager
                        {
                            get
                            {
                                if (__backed__tagHelperScopeManager == null)
                                {
                                    __backed__tagHelperScopeManager = new global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager(StartTagHelperWritingScope, EndTagHelperWritingScope);
                                }
                                return __backed__tagHelperScopeManager;
                            }
                        }
                        private global::AspNetCoreGeneratedDocument.Views_Home_Index.__Generated__TestViewComponentTagHelper __TestViewComponentTagHelper;
                        #pragma warning disable 1998
                        public async override global::System.Threading.Tasks.Task ExecuteAsync()
                        {
                            __tagHelperExecutionContext = __tagHelperScopeManager.Begin("vc:test", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.SelfClosing, "test", async() => {
                            }
                            );
                            __TestViewComponentTagHelper = CreateTagHelper<global::AspNetCoreGeneratedDocument.Views_Home_Index.__Generated__TestViewComponentTagHelper>();
                            __tagHelperExecutionContext.Add(__TestViewComponentTagHelper);
                            BeginWriteTagHelperAttribute();
                            WriteLiteral("Jan");
                            __tagHelperStringValueBuffer = EndWriteTagHelperAttribute();
                            __TestViewComponentTagHelper.firstName = __tagHelperStringValueBuffer;
                            __tagHelperExecutionContext.AddTagHelperAttribute("first-name", __TestViewComponentTagHelper.firstName, global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
                            await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
                            if (!__tagHelperExecutionContext.Output.IsContentModified)
                            {
                                await __tagHelperExecutionContext.SetOutputContentAsync();
                            }
                            Write(__tagHelperExecutionContext.Output);
                            __tagHelperExecutionContext = __tagHelperScopeManager.End();
                        }
                        #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
                        [Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute("vc:test")]
                        public class __Generated__TestViewComponentTagHelper : Microsoft.AspNetCore.Razor.TagHelpers.TagHelper
                        {
                            private readonly global::Microsoft.AspNetCore.Mvc.IViewComponentHelper __helper = null;
                            public __Generated__TestViewComponentTagHelper(global::Microsoft.AspNetCore.Mvc.IViewComponentHelper helper)
                            {
                                __helper = helper;
                            }
                            [Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNotBoundAttribute, global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewContextAttribute]
                            public global::Microsoft.AspNetCore.Mvc.Rendering.ViewContext ViewContext { get; set; }
                            public System.String firstName { get; set; }
                            public override async global::System.Threading.Tasks.Task ProcessAsync(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext __context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput __output)
                            {
                                (__helper as global::Microsoft.AspNetCore.Mvc.ViewFeatures.IViewContextAware)?.Contextualize(ViewContext);
                                var __helperContent = await __helper.InvokeAsync("Test", ProcessInvokeAsyncArgs(__context));
                                __output.TagName = null;
                                __output.Content.SetHtmlContent(__helperContent);
                            }
                            private Dictionary<string, object> ProcessInvokeAsyncArgs(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext __context)
                            {
                                Dictionary<string, object> args = new Dictionary<string, object>();
                                if (__context.AllAttributes.ContainsName("first-name"))
                                {
                                    args[nameof(firstName)] = firstName;
                                }
                                return args;
                            }
                        }
                    }
                }
                #pragma warning restore 1591

                """);
            Assert.Empty(result.Diagnostics);
            Assert.Single(result.GeneratedSources);
        }

        [Fact, WorkItem("https://github.com/dotnet/aspnetcore/issues/36227")]
        public async Task SourceGenerator_DoesNotUpdateSources_WhenSourceGeneratorIsSuppressed()
        {
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });

            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts, optionsProvider) = await GetDriverWithAdditionalTextAndProviderAsync(project);

            // start with the generator suppressed (this is the default state in VS)
            driver = SetSuppressionState(true);

            // results should be empty, and no recorded steps should have run
            using var eventListener = new RazorEventListener();
            var result = RunGenerator(compilation!, ref driver).VerifyPageOutput();
            Assert.DoesNotContain("DocumentsWithSuppression", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedSteps);
            Assert.DoesNotContain("ImplementationSourceOutput", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedOutputSteps);
            Assert.Empty(eventListener.Events);

            // Now enable the generator and confirm we get the expected output
            driver = SetSuppressionState(false);
            result = RunGenerator(compilation!, ref driver)
                .VerifyPageOutput(
@"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
",
@"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""0de17e526cd536d59072aa0e924e99111b16b97a""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Counter</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");
            // Caches have data
            var documents = Assert.Contains("CSharpDocuments", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedSteps);
            Assert.Collection(documents,
               (o) => Assert.Equal(IncrementalStepRunReason.New, Assert.Single(o.Outputs).Reason),
               (o) => Assert.Equal(IncrementalStepRunReason.New, Assert.Single(o.Outputs).Reason)
           );

            var withSuppression = Assert.Contains("DocumentsWithSuppression", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedSteps);
            Assert.Collection(withSuppression,
               (o) => Assert.Equal(IncrementalStepRunReason.New, Assert.Single(o.Outputs).Reason),
               (o) => Assert.Equal(IncrementalStepRunReason.New, Assert.Single(o.Outputs).Reason)
           );

            var outputs = Assert.Contains("ImplementationSourceOutput", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedOutputSteps);
            Assert.Collection(outputs,
                (o) => Assert.Equal(IncrementalStepRunReason.New, Assert.Single(o.Outputs).Reason),
                (o) => Assert.Equal(IncrementalStepRunReason.New, Assert.Single(o.Outputs).Reason)
            );

            // All required steps should have run
            Assert.Collection(eventListener.Events,
                e => Assert.Equal("ComputeRazorSourceGeneratorOptions", e.EventName),
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Counter.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/Index.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/Index.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/Counter.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/Counter.razor"),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromReferencesStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromReferencesStop", e.EventName),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Index.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Index.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Counter.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Counter.razor", "Runtime"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_razor.g.cs"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs")
                );

            // flip the suppression state back to off
            driver = SetSuppressionState(true);

            // results should be empty, and no events recorded
            eventListener.Clear();
            result = RunGenerator(compilation!, ref driver)
                .VerifyPageOutput();
            Assert.Empty(eventListener.Events);

            // But there *should* still be data in the caches:

            // Documents are cached
            documents = Assert.Contains("CSharpDocuments", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedSteps);
            Assert.Collection(documents,
               (o) => Assert.Equal(IncrementalStepRunReason.Cached, Assert.Single(o.Outputs).Reason),
               (o) => Assert.Equal(IncrementalStepRunReason.Cached, Assert.Single(o.Outputs).Reason)
            );

            // With suppression is modified, because we actually want the output to run to remove the docs
            withSuppression = Assert.Contains("DocumentsWithSuppression", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedSteps);
            Assert.Collection(withSuppression,
               (o) => Assert.Equal(IncrementalStepRunReason.Modified, Assert.Single(o.Outputs).Reason),
               (o) => Assert.Equal(IncrementalStepRunReason.Modified, Assert.Single(o.Outputs).Reason)
            );

            // outputs are modified but empty (as asserted above via VerifyPageOutput)
            outputs = Assert.Contains("ImplementationSourceOutput", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedOutputSteps);
            Assert.Collection(outputs,
               (o) => Assert.Equal(IncrementalStepRunReason.Modified, Assert.Single(o.Outputs).Reason),
               (o) => Assert.Equal(IncrementalStepRunReason.Modified, Assert.Single(o.Outputs).Reason)
            );

            // Make an edit, check nothing changes
            var updatedText = new TestAdditionalText("Pages/Index.razor", SourceText.From("<h2>Hi Folks</h2>", Encoding.UTF8));
            driver = driver.ReplaceAdditionalText(additionalTexts.First(f => f.Path == updatedText.Path), updatedText);

            result = RunGenerator(compilation!, ref driver)
                .VerifyPageOutput();
            Assert.Empty(eventListener.Events);

            // Flip suppression off, and check that only the edited page gets updated
            driver = SetSuppressionState(false);
            result = RunGenerator(compilation!, ref driver)
                .VerifyPageOutput(
                @"#pragma checksum ""Pages/Index.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""655d24d23dc31b880e337601cfb59ad7345bee39""
                // <auto-generated/>
                #pragma warning disable 1591
                namespace MyApp.Pages
                {
                    #line default
                    using global::System;
                    using global::System.Collections.Generic;
                    using global::System.Linq;
                    using global::System.Threading.Tasks;
                    using global::Microsoft.AspNetCore.Components;
                    #line default
                    #line hidden
                    #nullable restore
                    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
                    #nullable disable
                    {
                        #pragma warning disable 1998
                        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
                        {
                            __builder.AddMarkupContent(0, ""<h2>Hi Folks</h2>"");
                        }
                        #pragma warning restore 1998
                    }
                }
                #pragma warning restore 1591
                ",
                @"#pragma checksum ""Pages/Counter.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""0de17e526cd536d59072aa0e924e99111b16b97a""
                // <auto-generated/>
                #pragma warning disable 1591
                namespace MyApp.Pages
                {
                    #line default
                    using global::System;
                    using global::System.Collections.Generic;
                    using global::System.Linq;
                    using global::System.Threading.Tasks;
                    using global::Microsoft.AspNetCore.Components;
                    #line default
                    #line hidden
                    #nullable restore
                    public partial class Counter : global::Microsoft.AspNetCore.Components.ComponentBase
                    #nullable disable
                    {
                        #pragma warning disable 1998
                        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
                        {
                            __builder.AddMarkupContent(0, ""<h1>Counter</h1>"");
                        }
                        #pragma warning restore 1998
                    }
                }
                #pragma warning restore 1591
                ");

            // Only the first document (Index.razor) was modified
            documents = Assert.Contains("CSharpDocuments", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedSteps);
            Assert.Collection(documents,
               (o) => Assert.Equal(IncrementalStepRunReason.Modified, Assert.Single(o.Outputs).Reason),
               (o) => Assert.Equal(IncrementalStepRunReason.Cached, Assert.Single(o.Outputs).Reason)
            );

            Assert.Collection(eventListener.Events,
                e => Assert.Equal("ComputeRazorSourceGeneratorOptions", e.EventName),
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/Index.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/Index.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Index.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Index.razor", "Runtime"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_razor.g.cs"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs")
            );

            // Flip suppression on, change the compilation, no changes
            driver = SetSuppressionState(true);
            eventListener.Clear();
            RunGenerator(compilation!, ref driver).VerifyPageOutput();

            project = project.AddDocument("viewcomponent.cs", """
                public class MyViewComponent : Microsoft.AspNetCore.Mvc.ViewComponent{}
                """).Project;

            compilation = await project.GetCompilationAsync();
            RunGenerator(compilation!, ref driver).VerifyPageOutput();

            Assert.Empty(eventListener.Events);

            // Un-suppress, check that tag helper discovery runs for the compilation that changed during suppression
            driver = SetSuppressionState(false);
            result = RunGenerator(compilation!, ref driver).VerifyOutputsMatch(result);
            Assert.Collection(eventListener.Events,
               e => Assert.Equal("ComputeRazorSourceGeneratorOptions", e.EventName),
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName),
               e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"),
               e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"),
               e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"),
               e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"),
               e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_razor.g.cs"),
               e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs")
            );

            // Flip suppression on, change the parse options, no changes
            driver = SetSuppressionState(true);
            eventListener.Clear();
            project = project.WithParseOptions(((CSharpParseOptions)project.ParseOptions!).WithLanguageVersion(LanguageVersion.CSharp8));
            compilation = await project.GetCompilationAsync();
            driver = driver.WithUpdatedParseOptions(project.ParseOptions!);
            RunGenerator(compilation!, ref driver).VerifyPageOutput();

            Assert.Empty(eventListener.Events);

            // Un-suppress, ensure that we completely re-run as we now have a different language version
            driver = SetSuppressionState(false);
            result = RunGenerator(compilation!, ref driver).VerifyOutputsMatch(result);
            Assert.Collection(eventListener.Events,
                e => Assert.Equal("ComputeRazorSourceGeneratorOptions", e.EventName),
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Counter.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/Index.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/Index.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/Counter.razor"),
                e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/Counter.razor"),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromReferencesStart", e.EventName),
                e => Assert.Equal("DiscoverTagHelpersFromReferencesStop", e.EventName),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"),
                e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Index.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Index.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStart", "Pages/Counter.razor", "Runtime"),
                e => e.AssertPair("RazorCodeGenerateStop", "Pages/Counter.razor", "Runtime"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_razor.g.cs"),
                e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs")
            );

            GeneratorDriver SetSuppressionState(bool state)
            {
                var suppressedOptions = optionsProvider.Clone();
                suppressedOptions.TestGlobalOptions["build_property.SuppressRazorSourceGenerator"] = state ? "true" : "false";
                return driver.WithUpdatedAnalyzerConfigOptions(suppressedOptions);
            }
        }

        [Fact, WorkItem("https://github.com/dotnet/razor/issues/7914")]
        public async Task SourceGenerator_UppercaseRazor_GeneratesComponent()
        {
            var project = CreateTestProject(new()
            {
                ["Component.Razor"] = "<h1>Hello world</h1>",
            });

            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            var result = RunGenerator(compilation!, ref driver).VerifyPageOutput(@"
#pragma checksum ""Component.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Component : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");

            Assert.Empty(result.Diagnostics);
            Assert.Single(result.GeneratedSources);
        }

        [Theory, WorkItem("https://github.com/dotnet/razor/issues/7236")]
        [InlineData("")]
        [InlineData(" ")]
        [InlineData("\n")]
        public async Task SourceGenerator_EmptyTargetPath(string targetPath)
        {
            const string componentPath = "Component.razor";
            var project = CreateTestProject(new()
            {
                [componentPath] = "<h1>Hello world</h1>",
            });

            var compilation = await project.GetCompilationAsync();
            var (driver, _, _) = await GetDriverWithAdditionalTextAndProviderAsync(project, optionsProvider =>
            {
                optionsProvider.AdditionalTextOptions[componentPath] = new TestAnalyzerConfigOptions
                {
                    ["build_metadata.AdditionalFiles.TargetPath"] = targetPath
                };
            });

            var result = RunGenerator(compilation!, ref driver);

            var diagnostic = Assert.Single(result.Diagnostics);
            // RSG002: TargetPath not specified for additional file
            Assert.Equal("RSG002", diagnostic.Id);

            Assert.Empty(result.GeneratedSources);
        }

        [Fact]
        public async Task SourceGenerator_Class_Inside_CodeBlock()
        {
            var project = CreateTestProject(new()
            {
                ["Component.Razor"] =
"""
<h1>Hello world</h1>

@code
{
    public class X {}
}
"""
            });

            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            var result = RunGenerator(compilation!, ref driver).VerifyPageOutput(
                """
                #pragma checksum "Component.Razor" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "20b14071a74e1fd554d7b3dff6ff41722270ebee"
                // <auto-generated/>
                #pragma warning disable 1591
                namespace MyApp
                {
                    #line default
                    using global::System;
                    using global::System.Collections.Generic;
                    using global::System.Linq;
                    using global::System.Threading.Tasks;
                    using global::Microsoft.AspNetCore.Components;
                    #line default
                    #line hidden
                    #nullable restore
                    public partial class Component : global::Microsoft.AspNetCore.Components.ComponentBase
                    #nullable disable
                    {
                        #pragma warning disable 1998
                        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
                        {
                            __builder.AddMarkupContent(0, "<h1>Hello world</h1>");
                        }
                        #pragma warning restore 1998
                #nullable restore
                #line (4,2)-(6,1) "Component.Razor"
                    public class X {}
                #line default
                #line hidden
                #nullable disable
                    }
                }
                #pragma warning restore 1591

                """);

            Assert.Empty(result.Diagnostics);
            Assert.Single(result.GeneratedSources);
        }

        [Fact, WorkItem("https://github.com/dotnet/razor/issues/8850")]
        public async Task SystemFolder()
        {
            var project = CreateTestProject(new()
            {
                ["Pages/__Host.cshtml"] = """
                    @page "/"
                    @namespace MyApp.Pages
                    """,
                ["Pages/System/MyComponent.razor"] = """
                    <h1>My component</h1>
                    """,
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project);

            var result = RunGenerator(compilation!, ref driver);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);
        }

        [Theory, CombinatorialData]
        public async Task RazorLangVersion_Incorrect([CombinatorialValues("incorrect", "-1", "10000")] string langVersion)
        {
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var driver = await GetDriverAsync(project, options =>
            {
                options.TestGlobalOptions["build_property.RazorLangVersion"] = langVersion;
            });

            var result = RunGenerator(compilation!, ref driver);

            result.Diagnostics.Verify(
                // error RZ3600: Invalid value '{0}'' for RazorLangVersion. Valid values include 'Latest' or a valid version in range 1.0 to 8.0.
                Diagnostic("RZ3600").WithArguments(langVersion).WithLocation(1, 1));
            Assert.Single(result.GeneratedSources);
        }

        [Fact]
        public async Task Test_WhenEmptyOrCached()
        {
            bool flag = false;
            string[] data = ["a", "b", "c"];

            var generator = new LambdaGenerator(context =>
            {
                // simulate the flag input
                var flagInput = context.CompilationProvider.Select((c, _) => flag).WithTrackingName("FlagInput");

                // simulate some data
                var dataInput = context.CompilationProvider.Select((c, _) => data).SelectMany((c, _) => c).WithTrackingName("DataInput");

                // apply empty or cached
                var result = dataInput.EmptyOrCachedWhen(flagInput, false);

                // write out results
                context.RegisterSourceOutput(result, (spc, s) => spc.AddSource(s, ""));
            });

            var project = CreateTestProject(new());
            GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], parseOptions: (CSharpParseOptions?)project.ParseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true));

            var compilation = (await project.GetCompilationAsync())!;

            driver = driver.RunGenerators(compilation);
            var result = driver.GetRunResult();


            // check that we produced nothing
            Assert.Empty(result.GeneratedTrees);

            // now flip the switch to on and re-run
            flag = true;
            compilation = compilation.WithAssemblyName("changed");

            driver = driver.RunGenerators(compilation);
            result = driver.GetRunResult();

            Assert.Collection(result.GeneratedTrees,
                t => Assert.EndsWith("a.cs", t.FilePath),
                t => Assert.EndsWith("b.cs", t.FilePath),
                t => Assert.EndsWith("c.cs", t.FilePath)
           );

            // switch it off, make sure we get the same data
            flag = false;
            compilation = compilation.WithAssemblyName("changed2");

            driver = driver.RunGenerators(compilation);
            result = driver.GetRunResult();

            Assert.Collection(result.GeneratedTrees,
                t => Assert.EndsWith("a.cs", t.FilePath),
                t => Assert.EndsWith("b.cs", t.FilePath),
                t => Assert.EndsWith("c.cs", t.FilePath)
            );

            // now, even if we change the data, with it off, we get the old data
            data = ["a", "d", "c"];
            compilation = compilation.WithAssemblyName("changed3");

            driver = driver.RunGenerators(compilation);
            result = driver.GetRunResult();

            Assert.Collection(result.GeneratedTrees,
                t => Assert.EndsWith("a.cs", t.FilePath),
                t => Assert.EndsWith("b.cs", t.FilePath),
                t => Assert.EndsWith("c.cs", t.FilePath)
            );

            // turning it back on, we get the updated data
            flag = true;
            compilation = compilation.WithAssemblyName("changed4");

            driver = driver.RunGenerators(compilation);
            result = driver.GetRunResult();

            Assert.Collection(result.GeneratedTrees,
                t => Assert.EndsWith("a.cs", t.FilePath),
                t => Assert.EndsWith("d.cs", t.FilePath),
                t => Assert.EndsWith("c.cs", t.FilePath)
            );
        }

#pragma warning disable RS1041 // This compiler extension should not be implemented in an assembly with target framework '.NET 8.0'. References to other target frameworks will cause the compiler to behave unpredictably.
#pragma warning disable RS1038 // This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces.
        [Generator]
#pragma warning restore RS1038 // This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces.
#pragma warning restore RS1041 // This compiler extension should not be implemented in an assembly with target framework '.NET 8.0'. References to other target frameworks will cause the compiler to behave unpredictably.
#pragma warning disable RS1036 // Specify analyzer banned API enforcement setting
        public class LambdaGenerator(Action<IncrementalGeneratorInitializationContext> action) : IIncrementalGenerator
#pragma warning restore RS1036 // Specify analyzer banned API enforcement setting
        {
            public void Initialize(IncrementalGeneratorInitializationContext context) => action(context);
        }

        [Fact]
        public async Task IncrementalCompilation_NothingRuns_When_AdditionalFiles_HaveSameContent()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver);
            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);
            Assert.Empty(eventListener.Events);

            project = project.RemoveAdditionalDocument(project.AdditionalDocumentIds[1])
                             .AddAdditionalDocument("Counter.razor", SourceText.From("<h1>Counter</h1>", Encoding.UTF8))
                             .Project;

            compilation = await project.GetCompilationAsync();

            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            Assert.Empty(eventListener.Events);
        }

        [Fact]
        public async Task IncrementalCompilation_OnlyCompilationRuns_When_MetadataReferences_SameAssembly()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            result = RunGenerator(compilation!, ref driver)
                .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);
            Assert.Empty(eventListener.Events);

            var reference = (PortableExecutableReference) project.MetadataReferences[^1];

            project = project.RemoveMetadataReference(reference)
                             .AddMetadataReference(MetadataReference.CreateFromFile(reference.FilePath!));

            compilation = await project.GetCompilationAsync();

            result = RunGenerator(compilation!, ref driver)
                .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            // reference causes the compilation to change so we re-run tag helper discovery there
            // but we didn't re-check the actual reference itself
            Assert.Collection(eventListener.Events,
               e => Assert.Equal("ComputeRazorSourceGeneratorOptions", e.EventName),
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
               e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName));
        }

        [Theory]
        [InlineData("true")]
        [InlineData("True")]
        [InlineData("TRUE")]
        [InlineData("tRuE")]
        public async Task RoslynTokenizerEnabledWithTrue(string value)
        {
            var parseOptions = CSharpParseOptions.Default.WithFeatures([new("use-roslyn-tokenizer", value)]);

            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = """"
                    <div>@("""
                        nested "
                        """)</div>
                    """",
            }, cSharpParseOptions: parseOptions);

            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver);
            result.VerifyPageOutput(
                """"
                #pragma checksum "Pages/Index.razor" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "c6855f3cabbcb69477e3f5a61f8d77fcfed086c2"
                // <auto-generated/>
                #pragma warning disable 1591
                namespace MyApp.Pages
                {
                    #line default
                    using global::System;
                    using global::System.Collections.Generic;
                    using global::System.Linq;
                    using global::System.Threading.Tasks;
                    using global::Microsoft.AspNetCore.Components;
                    #line default
                    #line hidden
                    #nullable restore
                    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
                    #nullable disable
                    {
                        #pragma warning disable 1998
                        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
                        {
                            __builder.OpenElement(0, "div");
                #nullable restore
                #line (1,8)-(3,8) 24 "Pages/Index.razor"
                __builder.AddContent(1, """
                    nested "
                    """

                #line default
                #line hidden
                #nullable disable
                            );
                            __builder.CloseElement();
                        }
                        #pragma warning restore 1998
                    }
                }
                #pragma warning restore 1591

                """");
        }

        [Theory]
        [InlineData("false")]
        [InlineData("False")]
        [InlineData("FALSE")]
        [InlineData("FaLsE")]
        [InlineData("")]
        [InlineData(null)]
        public async Task RoslynTokenizerDisabledWithFalseOrNothing(string? value)
        {
            var parseOptions = CSharpParseOptions.Default;

            if (value != null)
            {
                parseOptions = parseOptions.WithFeatures([new("use-roslyn-tokenizer", value)]);
            }

            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = """"
                    <div>@("""
                        nested "
                        """)</div>
                    """",
            }, cSharpParseOptions: parseOptions);

            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts) = await GetDriverWithAdditionalTextAsync(project);

            var result = RunGenerator(compilation!, ref driver,
                // Pages/Index.razor(3,10): error CS1525: Invalid expression term '/'
                //     """)</div>
                Diagnostic(ErrorCode.ERR_InvalidExprTerm, "/").WithArguments("/").WithLocation(3, 10),
                // Pages/Index.razor(3,11): error CS0103: The name 'div' does not exist in the current context
                //     """)</div>
                Diagnostic(ErrorCode.ERR_NameNotInContext, "div").WithArguments("div").WithLocation(3, 11),
                // Pages/Index.razor(3,15): error CS1525: Invalid expression term ')'
                //     """)</div>
                Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments(")").WithLocation(3, 15),
                // Pages/Index.razor(3,15): error CS1002: ; expected
                //     """)</div>
                Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15),
                // Pages/Index.razor(3,15): error CS1513: } expected
                //     """)</div>
                Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(3, 15));

            result.VerifyPageOutput(
                """"
                #pragma checksum "Pages/Index.razor" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "c6855f3cabbcb69477e3f5a61f8d77fcfed086c2"
                // <auto-generated/>
                #pragma warning disable 1591
                namespace MyApp.Pages
                {
                    #line default
                    using global::System;
                    using global::System.Collections.Generic;
                    using global::System.Linq;
                    using global::System.Threading.Tasks;
                    using global::Microsoft.AspNetCore.Components;
                    #line default
                    #line hidden
                    #nullable restore
                    public partial class Index : global::Microsoft.AspNetCore.Components.ComponentBase
                    #nullable disable
                    {
                        #pragma warning disable 1998
                        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
                        {
                            __builder.OpenElement(0, "div");
                #nullable restore
                #line (1,8)-(3,15) 24 "Pages/Index.razor"
                __builder.AddContent(1, """
                    nested "
                    """)</div>
                #line default
                #line hidden
                #nullable disable
                            );
                            __builder.CloseElement();
                        }
                        #pragma warning restore 1998
                    }
                }
                #pragma warning restore 1591

                """");
        }

        [Fact]
        public async Task UseRazorCohostServer_CanOverride_Suppression()
        {
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Component.Razor"] = "<h1>Hello world</h1>",
            });
            var compilation = await project.GetCompilationAsync();

            // Start with the generator suppressed
            var (driver, additionalTexts, optionsProvider) = await GetDriverWithAdditionalTextAndProviderAsync(project, configureGlobalOptions: (o) =>
            {
                o.TestGlobalOptions["build_property.SuppressRazorSourceGenerator"] = "true";
            });

            // results are empty, and no recorded steps run
            var result = RunGenerator(compilation!, ref driver).VerifyPageOutput();
            Assert.DoesNotContain("DocumentsWithSuppression", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedSteps);
            Assert.DoesNotContain("ImplementationSourceOutput", (IReadOnlyDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>)result.TrackedOutputSteps);

            // now enable UseRazorCohostServer
            RazorCohostingOptions.UseRazorCohostServer = true;

            // unfortunately if we re-run we'll still be in the suppressed mode. Because nothing in the compilation has changed
            // there is no event that the source generator can 'trigger' on to re-evaluate.
            result = RunGenerator(compilation!, ref driver).VerifyPageOutput();

            // but if we make a trivial change to the compilation, we will re-run and get results
            compilation = compilation!.WithAssemblyName(compilation.AssemblyName);

            result = RunGenerator(compilation!, ref driver).VerifyPageOutput(@"
#pragma checksum ""Component.razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""6b5db227a6aa2228c777b0771108b184b1fc5df3""
// <auto-generated/>
#pragma warning disable 1591
namespace MyApp
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    #line default
    #line hidden
    #nullable restore
    public partial class Component : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, ""<h1>Hello world</h1>"");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591
");
        }

        [Fact]
        public async Task IncrementalCompilation_RerunsGenerator_When_AdditionalFileRenamed()
        {
            // Arrange
            using var eventListener = new RazorEventListener();
            var project = CreateTestProject(new()
            {
                ["Pages/Index.razor"] = "<h1>Hello world</h1>",
                ["Pages/Counter.razor"] = "<h1>Counter</h1>",
            });
            var compilation = await project.GetCompilationAsync();
            var (driver, additionalTexts, analyzerConfigOptionProvider) = await GetDriverWithAdditionalTextAndProviderAsync(project);

            var result = RunGenerator(compilation!, ref driver);
            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            eventListener.Clear();

            // Verify no changes when re-running
            result = RunGenerator(compilation!, ref driver)
                        .VerifyOutputsMatch(result);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);
            Assert.Empty(eventListener.Events);

            // Rename Counter.razor to NewCounter.razor by removing and re-adding with same content
            var counterText = additionalTexts.First(f => f.Path.EndsWith("Counter.razor", StringComparison.OrdinalIgnoreCase));
            var renamedText = new TestAdditionalText("Pages/NewCounter.razor", counterText.GetText()!);
            driver = driver.RemoveAdditionalTexts([counterText])
                          .AddAdditionalTexts([renamedText]);

            // Update the analyzer config options with the new target path
            analyzerConfigOptionProvider.AdditionalTextOptions[renamedText.Path] = new TestAnalyzerConfigOptions
            {
                ["build_metadata.AdditionalFiles.TargetPath"] = Convert.ToBase64String(Encoding.UTF8.GetBytes(renamedText.Path))
            };
            driver = driver.WithUpdatedAnalyzerConfigOptions(analyzerConfigOptionProvider);

            result = RunGenerator(compilation!, ref driver);

            Assert.Empty(result.Diagnostics);
            Assert.Equal(2, result.GeneratedSources.Length);

            // Verify the new file was processed
            Assert.Collection(eventListener.Events,
                    e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/NewCounter.razor"),
                    e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/NewCounter.razor"),
                    e => e.AssertSingleItem("GenerateDeclarationCodeStart", "/Pages/NewCounter.razor"),
                    e => e.AssertSingleItem("GenerateDeclarationCodeStop", "/Pages/NewCounter.razor"),
                    e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
                    e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName),
                    e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/NewCounter.razor"),
                    e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/NewCounter.razor"),
                    e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"),
                    e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"),
                    e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/NewCounter.razor"),
                    e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/NewCounter.razor"),
                    e => e.AssertPair("RazorCodeGenerateStart", "Pages/Index.razor", "Runtime"),
                    e => e.AssertPair("RazorCodeGenerateStop", "Pages/Index.razor", "Runtime"),
                    e => e.AssertPair("RazorCodeGenerateStart", "Pages/NewCounter.razor", "Runtime"),
                    e => e.AssertPair("RazorCodeGenerateStop", "Pages/NewCounter.razor", "Runtime"),
                    e => e.AssertSingleItem("AddSyntaxTrees", "Pages_NewCounter_razor.g.cs")
            );

            // Verify the generated source has the correct namespace and class name
            var newCounterSource = result.GeneratedSources.FirstOrDefault(s => s.HintName.Contains("NewCounter"));
            Assert.Contains("namespace MyApp.Pages", newCounterSource.SourceText.ToString());
            Assert.Contains("public partial class NewCounter", newCounterSource.SourceText.ToString());

            // Do a case-only rename and make sure we update the generated class name still
            // as component names are case sensitive even on Windows.
            var renamedText2 = new TestAdditionalText("Pages/NewCouNter.razor", counterText.GetText()!);
            driver = driver.RemoveAdditionalTexts([renamedText])
                          .AddAdditionalTexts([renamedText2]);

            // Update the analyzer config options with the new target path
            analyzerConfigOptionProvider.AdditionalTextOptions[renamedText2.Path] = new TestAnalyzerConfigOptions
            {
                ["build_metadata.AdditionalFiles.TargetPath"] = Convert.ToBase64String(Encoding.UTF8.GetBytes(renamedText2.Path))
            };
            driver = driver.WithUpdatedAnalyzerConfigOptions(analyzerConfigOptionProvider);

            result = RunGenerator(compilation!, ref driver);

            var newCouNterSource = result.GeneratedSources.FirstOrDefault(s => s.HintName.Contains("NewCouNter"));
            Assert.Contains("public partial class NewCouNter", newCouNterSource.SourceText.ToString());
        }
    }
}
