using System.Text; using DodoSSH.Client.Ssh; using NSubstitute; namespace DodoSSH.Client.Terminal.Tests; /// /// The renderer gate: the one place the workspace waits on something outside the process. /// /// /// The gate itself is not in question. drops frames when nothing /// is attached, so a session opened before the renderer arrives loses its SessionOpened frame and /// streams output at a terminal that was never created — and that it opens when a renderer does attach is /// covered by . What is worth a test here is the /// half that used to be missing: waiting for a renderer that never arrives has to end. /// public sealed class TerminalWorkspaceTests { [Fact] public async Task WaitingForARendererThatNeverAttaches_GivesUp() { // The shipped failure this stands in for is a WebView2 that never initialises — no Evergreen // runtime, an install blocked by policy, an AppContainer that cannot reach loopback. From this // side they are identical and all look like the listener being up with nothing ever connecting // to it. Before the wait was bounded this test would have hung instead of failing. await using var workspace = CreateWorkspace(TimeSpan.FromMilliseconds(250)); workspace.Start(); await Should.ThrowAsync(async () => await workspace.WaitForRendererAsync(TestContext.Current.CancellationToken)); } [Fact] public async Task WaitingForARenderer_ObeysItsCancellationToken() { // The timeout is the backstop; the caller's token is what makes a Connect the user gave up on // return at once rather than sitting out the rest of the wait. The timeout here is long enough // that only cancellation can end this. await using var workspace = CreateWorkspace(TimeSpan.FromMinutes(5)); workspace.Start(); using var cancellation = new CancellationTokenSource(); var wait = workspace.WaitForRendererAsync(cancellation.Token); await cancellation.CancelAsync(); await Should.ThrowAsync(async () => await wait); } /// /// The connection factory is never reached: every test here stops at the gate, and reaching a real /// host would make this a network test. /// private static TerminalWorkspace CreateWorkspace(TimeSpan rendererTimeout) => new( new InMemoryTerminalAssetProvider( new Dictionary(StringComparer.Ordinal) { [TerminalDataPlane.PagePath] = new( "text/html; charset=utf-8", Encoding.UTF8.GetBytes("")), }), Substitute.For(), TimeProvider.System, new TerminalWorkspaceOptions { RendererTimeout = rendererTimeout }); }