File: NativeAotTestHarnessGenerator.cs
Web Access
Project: src\aspnetcore\src\Components\Testing\nativeaot\Microsoft.AspNetCore.Components.Testing.NativeAot.csproj (Microsoft.AspNetCore.Components.Testing.NativeAot)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using Microsoft.CodeAnalysis;
 
namespace Microsoft.AspNetCore.Components.Testing.NativeAot;
 
[Generator]
internal sealed class NativeAotTestHarnessGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var enabled = context.AnalyzerConfigOptionsProvider.Select(static (options, _) =>
            options.GlobalOptions.TryGetValue("build_property.E2ECompileTestHarness", out var value) &&
            string.Equals(value, "true", StringComparison.OrdinalIgnoreCase));
 
        context.RegisterSourceOutput(enabled, static (productionContext, isEnabled) =>
        {
            if (isEnabled)
            {
                productionContext.AddSource("NativeAotTestHarness.g.cs", HarnessSource);
            }
        });
    }
 
    private const string HarnessSource = """
        // <auto-generated/>
        #nullable enable
 
        [assembly: global::Microsoft.AspNetCore.Hosting.HostingStartupAttribute(
            typeof(global::Microsoft.AspNetCore.Components.Testing.NativeAot.Generated.NativeAotTestHarnessHostingStartup))]
 
        namespace Microsoft.AspNetCore.Components.Testing.NativeAot.Generated
        {
            internal sealed class NativeAotTestHarnessHostingStartup : global::Microsoft.AspNetCore.Hosting.IHostingStartup
            {
                public void Configure(global::Microsoft.AspNetCore.Hosting.IWebHostBuilder builder)
                {
                    builder.ConfigureServices(
                        static services => E2ETestHarnessServiceCollectionExtensions.AddE2ETestHarness(services));
                }
            }
 
            public static class E2ETestHarnessServiceCollectionExtensions
            {
                public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddE2ETestHarness(
                    this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
                {
                    global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<
                        global::Microsoft.Extensions.Hosting.IHostedService>(
                            services,
                            static provider => new ReadinessNotificationService(
                                global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<
                                    global::Microsoft.Extensions.Hosting.IHostApplicationLifetime>(provider)));
                    global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<
                        global::Microsoft.Extensions.Hosting.IHostedService>(
                            services,
                            static provider => new ParentProcessMonitor(
                                global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<
                                    global::Microsoft.Extensions.Hosting.IHostApplicationLifetime>(provider)));
                    return services;
                }
            }
 
            internal sealed class ReadinessNotificationService : global::Microsoft.Extensions.Hosting.IHostedService
            {
                private readonly global::Microsoft.Extensions.Hosting.IHostApplicationLifetime _lifetime;
 
                public ReadinessNotificationService(global::Microsoft.Extensions.Hosting.IHostApplicationLifetime lifetime)
                {
                    _lifetime = lifetime;
                }
 
                public global::System.Threading.Tasks.Task StartAsync(
                    global::System.Threading.CancellationToken cancellationToken)
                {
                    var readyUrl = global::System.Environment.GetEnvironmentVariable("E2E_READY_URL");
                    if (!global::System.String.IsNullOrEmpty(readyUrl))
                    {
                        _lifetime.ApplicationStarted.Register(
                            static state => _ = NotifyReadyAsync((string)state!),
                            readyUrl);
                    }
 
                    return global::System.Threading.Tasks.Task.CompletedTask;
                }
 
                public global::System.Threading.Tasks.Task StopAsync(
                    global::System.Threading.CancellationToken cancellationToken)
                    => global::System.Threading.Tasks.Task.CompletedTask;
 
                private static async global::System.Threading.Tasks.Task NotifyReadyAsync(string readyUrl)
                {
                    var delay = global::System.TimeSpan.FromMilliseconds(200);
                    for (var attempt = 0; attempt < 5; attempt++)
                    {
                        try
                        {
                            using var client = new global::System.Net.Http.HttpClient
                            {
                                Timeout = global::System.TimeSpan.FromSeconds(5),
                            };
                            using var response = await client.PostAsync(readyUrl, content: null).ConfigureAwait(false);
                            if (response.IsSuccessStatusCode)
                            {
                                return;
                            }
                        }
                        catch
                        {
                        }
 
                        if (attempt < 4)
                        {
                            await global::System.Threading.Tasks.Task.Delay(delay).ConfigureAwait(false);
                            delay = global::System.TimeSpan.FromMilliseconds(delay.TotalMilliseconds * 2);
                        }
                    }
                }
            }
 
            internal sealed class ParentProcessMonitor : global::Microsoft.Extensions.Hosting.BackgroundService
            {
                private readonly global::Microsoft.Extensions.Hosting.IHostApplicationLifetime _lifetime;
                private readonly int _parentProcessId;
 
                public ParentProcessMonitor(global::Microsoft.Extensions.Hosting.IHostApplicationLifetime lifetime)
                {
                    _lifetime = lifetime;
                    _parentProcessId = global::System.Int32.TryParse(
                        global::System.Environment.GetEnvironmentVariable("TEST_PARENT_PID"),
                        out var parentProcessId)
                            ? parentProcessId
                            : -1;
                }
 
                protected override async global::System.Threading.Tasks.Task ExecuteAsync(
                    global::System.Threading.CancellationToken stoppingToken)
                {
                    if (_parentProcessId <= 0)
                    {
                        return;
                    }
 
                    try
                    {
                        using var parent = global::System.Diagnostics.Process.GetProcessById(_parentProcessId);
                        while (!stoppingToken.IsCancellationRequested)
                        {
                            if (parent.HasExited)
                            {
                                _lifetime.StopApplication();
                                return;
                            }
 
                            await global::System.Threading.Tasks.Task.Delay(1000, stoppingToken).ConfigureAwait(false);
                        }
                    }
                    catch (global::System.ArgumentException)
                    {
                        _lifetime.StopApplication();
                    }
                    catch (global::System.InvalidOperationException)
                    {
                        _lifetime.StopApplication();
                    }
                    catch (global::System.OperationCanceledException)
                    {
                    }
                }
            }
        }
        """;
}