Skip to content

Add more gRPC interop test logging #61532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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