-
Notifications
You must be signed in to change notification settings - Fork 42
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
Fix exec deadlock when emitter is not Typer intf #216
Conversation
936b3a0
to
1faa31a
Compare
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.
LGTM, I added a test to cover this as well. Thanks!
1faa31a
to
5a47abf
Compare
@guseggert I just noticed now that the nil check for I think this check was originally supposed to check the input argument I'm going to investigate this now, and will likely make a follow up PR. |
Sounds good, thanks for the follow-up! Would you mind adding whatever tests/assertions are missing that would have caught that? |
For sure. I was surprised the existing ones didn't catch it. Current draft is looking like this (but I need to mock responder so that it pretends to be the CLI or something else): func TestExecutorPostRun(t *testing.T) {
expectedValue := true
testCmd := &Command{
Run: func(*Request, ResponseEmitter, Environment) error {
return nil
},
PostRun: PostRunMap{
CLI: func(response Response, emitter ResponseEmitter) error {
return emitter.Emit(expectedValue)
},
},
}
testRoot := &Command{
Subcommands: map[string]*Command{
"test": testCmd,
},
}
req, err := NewRequest(context.Background(), []string{"test"}, nil, nil, nil, testRoot)
if err != nil {
t.Fatal(err)
}
t.Run("with", func(t *testing.T) {
var (
emitter, resp = NewChanResponsePair(req)
x = NewExecutor(testRoot)
)
err = x.Execute(req, emitter, nil)
if err != nil {
t.Fatal(err)
}
response, err := resp.Next()
if err != nil && err != io.EOF {
t.Fatal(err)
}
responseTyped, isBool := response.(bool)
if !isBool {
t.Fatalf("expected postrun response to be `bool` but got: %#v",
response)
}
if responseTyped != expectedValue {
t.Fatalf("expected postrun to respond with %v but got: %v",
expectedValue, responseTyped)
}
_, err = resp.Next()
if err != io.EOF {
t.Fatalf("expected EOF but got: %s", err)
}
})
t.Run("without", func(t *testing.T) {
testCmd.PostRun = nil
var (
emitter, resp = NewChanResponsePair(req)
x = NewExecutor(testRoot)
)
err = x.Execute(req, emitter, nil)
if err != nil {
t.Fatal(err)
}
_, err := resp.Next()
if err != io.EOF {
t.Fatalf("expected EOF but got: %s", err)
}
})
} |
There's probably a better way this could be done but I don't have time to investigate it. Edits welcomed.
If the emitter passed to the executor does not implement the
Type
method, it will just block forever on the channel operations topostCloseErr
.Example:
I encountered this a while ago when trying to write tests using the
cmds.NewChanResponsePair
structures.In those scenarios they would just never return. When isolated like this, Go flatout panics.
NewChanResponsePair
should probably implementType
, but this still needs to be fixed in case someone passes any other type toExecute
.* I think that's all right, I'm recalling from months ago, but some details may be wrong. Just poke me for details.