/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Components/Testing/nativeaot/NativeAotTestHarnessGenerator.cs
175 строк
8 KB
Javier Calvarro Nelson
[Blazor] Add Native AOT Components testing harness (#68289)
10 авг 2026, 15:11
Не верифицирован
10 авг 2026, 15:11
330e89c
Код
Авторство
О чём код?
// 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) { } } } } """; }