Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Grpc/Interop/test/InteropTests/Helpers/ClientProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public ClientProcess(ITestOutputHelper output, string path, string serverPort, s
_process.EnableRaisingEvents = true;
_process.OutputDataReceived += Process_OutputDataReceived;
_process.ErrorDataReceived += Process_ErrorDataReceived;

output.WriteLine($"Starting process: {ProcessDebugHelper.GetDebugCommand(_process.StartInfo)}");
_process.Start();

_processEx = new ProcessEx(output, _process, timeout: Timeout.InfiniteTimeSpan);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace InteropTests.Helpers;

public static class ProcessDebugHelper
{
public static string GetDebugCommand(ProcessStartInfo psi)
{
// Quote the file name if it contains spaces or special characters
var fileName = QuoteIfNeeded(psi.FileName);

// Arguments are typically already passed as a single string
var arguments = psi.Arguments;

return $"{fileName} {arguments}".Trim();
}

private static string QuoteIfNeeded(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return "\"\"";
}

// Add quotes if value contains spaces or special characters
if (value.Contains(' ') || value.Contains('"'))
{
return $"\"{value.Replace("\"", "\\\"")}\"";
}

return value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public WebsiteProcess(string path, ITestOutputHelper output)
_process.EnableRaisingEvents = true;
_process.OutputDataReceived += Process_OutputDataReceived;
_process.ErrorDataReceived += Process_ErrorDataReceived;

output.WriteLine($"Starting process: {ProcessDebugHelper.GetDebugCommand(_process.StartInfo)}");
_process.Start();

_processEx = new ProcessEx(output, _process, Timeout.InfiniteTimeSpan);
Expand Down
43 changes: 41 additions & 2 deletions src/Grpc/Interop/test/InteropTests/InteropTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace InteropTests;

// All interop test cases, minus GCE authentication specific tests.
// Tests are separate methods so that they can be quarantined separately.
[Retry]
public class InteropTests
{
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(100);
Expand Down Expand Up @@ -83,13 +84,48 @@ public InteropTests(ITestOutputHelper output)

private async Task InteropTestCase(string name)
{
// Building interop tests processes can be flaky. Sometimes it times out.
// To mitigate this, we retry the test case a few times on timeout.
const int maxRetries = 3;
var attempt = 0;

while (true)
{
attempt++;

try
{
await InteropTestCaseCore(name);
break; // Exit loop on success
}
catch (TimeoutException ex)
{
_output.WriteLine($"Attempt {attempt} failed: {ex.Message}");

if (attempt == maxRetries)
{
_output.WriteLine("Maximum retry attempts reached. Giving up.");
throw;
}
else
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
}
}

private async Task InteropTestCaseCore(string name)
{
_output.WriteLine($"Starting {nameof(WebsiteProcess)}.");
using (var serverProcess = new WebsiteProcess(_serverPath, _output))
{
try
{
_output.WriteLine($"Waiting for {nameof(WebsiteProcess)} to be ready.");
await serverProcess.WaitForReady().TimeoutAfter(DefaultTimeout);
}
catch (Exception ex)
catch (Exception ex) when (ex is not TimeoutException)
{
var errorMessage = $@"Error while running server process.

Expand All @@ -102,17 +138,20 @@ private async Task InteropTestCase(string name)
throw new InvalidOperationException(errorMessage, ex);
}

_output.WriteLine($"Starting {nameof(ClientProcess)}.");
using (var clientProcess = new ClientProcess(_output, _clientPath, serverProcess.ServerPort, name))
{
try
{
_output.WriteLine($"Waiting for {nameof(ClientProcess)} to be ready.");
await clientProcess.WaitForReadyAsync().TimeoutAfter(DefaultTimeout);

_output.WriteLine($"Waiting for {nameof(ClientProcess)} to exit.");
await clientProcess.WaitForExitAsync().TimeoutAfter(DefaultTimeout);

Assert.Equal(0, clientProcess.ExitCode);
}
catch (Exception ex)
catch (Exception ex) when (ex is not TimeoutException)
{
var errorMessage = $@"Error while running client process.

Expand Down
Loading