-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Add ProcessStartOptions class with platform-aware path resolution #124271
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
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-system-diagnostics-process |
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
adamsitnik
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot please address my feedback
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.cs
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartOptions.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartOptions.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartOptions.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Unix.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Unix.cs
Show resolved
Hide resolved
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
adamsitnik
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot address my feedback
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartOptions.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
| return filename; | ||
| } | ||
|
|
||
| #if WINDOWS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To other reviewers: In contrary to other classes, where we have dedicated .Windows.cs and .Unix.cs files, I would prefer this method to use #if defs and have everything in once place to make it easier to ensure they do the same thing on every OS, and when they don't it's also clear.
adamsitnik
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot please address my feedback
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Unix.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Windows.cs
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Windows.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
adamsitnik
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot please address my feedback
|
|
||
| namespace System.Diagnostics.Tests | ||
| { | ||
| public class ProcessStartOptionsTests : ProcessTestBase |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to inherit from ProcessTestBase?
| public class ProcessStartOptionsTests : ProcessTestBase | |
| public class ProcessStartOptionsTests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c3517ee - removed inheritance from ProcessTestBase.
| [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows), nameof(PlatformDetection.IsNotWindowsNanoServer))] | ||
| public void TestConstructor_ResolvesCmdOnWindows() | ||
| { | ||
| ProcessStartOptions options = new ProcessStartOptions("cmd"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer simpler syntax for creating new instances of ProcessStartOptions type in all the new tests.
| ProcessStartOptions options = new ProcessStartOptions("cmd"); | |
| ProcessStartOptions options = new("cmd"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c3517ee - using new() syntax throughout all tests.
| public void TestResolvePath_FindsCommonUtilities() | ||
| { | ||
| // Test common Unix utilities | ||
| string[] commonUtils = { "ls", "cat", "echo", "sh" }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turn this test into Theory that accepts just a string utilName
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c3517ee - converted to Theory with InlineData for "ls", "cat", "echo", "sh".
| [PlatformSpecific(TestPlatforms.Linux | TestPlatforms.FreeBSD | TestPlatforms.OSX)] | ||
| [Fact] | ||
| public void TestConstructor_ResolvesShOnUnix() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this test to ProcessStartOptionsTests.Unix.cs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c3517ee - moved to ProcessStartOptionsTests.Unix.cs as Constructor_ResolvesShOnUnix.
| [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows), nameof(PlatformDetection.IsNotWindowsNanoServer))] | ||
| public void TestConstructor_ResolvesCmdOnWindows() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this test to ProcessStartOptionsTests.Windows.cs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c3517ee - moved to ProcessStartOptionsTests.Windows.cs as Constructor_ResolvesCmdOnWindows.
| // sh should be findable in PATH on all Unix systems | ||
| ProcessStartOptions options = new ProcessStartOptions("sh"); | ||
| Assert.True(File.Exists(options.FileName)); | ||
| Assert.EndsWith("sh", options.FileName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it safe to assume it's always going to be equal "/bin/sh"?
| Assert.EndsWith("sh", options.FileName); | |
| Assert.Equals("/bin/sh", options.FileName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c3517ee - using Assert.Equal("/bin/sh", options.FileName).
|
|
||
| namespace System.Diagnostics.Tests | ||
| { | ||
| public class ProcessStartOptionsTests_Windows |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| public class ProcessStartOptionsTests_Windows | |
| public partial class ProcessStartOptionsTests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c3517ee - made it a partial class.
| { | ||
| public class ProcessStartOptionsTests_Windows | ||
| { | ||
| [Fact] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test fails on Nano because notepad is not available on nano.
| [Fact] | |
| [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c3517ee - added ConditionalFact back for IsNotWindowsNanoServer.
| { | ||
| Directory.SetCurrentDirectory(tempDir); | ||
| ProcessStartOptions options = new ProcessStartOptions(fileName); | ||
| Assert.Equal(fullPath, options.FileName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test keeps failing on OSX, I think it's because it's not the actual full path:
Assert.Equal() Failure: Strings differ
↓ (pos 1)
Expected: "/tmp/helix/working/BDA1097F/t/testscript."···
Actual: "/private/tmp/helix/working/BDA1097F/t/tes"·
Most likely just using Path.GetFullPath should fix it.
| Assert.Equal(fullPath, options.FileName); | |
| Assert.Equal(Path.GetFullPath(fullPath), options.FileName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c3517ee - using Path.GetFullPath(fullPath) for assertion.
| { | ||
| Directory.SetCurrentDirectory(tempDir); | ||
| // Should throw because file is not executable | ||
| Assert.Throws<FileNotFoundException>(() => new ProcessStartOptions(fileName)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test fails:
Assert.Throws() Failure: No exception was thrown
Expected: typeof(System.IO.FileNotFoundException)
I think it's because currently we check the permissions only if file was found in PATH:
runtime/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs
Lines 737 to 748 in 6fbc0b6
| private static string? FindProgramInPath(string program) | |
| { | |
| string path; | |
| string? pathEnvVar = Environment.GetEnvironmentVariable("PATH"); | |
| if (pathEnvVar != null) | |
| { | |
| var pathParser = new StringParser(pathEnvVar, ':', skipEmpty: true); | |
| while (pathParser.MoveNext()) | |
| { | |
| string subPath = pathParser.ExtractCurrent(); | |
| path = Path.Combine(subPath, program); | |
| if (IsExecutable(path)) |
And we don't do that for CWD:
runtime/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs
Lines 721 to 726 in 6fbc0b6
| // Then check the current directory | |
| path = Path.Combine(Directory.GetCurrentDirectory(), filename); | |
| if (File.Exists(path)) | |
| { | |
| return path; | |
| } |
So this test should be either fixed or removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c3517ee - removed the ChecksExecutablePermissions test since ProcessStartOptions.ResolvePath doesn't check executable permissions for files in CWD (matching Process.ResolvePath behavior).
…, Theory for utils Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
adamsitnik
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot please address my feedback
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/tests/ProcessStartOptionsTests.Unix.cs
Show resolved
Hide resolved
…h for sh Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Description
Implements
ProcessStartOptionsclass providing path resolution and process configuration with explicit platform semantics.Changes
API Surface
ProcessStartOptionswith constructor that resolves executable pathsFileName(resolved),Arguments,Environment,WorkingDirectory,InheritedHandles,KillOnParentExit,CreateNewProcessGroupArguments,Environment, andInheritedHandlesuse lazy allocationEnvironmentreusesDictionaryWrapperimplementation fromProcessStartInfoArgumentsandInheritedHandlessettersPath Resolution
ProcessStartOptions.ResolvePathis the primary path resolution implementation for both Windows and Unix.exe, searches System32 (cached), Windows directory (not cached), uses;PATH separatorProcess.IsExecutable, uses:PATH separatorProcess.Unix.csnow callsProcessStartOptions.ResolvePathinstead of having its own implementationProcess.IsExecutableinternal for code reuse between ProcessStartOptions and ProcessProcess.ResolvePathto eliminate duplication - all callers now useProcessStartOptions.ResolvePathFileNotFoundExceptionwhen path cannot be resolvedInfrastructure
Interop.GetWindowsDirectoryWfor Windows directory lookupWINDOWScompilation constant for platform-specific codeStringParserto common section (required by both platforms)Tests
new()syntax for concisenessResolvePath_FindsCommonUtilitiesuses[Theory]with[InlineData]for parameterized testing of multiple utilities ("ls", "cat", "echo", "sh").exeappending, system directory search, semicolon PATH separatorEndsWithandPath.GetFullPath()for precision and symlink handlingConstructor_NullFileName_Throws)Example
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.