using System.Windows.Threading;
namespace VideoDownloader.Tests.Infrastructure;
///
/// WPF/WebView2 E2E 테스트용 STA 디스패처. 전용 STA 스레드에서 Dispatcher.Run() 으로
/// 메시지 펌프를 돌리며, 테스트 코드는 InvokeAsync 로 UI 스레드에 작업을 올린다.
/// WebView2 이벤트(비동기)는 UI 스레드 디스패처에서 발생하므로 async 흐름도 이 펌프 위에서 완결된다.
///
public sealed class StaDispatcher : IDisposable
{
private readonly Thread _thread;
private readonly TaskCompletionSource _ready = new(TaskCreationOptions.RunContinuationsAsynchronously);
private Dispatcher? _dispatcher;
private StaDispatcher(Thread thread)
{
_thread = thread;
}
public Dispatcher Dispatcher =>
_dispatcher ?? throw new InvalidOperationException("STA dispatcher not started");
public static StaDispatcher Start()
{
var sta = default(StaDispatcher)!;
var thread = new Thread(() =>
{
sta._dispatcher = Dispatcher.CurrentDispatcher;
sta._ready.TrySetResult();
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
sta = new StaDispatcher(thread);
thread.Start();
sta._ready.Task.Wait(5000);
return sta;
}
public Task InvokeAsync(Func> func)
=> Dispatcher.InvokeAsync(func).Task.Unwrap();
public Task InvokeAsync(Func func)
=> Dispatcher.InvokeAsync(func).Task.Unwrap();
public T Invoke(Func func) => Dispatcher.Invoke(func);
public void Invoke(Action action) => Dispatcher.Invoke(action);
public void Dispose()
{
try { Dispatcher.InvokeShutdown(); } catch { }
try { _thread.Join(5000); } catch { }
}
}
/// WebView2 비동기 초기화/이벤트 대기 공통 유틸.
public static class Wait
{
public static async Task Until(Func> poll, Func done, int timeoutMs = 15000, int intervalMs = 100)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
while (sw.ElapsedMilliseconds < timeoutMs)
{
var v = await poll();
if (done(v)) return v;
await Task.Delay(intervalMs);
}
throw new TimeoutException($"조건 미충족 ({timeoutMs}ms 초과)");
}
}