Skip to content

Use Process.RunAndCaptureText in src/tests child-process helpers#129779

Open
AaronRobinsonMSFT wants to merge 14 commits into
dotnet:mainfrom
AaronRobinsonMSFT:update_child_proc_in_tests
Open

Use Process.RunAndCaptureText in src/tests child-process helpers#129779
AaronRobinsonMSFT wants to merge 14 commits into
dotnet:mainfrom
AaronRobinsonMSFT:update_child_proc_in_tests

Conversation

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Adopts the new .NET 11 Process.RunAndCaptureText API across src/tests C# tests/helpers that followed the "launch a child process, wait for exit, read exit code + full stdout/stderr" pattern (no stdin, no live process control).

Converted:

  • Loader/PlatformNativeR2R/PlatformNativeR2R.cs
  • nativeaot/AggregateExecutableLibrary/ValidatorExe/ValidatorExe.cs
  • GC/Stress/Framework/RFLogging.cs (WriteToReport)
  • baseservices/exceptions/unhandled/unhandledTester.cs
  • baseservices/exceptions/stackoverflow/stackoverflowtester.cs
  • Loader/binding/tracing/BinderTracingTest.cs
  • profiler/common/ProfilerTestRunner.cs
  • nativeaot/SmokeTests/DwarfDump/Program.cs
  • tracing/userevents/common/UserEventsRequirements.cs
  • GC/Stress/Framework/ReliabilityFramework.cs (AddFailure)
  • GC/LargeMemory/memcheck.cs (RunCommand)
  • Common/CoreCLRTestLibrary/CoreclrTestWrapperLib.cs (pgrep + wmic)
  • Common/XUnitLogChecker/XUnitLogChecker.cs (RunProcess)

Call sites that write to stdin, coordinate with a live process (PID/signals/EventPipe), or rely on sudo/root process-tree kills were intentionally left unchanged.

Note

This PR description was generated with the assistance of GitHub Copilot.

AaronRobinsonMSFT and others added 13 commits June 23, 2026 13:55
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eText

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…aptureText

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the area-Infrastructure-coreclr Only use for closed issues label Jun 23, 2026
@AaronRobinsonMSFT AaronRobinsonMSFT changed the title Use Process.RunAndCaptureText in src/tests child-process helpers Use Process.RunAndCaptureText in src/tests child-process helpers Jun 23, 2026
@AaronRobinsonMSFT AaronRobinsonMSFT added test-enhancement Improvements of test source code area-Infrastructure and removed area-Infrastructure-coreclr Only use for closed issues labels Jun 23, 2026
@AaronRobinsonMSFT AaronRobinsonMSFT added this to the 11.0.0 milestone Jun 23, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/runtime-infrastructure
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors multiple src/tests helpers and test utilities to use System.Diagnostics.Process.RunAndCaptureText / ProcessTextOutput for the common “run child process, capture full stdout/stderr, check exit code” pattern.

Changes:

  • Replaces ad-hoc Process.Start + async stream reads / event handlers with Process.RunAndCaptureText(...) across several test areas.
  • Updates call sites to consume ProcessTextOutput.StandardOutput / .StandardError and result.ExitStatus.ExitCode (and timeout cancellation where applicable).
  • Simplifies a few helpers by removing manual WaitForExit / ReadToEndAsync patterns.
