-
Notifications
You must be signed in to change notification settings - Fork 1
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
Change ShellCommand default behaviour not to swallow Cancellation exception #134
Conversation
], because: "When we cancel the process we close StdIn and it shuts down. The process observes the EOF as empty string and prints 'Hello ' but there is a benign race condition which means we may not observe this output. Test needs to handle both cases"); | ||
} | ||
|
||
[Theory, InlineData(SyncBehaviour.Sync), InlineData(SyncBehaviour.Async)] |
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.
The diff is a bit weird. This is the pre-existing test; it has no modifications other than adding the _DoNotThrowOnCancellation
name suffix and setting .WithOptions(ShellCommandOptions.DoNotThrowOnCancellation)
The above test is the new one, it is a copy-paste with tweaks to handle the exception on cancel.
…ycle them for other processes
Octopus.Shellfish.ShellCommand.Execute | ||
Octopus.Shellfish.ShellCommand.ExecuteAsync | ||
Octopus.Shellfish.ShellCommandOptions.value__ |
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.
What's with this value__
?
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.
That's how enums work in C#, if you decompile it in sharplab you get this
.class public auto ansi sealed ShellCommandOptions
extends [System.Runtime]System.Enum
{
// Fields
.field public specialname rtspecialname int32 value__
.field public static literal valuetype ShellCommandOptions None = int32(0)
.field public static literal valuetype ShellCommandOptions DoNotThrowOnCancellation = int32(1)
} // end of class ShellCommandOptions
The reflection-scanny thing is obviously picking it up. It's just a convention test so 🤷
|
||
namespace Octopus.Shellfish; | ||
|
||
public enum ShellCommandOptions |
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.
Using an enum means this will be a list of mutually exclusive boolean options (or not, if we added [Flags]
). It's difficult to predict what options we might want to add in future, but this seems quite limited. I think a class would provide more flexibility.
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.
I agree, but it's hard to predict the future. We might only have this one option for the next 7 years, in which case classes would be additional trouble for no benefit.
Given the limited use of Shellfish, if we find a future scenario where we need more kinds of options, I'd happily commit to a breaking API change at that point. (Note also: making it [Flags]
is nonbreaking because we only have 0 and 1 right now)
Background
When the new
ShellCommand
API was added, we chose to preserve the cancellation behaviour of the oldShellExecutor
API.The legacy behaviour is such:
Execute(cancellationToken)
;OperationCanceledException
, it would return successfully. This contradicts the behaviour of essentially all other cancellable functions in .NET.This "preservation" behaviour was chosen to make migrating other code to the new API easier, and seemed sensible at the time.
However, we have since learnt:
Results
Adds a new
enum ShellCommandOptions
with valuesNone
andDoNotThrowOnCancellation
Adds a new
WithOptions
function toShellCommand
, so you can docommand.WithOptions(ShellCommandOptions.DoNotThrowOnCancellation);
Changes the default behaviour so it rethrows the underlying
OperationCanceledException
as per other .NET code.Updates tests accordingly
Note There were two extra test changes here
A
CaptureProcess
method was added to ShellCommand. This is to allow tests to get theSystem.Diagnostics.Process
object and assert that a process has actually exited. Deliberately non-public as we don't want to commit to that level of information in the public API yet.Tests that wanted to block used to run
cmd
orbash
with no arguments, expecting them to wait forever for stdinput. We recently learnt that in linux on TeamCity, bash is capable of determining that it has no stdinput and is not running interactively and exits immediately, which would invalidate the test. I switched to using "sleep" on linux and "timeout.exe" on windows instead of relying on stdinput.