mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Improve visibility for AzureFunctions Workflows samples run tests in increase timeouts (#4820)
* Reduce timeout flakiness for AzureFunctions Workflows samples run tests * Add more updates * Address PR comments * Address PR comments
This commit is contained in:
+65
-3
@@ -141,6 +141,10 @@ public abstract class SamplesValidationBase : IAsyncLifetime
|
||||
{
|
||||
string uniqueTaskHubName = $"{this.TaskHubPrefix}-{Guid.NewGuid():N}"[..^26];
|
||||
|
||||
// Build the sample project first so that build failures are caught immediately
|
||||
// instead of silently failing inside 'dotnet run' and causing a timeout.
|
||||
await this.BuildSampleAsync(samplePath);
|
||||
|
||||
using BlockingCollection<OutputLog> logsContainer = [];
|
||||
using Process appProcess = this.StartConsoleApp(samplePath, logsContainer, uniqueTaskHubName);
|
||||
|
||||
@@ -154,7 +158,11 @@ public abstract class SamplesValidationBase : IAsyncLifetime
|
||||
}
|
||||
finally
|
||||
{
|
||||
logsContainer.CompleteAdding();
|
||||
if (!logsContainer.IsAddingCompleted)
|
||||
{
|
||||
logsContainer.CompleteAdding();
|
||||
}
|
||||
|
||||
await this.StopProcessAsync(appProcess);
|
||||
}
|
||||
}
|
||||
@@ -329,12 +337,56 @@ public abstract class SamplesValidationBase : IAsyncLifetime
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BuildSampleAsync(string samplePath)
|
||||
{
|
||||
this.OutputHelper.WriteLine($"Building sample at {samplePath}...");
|
||||
|
||||
ProcessStartInfo buildInfo = new()
|
||||
{
|
||||
FileName = "dotnet",
|
||||
Arguments = $"build --framework {DotnetTargetFramework}",
|
||||
WorkingDirectory = samplePath,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
};
|
||||
|
||||
using Process buildProcess = new() { StartInfo = buildInfo };
|
||||
buildProcess.Start();
|
||||
|
||||
// Read both streams asynchronously to avoid deadlocks from filled pipe buffers
|
||||
Task<string> stdoutTask = buildProcess.StandardOutput.ReadToEndAsync();
|
||||
Task<string> stderrTask = buildProcess.StandardError.ReadToEndAsync();
|
||||
|
||||
using CancellationTokenSource buildCts = new(TimeSpan.FromMinutes(5));
|
||||
try
|
||||
{
|
||||
await buildProcess.WaitForExitAsync(buildCts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
buildProcess.Kill(entireProcessTree: true);
|
||||
throw new TimeoutException($"Build timed out after 5 minutes for sample at {samplePath}.");
|
||||
}
|
||||
|
||||
await Task.WhenAll(stdoutTask, stderrTask);
|
||||
|
||||
string stdout = stdoutTask.Result;
|
||||
string stderr = stderrTask.Result;
|
||||
if (buildProcess.ExitCode != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to build sample at {samplePath}:\n{stdout}\n{stderr}");
|
||||
}
|
||||
|
||||
this.OutputHelper.WriteLine($"Build completed for {samplePath}.");
|
||||
}
|
||||
|
||||
private Process StartConsoleApp(string samplePath, BlockingCollection<OutputLog> logs, string taskHubName)
|
||||
{
|
||||
ProcessStartInfo startInfo = new()
|
||||
{
|
||||
FileName = "dotnet",
|
||||
Arguments = $"run --framework {DotnetTargetFramework}",
|
||||
Arguments = $"run --no-build --framework {DotnetTargetFramework}",
|
||||
WorkingDirectory = samplePath,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
@@ -360,11 +412,21 @@ public abstract class SamplesValidationBase : IAsyncLifetime
|
||||
|
||||
this.ConfigureAdditionalEnvironmentVariables(startInfo, SetAndLogEnvironmentVariable);
|
||||
|
||||
Process process = new() { StartInfo = startInfo };
|
||||
Process process = new() { StartInfo = startInfo, EnableRaisingEvents = true };
|
||||
|
||||
process.ErrorDataReceived += (sender, e) => this.HandleProcessOutput(e.Data, startInfo.FileName, "err", LogLevel.Error, logs);
|
||||
process.OutputDataReceived += (sender, e) => this.HandleProcessOutput(e.Data, startInfo.FileName, "out", LogLevel.Information, logs);
|
||||
|
||||
// When the process exits unexpectedly (e.g. build failure), complete the log collection
|
||||
// so that ReadLogLine returns null immediately instead of blocking until the test timeout.
|
||||
process.Exited += (sender, e) =>
|
||||
{
|
||||
if (!logs.IsAddingCompleted)
|
||||
{
|
||||
logs.CompleteAdding();
|
||||
}
|
||||
};
|
||||
|
||||
if (!process.Start())
|
||||
{
|
||||
throw new InvalidOperationException("Failed to start the console app");
|
||||
|
||||
Reference in New Issue
Block a user