ci: validate macOS managed behavior on Ubuntu and stabilize shutdown
PR and Push Build/Test / build-and-test (pull_request) Blocked by required conditions
PR and Push Build/Test / portable-build-and-test (pull_request) Blocked by required conditions

This commit is contained in:
dh
2026-10-03 11:30:29 +02:00
parent bd35ebc4c8
commit 1164c26846
10 changed files with 201 additions and 72 deletions
@@ -3,11 +3,92 @@ using MeetingAssistant.LaunchProfiles;
using MeetingAssistant.Transcription;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;
using System.Runtime.ExceptionServices;
namespace MeetingAssistant.Tests;
public sealed class PyannoteDiarizationWarmupHostedServiceTests
{
[Fact]
public async Task HostedServiceStopsSafelyWhenShutdownRequestsOverlap()
{
const int iterations = 64;
const int shutdownCallers = 8;
using var applicationLifetime = new CancellationTokenSource();
var finalizer = new PyannoteTranscriptFinalizer(
new BlockingCommandRunner(),
Options.Create(new MeetingAssistantOptions()),
NullLogger<PyannoteTranscriptFinalizer>.Instance);
using var callersReady = new Barrier(shutdownCallers + 1);
var shutdownErrors = new ConcurrentQueue<ExceptionDispatchInfo>();
PyannoteDiarizationWarmupHostedService? service = null;
var shutdownTasks = Enumerable.Range(0, shutdownCallers)
.Select(_ => Task.Factory.StartNew(() =>
{
try
{
for (var iteration = 0; iteration < iterations; iteration++)
{
WaitForShutdownCallers();
try
{
service!.StopAsync(CancellationToken.None)
.WaitAsync(TimeSpan.FromSeconds(5)).GetAwaiter().GetResult();
}
catch (Exception exception)
{
shutdownErrors.Enqueue(ExceptionDispatchInfo.Capture(exception));
}
WaitForShutdownCallers();
if (!shutdownErrors.IsEmpty)
{
break;
}
}
}
catch (Exception exception)
{
shutdownErrors.Enqueue(ExceptionDispatchInfo.Capture(exception));
}
}, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default))
.ToArray();
try
{
for (var iteration = 0; iteration < iterations; iteration++)
{
service = new PyannoteDiarizationWarmupHostedService(
finalizer,
new FakeLaunchProfileOptionsProvider(new MeetingAssistantOptions()),
NullLogger<PyannoteDiarizationWarmupHostedService>.Instance);
await service.StartAsync(applicationLifetime.Token);
WaitForShutdownCallers();
WaitForShutdownCallers();
if (!shutdownErrors.IsEmpty)
{
break;
}
}
}
finally
{
await Task.WhenAll(shutdownTasks).WaitAsync(TimeSpan.FromSeconds(10));
}
if (shutdownErrors.TryPeek(out var shutdownError))
{
shutdownError.Throw();
}
void WaitForShutdownCallers()
{
Assert.True(
callersReady.SignalAndWait(TimeSpan.FromSeconds(5)),
"Timed out coordinating overlapping shutdown requests.");
}
}
[Fact]
public async Task HostedServiceWarmsEnabledValidationRuntimeWithoutBlockingStartup()
{