| File: TestServices\NativeAuxiliaryTestHost.cs | Web Access |
| Project: src\tests\Aspire.Hosting.Native.Core.Tests\Aspire.Hosting.Native.Core.Tests.csproj (Aspire.Hosting.Native.Core.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Net.Sockets; using System.Text.Json; using Aspire.Hosting.Native.Api; using Aspire.Hosting.Native.Auxiliary; using Aspire.Hosting.Native.Cli; using Aspire.Hosting.Native.Server; using Aspire.Hosting.Native.Rpc; using Microsoft.Extensions.Logging.Abstractions; using StreamJsonRpc; namespace Aspire.Hosting.Native.Core.Tests.TestServices; internal sealed class NativeAuxiliaryTestHost : IAsyncDisposable { private readonly CancellationTokenSource _lifetime = new(); private readonly NativeAuxiliaryBackchannel _protocol; private readonly List<JsonRpc> _clients = []; private readonly Task _run; public string HomeDirectory { get; } public string AppHostPath { get; } public string SocketPath => _protocol.SocketPath; public TaskCompletionSource Ready { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously); public int? ExitCode { get; private set; } public TaskCompletionSource Stopped { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously); public NativeCliLogBuffer Logs { get; } = new(10); public NativeAuxiliaryTestHost(NativeApplicationServer server) : this(server, new NativeServerOptions()) { } public NativeAuxiliaryTestHost(NativeApplicationServer server, NativeServerOptions options) : this(server, options, new NativeBackchannelRpc(options, NullLogger.Instance)) { } public NativeAuxiliaryTestHost(NativeApplicationServer server, NativeServerOptions options, INativeBackchannelRpc rpc) { // MTP starts in the deeply nested test output directory. Allocate under the repository // instead so the shared compact socket still fits macOS's 104-byte AF_UNIX limit. var root = new DirectoryInfo(Environment.CurrentDirectory); while (!File.Exists(Path.Combine(root.FullName, "Aspire.slnx"))) { root = root.Parent ?? throw new InvalidOperationException("Repository root not found."); } HomeDirectory = Path.Combine(root.FullName, "artifacts", "aux", Guid.NewGuid().ToString("N")[..8]); Directory.CreateDirectory(HomeDirectory); AppHostPath = Path.Combine(HomeDirectory, "apphost.mts"); _protocol = new(AppHostPath, HomeDirectory, server, options, code => { ExitCode = code; Stopped.TrySetResult(); _lifetime.Cancel(); }, Ready.Task.WaitAsync, _ => Task.FromResult(new NativeCliDashboardUrls()), () => null, Logs, new(null, null, null, null), rpc, NullLogger.Instance); _run = _protocol.RunAsync(_lifetime.Token); } public JsonRpc Connect() { var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); socket.Connect(new UnixDomainSocketEndPoint(SocketPath)); var stream = new NetworkStream(socket, ownsSocket: true); var formatter = new SystemTextJsonFormatter { JsonSerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNameCaseInsensitive = true } }; var rpc = new JsonRpc(new HeaderDelimitedMessageHandler(stream, stream, formatter)); rpc.StartListening(); _clients.Add(rpc); return rpc; } public async ValueTask DisposeAsync() { foreach (var client in _clients) { client.Dispose(); } await _lifetime.CancelAsync(); try { await _run.WaitAsync(TimeSpan.FromSeconds(10)); } finally { _protocol.Dispose(); _lifetime.Dispose(); Directory.Delete(HomeDirectory, recursive: true); } } }