Show a summary per file
File Description
src/tests/tracing/userevents/common/UserEventsRequirements.cs Switches tracefs/user_events/ldd checks to RunAndCaptureText and prints captured output lines.
src/tests/profiler/common/ProfilerTestRunner.cs Refactors profilee execution to RunAndCaptureText and verifies captured stdout via TextReader.
src/tests/nativeaot/SmokeTests/DwarfDump/Program.cs Uses RunAndCaptureText for the llvm-dwarfdump --verify invocation and parses captured output.
src/tests/nativeaot/AggregateExecutableLibrary/ValidatorExe/ValidatorExe.cs Simplifies ValidatorExe process execution using captured stdout/stderr and exit code.
src/tests/Loader/PlatformNativeR2R/PlatformNativeR2R.cs Uses captured stdout/stderr for R2RDump execution and failure reporting.
src/tests/Loader/binding/tracing/BinderTracingTest.cs Runs subprocess via RunAndCaptureText and writes captured stdout/stderr to the console.
src/tests/GC/Stress/Framework/RFLogging.cs Updates status-reporting script invocation to RunAndCaptureText.
src/tests/GC/Stress/Framework/ReliabilityFramework.cs Updates harness status reporting invocation to RunAndCaptureText.
src/tests/GC/LargeMemory/memcheck.cs Uses RunAndCaptureText for a simple command execution helper.
src/tests/Common/XUnitLogChecker/XUnitLogChecker.cs Replaces manual timeout/kill/read logic with RunAndCaptureText(..., timeout) and checks ExitStatus.Canceled.
src/tests/Common/CoreCLRTestLibrary/CoreclrTestWrapperLib.cs Uses RunAndCaptureText for pgrep and wmic helpers, capturing output text directly.
src/tests/baseservices/exceptions/unhandled/unhandledTester.cs Refactors subprocess execution to RunAndCaptureText and parses captured stderr lines.
src/tests/baseservices/exceptions/stackoverflow/stackoverflowtester.cs Refactors subprocess execution to RunAndCaptureText and parses captured stderr lines.

Copilot's findings

  • Files reviewed: 13/13 changed files
  • Comments generated: 6

Comment thread src/tests/profiler/common/ProfilerTestRunner.cs
Comment thread src/tests/GC/Stress/Framework/RFLogging.cs
Comment thread src/tests/GC/Stress/Framework/ReliabilityFramework.cs
Comment thread src/tests/baseservices/exceptions/unhandled/unhandledTester.cs
Comment thread src/tests/GC/Stress/Framework/RFLogging.cs Outdated
Process.RunAndCaptureText throws InvalidOperationException unless both
RedirectStandardOutput and RedirectStandardError are set. Several converted
call sites only redirected one stream. Also fix RFLogging status message
to include the failing exit code via a {0} placeholder.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member Author

/azp list

@azure-pipelines

Copy link
Copy Markdown
CI/CD Pipelines for this repository:

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member Author

/azp run runtime-coreclr outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@adamsitnik adamsitnik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AaronRobinsonMSFT big thanks for dogfooding the new APIs! I've left some comments, PTAL

bool endOfStackTrace = false;

testProcess.ErrorDataReceived += (sender, line) =>
foreach (string rawLine in result.StandardError.Split('\n'))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this is a test helper, so we don't really care that much about performance, but I just want to point out that an alternative would be to use Process.ReadAllLines and avoid the need to split the captured text into separate lines:

public IEnumerable<ProcessOutputLine> ReadAllLines(TimeSpan? timeout = default)

Sth like this:

using Process testProcess = Process.Start(startInfo);
foreach (ProcessOutputLine line in testProcess.ReadAllLines)
{
    if (line.StandardError)
    {
        // the logic goes here
    }
}

testProcess.WaitForExit(); // a process can close std handles but keep running

process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit(100); // wait for 100 ms
string output = Process.RunAndCaptureText(process.StartInfo).StandardOutput;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previous implementation was using timeout, pointing this out as I am not sure whether this was intentional or not

Suggested change
string output = Process.RunAndCaptureText(process.StartInfo).StandardOutput;
string output = Process.RunAndCaptureText(process.StartInfo, timeout: TimeSpan.FromMilliseconds(100)).StandardOutput;

Comment on lines +201 to +203
ProcessTextOutput result = Process.RunAndCaptureText(startInfo);
Console.Write(result.StandardOutput);
Console.Error.Write(result.StandardError);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of redirecting the output and error to pipes, reading it and then writing it to parent standard output/error we can just use Process.Run. It's going to inherit the standard handles from parent and the child process is going to write directly to parent standard output/error.

Suggested change
ProcessTextOutput result = Process.RunAndCaptureText(startInfo);
Console.Write(result.StandardOutput);
Console.Error.Write(result.StandardError);
ProcessTextOutput result = Process.Run(startInfo);

Comment on lines +19 to +22
string error = result.StandardError.Trim();
if (result.ExitStatus.ExitCode != 0 || output != "Hello from HelloExe")
{
Console.Error.WriteLine($"Failed to start {helloExePath}");
return 1;
}

