Fix terminal reconnect output pump lifetime
This commit is contained in:
parent
f2282b8619
commit
4fcbb07fdc
@ -22,6 +22,7 @@ internal sealed class HelperBackedConPtySession : IConPtySession
|
|||||||
private StreamWriter? _commandWriter;
|
private StreamWriter? _commandWriter;
|
||||||
private StreamReader? _outputReader;
|
private StreamReader? _outputReader;
|
||||||
private Task? _outputPumpTask;
|
private Task? _outputPumpTask;
|
||||||
|
private readonly CancellationTokenSource _lifetime = new();
|
||||||
private bool _started;
|
private bool _started;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ internal sealed class HelperBackedConPtySession : IConPtySession
|
|||||||
throw new InvalidOperationException($"Unexpected ConPTY helper startup handshake: {handshake}");
|
throw new InvalidOperationException($"Unexpected ConPTY helper startup handshake: {handshake}");
|
||||||
}
|
}
|
||||||
|
|
||||||
_outputPumpTask = Task.Run(() => PumpOutputAsync(_outputReader, cancellationToken), cancellationToken);
|
_outputPumpTask = Task.Run(() => PumpOutputAsync(_outputReader, _lifetime.Token), CancellationToken.None);
|
||||||
_started = true;
|
_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +150,7 @@ internal sealed class HelperBackedConPtySession : IConPtySession
|
|||||||
}
|
}
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
_lifetime.Cancel();
|
||||||
|
|
||||||
if (_commandWriter is not null)
|
if (_commandWriter is not null)
|
||||||
{
|
{
|
||||||
@ -182,6 +184,7 @@ internal sealed class HelperBackedConPtySession : IConPtySession
|
|||||||
_commandPipe?.Dispose();
|
_commandPipe?.Dispose();
|
||||||
_outputPipe?.Dispose();
|
_outputPipe?.Dispose();
|
||||||
_helperProcess?.Dispose();
|
_helperProcess?.Dispose();
|
||||||
|
_lifetime.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PumpOutputAsync(StreamReader reader, CancellationToken cancellationToken)
|
private async Task PumpOutputAsync(StreamReader reader, CancellationToken cancellationToken)
|
||||||
|
|||||||
@ -44,6 +44,46 @@ public sealed class TerminalSmokeCheckTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BuiltAgentExe_Reconnects_Existing_Terminal_And_Accepts_Input()
|
||||||
|
{
|
||||||
|
if (!OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await using var fixture = new BuiltAgentFixture();
|
||||||
|
await fixture.StartAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var session = await fixture.CreateSessionAsync("smoke-reconnect");
|
||||||
|
|
||||||
|
using (var firstSocket = await fixture.ConnectTerminalAsync(session.SessionId))
|
||||||
|
{
|
||||||
|
_ = await fixture.ReceiveTextAsync(firstSocket, TimeSpan.FromSeconds(20));
|
||||||
|
await fixture.SendTextAsync(firstSocket, JsonSerializer.Serialize(new { type = "input", input = "Write-Output first\r" }));
|
||||||
|
_ = await fixture.ReceiveTextContainingAsync(firstSocket, "first", TimeSpan.FromSeconds(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
using var secondSocket = await fixture.ConnectTerminalAsync(session.SessionId);
|
||||||
|
var attached = await fixture.ReceiveTextAsync(secondSocket, TimeSpan.FromSeconds(20));
|
||||||
|
var attachedPayload = JsonSerializer.Deserialize<TerminalAttachResponse>(attached, new JsonSerializerOptions(JsonSerializerDefaults.Web));
|
||||||
|
Assert.NotNull(attachedPayload);
|
||||||
|
Assert.Equal("attached", attachedPayload!.Type);
|
||||||
|
Assert.Equal(session.SessionId, attachedPayload.SessionId);
|
||||||
|
|
||||||
|
await fixture.SendTextAsync(secondSocket, JsonSerializer.Serialize(new { type = "input", input = "Write-Output second\r" }));
|
||||||
|
var output = await fixture.ReceiveTextContainingAsync(secondSocket, "second", TimeSpan.FromSeconds(20));
|
||||||
|
|
||||||
|
Assert.Contains("second", output, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"{ex.Message}{Environment.NewLine}{fixture.GetDiagnostics()}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class BuiltAgentFixture : IAsyncDisposable
|
private sealed class BuiltAgentFixture : IAsyncDisposable
|
||||||
{
|
{
|
||||||
private readonly string _projectRoot;
|
private readonly string _projectRoot;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user