72 lines
2.4 KiB
C#
72 lines
2.4 KiB
C#
using System.Windows.Threading;
|
|
|
|
namespace VideoDownloader.Tests.Infrastructure;
|
|
|
|
/// <summary>
|
|
/// WPF/WebView2 E2E 테스트용 STA 디스패처. 전용 STA 스레드에서 Dispatcher.Run() 으로
|
|
/// 메시지 펌프를 돌리며, 테스트 코드는 InvokeAsync 로 UI 스레드에 작업을 올린다.
|
|
/// WebView2 이벤트(비동기)는 UI 스레드 디스패처에서 발생하므로 async 흐름도 이 펌프 위에서 완결된다.
|
|
/// </summary>
|
|
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<T> InvokeAsync<T>(Func<Task<T>> func)
|
|
=> Dispatcher.InvokeAsync(func).Task.Unwrap();
|
|
|
|
public Task InvokeAsync(Func<Task> func)
|
|
=> Dispatcher.InvokeAsync(func).Task.Unwrap();
|
|
|
|
public T Invoke<T>(Func<T> func) => Dispatcher.Invoke(func);
|
|
|
|
public void Invoke(Action action) => Dispatcher.Invoke(action);
|
|
|
|
public void Dispose()
|
|
{
|
|
try { Dispatcher.InvokeShutdown(); } catch { }
|
|
try { _thread.Join(5000); } catch { }
|
|
}
|
|
}
|
|
|
|
/// <summary>WebView2 비동기 초기화/이벤트 대기 공통 유틸.</summary>
|
|
public static class Wait
|
|
{
|
|
public static async Task<T> Until<T>(Func<Task<T>> poll, Func<T, bool> 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 초과)");
|
|
}
|
|
}
|