Task<string> outputTask = helloExe.StandardOutput.ReadToEndAsync();
Task<string> errorTask = helloExe.StandardError.ReadToEndAsync();
helloExe.WaitForExit();
string output = (await outputTask).Trim();
string error = (await errorTask).Trim();
if (helloExe.ExitCode != 0 || output != "Hello from HelloExe")
{
Console.Error.WriteLine($"Unexpected HelloExe result. Exit code: {helloExe.ExitCode}; output: '{output}'; error: '{error}'");
Console.Error.WriteLine($"Unexpected HelloExe result. Exit code: {result.ExitStatus.ExitCode}; output: '{output}'; error: '{error}'");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: trim the error only when we need it

Suggested change
string error = result.StandardError.Trim();
if (result.ExitStatus.ExitCode != 0 || output != "Hello from HelloExe")
{
Console.Error.WriteLine($"Failed to start {helloExePath}");
return 1;
}
Task<string> outputTask = helloExe.StandardOutput.ReadToEndAsync();
Task<string> errorTask = helloExe.StandardError.ReadToEndAsync();
helloExe.WaitForExit();
string output = (await outputTask).Trim();
string error = (await errorTask).Trim();
if (helloExe.ExitCode != 0 || output != "Hello from HelloExe")
{
Console.Error.WriteLine($"Unexpected HelloExe result. Exit code: {helloExe.ExitCode}; output: '{output}'; error: '{error}'");
Console.Error.WriteLine($"Unexpected HelloExe result. Exit code: {result.ExitStatus.ExitCode}; output: '{output}'; error: '{error}'");
if (result.ExitStatus.ExitCode != 0 || output != "Hello from HelloExe")
{
string error = result.StandardError.Trim();
Console.Error.WriteLine($"Unexpected HelloExe result. Exit code: {result.ExitStatus.ExitCode}; output: '{output}'; error: '{error}'");

Comment on lines 110 to 113
foreach (string key in Environment.GetEnvironmentVariables().Keys)
{
process.StartInfo.EnvironmentVariables[key] = Environment.GetEnvironmentVariable(key);
startInfo.EnvironmentVariables[key] = Environment.GetEnvironmentVariable(key);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no need to perform this logic, ProcessStartInfo.EnvironmentVariables is always by default initialized to parent process env vars.

It's a test app so we don't care, but when ProcessStartInfo.EnvironmentVariables is not modified at all, we can avoid allocating a lot of strings on Windows (we just let the OS know "don't use explicit env vars, just inherit all" )

using Process checkTracefs = new() { StartInfo = checkTracefsStartInfo };
checkTracefs.OutputDataReceived += (_, e) =>
ProcessTextOutput result = Process.RunAndCaptureText(checkTracefsStartInfo);
foreach (string rawLine in result.StandardOutput.Split('\n'))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to use process.ReadAllLines here. We would avoid the need to split (and trim) and print all updates as they appear, not when they are all finished

Comment on lines +555 to 560
ProcessTextOutput result = Process.RunAndCaptureText(startInfo, TimeSpan.FromMilliseconds(DEFAULT_TIMEOUT_MS));
if (result.ExitStatus.Canceled)
{
proc.Kill(true);
outputWriter.WriteLine($"Timedout: '{fileName} {arguments}");
return false;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When Process.Run* APIs timeout, they kill the process and then throw TimeoutException

Suggested change
ProcessTextOutput result = Process.RunAndCaptureText(startInfo, TimeSpan.FromMilliseconds(DEFAULT_TIMEOUT_MS));
if (result.ExitStatus.Canceled)
{
proc.Kill(true);
outputWriter.WriteLine($"Timedout: '{fileName} {arguments}");
return false;
}
ProcessTextOutput result;
try
{
result = Process.RunAndCaptureText(startInfo, TimeSpan.FromMilliseconds(DEFAULT_TIMEOUT_MS));
}
catch (TimeoutException)
{
outputWriter.WriteLine($"Timedout: '{fileName} {arguments}");
return false;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Infrastructure test-enhancement Improvements of test source code

Projects

Status: No status
Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants