mirror of https://github.com/spf13/cobra.git
Always propagate root context to child command
The context passed to the root command should propagate to its children not only on the first execution but also subsequent calls. Calling the same command multiple times is common when testing cobra applications.
This commit is contained in:
parent
a6f198b635
commit
8e2c9596e1
|
@ -1009,7 +1009,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||||
|
|
||||||
// Regardless of what command execute is called on, run on Root only
|
// Regardless of what command execute is called on, run on Root only
|
||||||
if c.HasParent() {
|
if c.HasParent() {
|
||||||
return c.Root().ExecuteC()
|
return c.Root().ExecuteContextC(c.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// windows hook
|
// windows hook
|
||||||
|
@ -1059,11 +1059,8 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||||
cmd.commandCalledAs.name = cmd.Name()
|
cmd.commandCalledAs.name = cmd.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have to pass global context to children command
|
// Pass context of root command to child command.
|
||||||
// if context is present on the parent command.
|
cmd.ctx = c.ctx
|
||||||
if cmd.ctx == nil {
|
|
||||||
cmd.ctx = c.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cmd.execute(flags)
|
err = cmd.execute(flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -232,6 +232,96 @@ func TestExecuteContextC(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This tests that the context passed to the root command propagates to its children
|
||||||
|
// not only on the first execution but also subsequent calls.
|
||||||
|
// Calling the same command multiple times is common when testing cobra applications.
|
||||||
|
func TestExecuteContextMultiple(t *testing.T) {
|
||||||
|
var key string
|
||||||
|
|
||||||
|
// Define unique contexts so we can tell them apart below.
|
||||||
|
ctxs := []context.Context{
|
||||||
|
context.WithValue(context.Background(), &key, "1"),
|
||||||
|
context.WithValue(context.Background(), &key, "2"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared reference to the context in the current iteration.
|
||||||
|
var currentCtx context.Context
|
||||||
|
|
||||||
|
ctxRun := func(cmd *Command, args []string) {
|
||||||
|
if cmd.Context() != currentCtx {
|
||||||
|
t.Errorf("Command %q must have context with value %s", cmd.Use, currentCtx.Value(&key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd := &Command{Use: "root", Run: ctxRun, PreRun: ctxRun}
|
||||||
|
childCmd := &Command{Use: "child", Run: ctxRun, PreRun: ctxRun}
|
||||||
|
granchildCmd := &Command{Use: "grandchild", Run: ctxRun, PreRun: ctxRun}
|
||||||
|
|
||||||
|
childCmd.AddCommand(granchildCmd)
|
||||||
|
rootCmd.AddCommand(childCmd)
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
currentCtx = ctxs[i]
|
||||||
|
|
||||||
|
if _, err := executeCommandWithContext(currentCtx, rootCmd, ""); err != nil {
|
||||||
|
t.Errorf("Root command must not fail: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := executeCommandWithContext(currentCtx, rootCmd, "child"); err != nil {
|
||||||
|
t.Errorf("Subcommand must not fail: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := executeCommandWithContext(currentCtx, rootCmd, "child", "grandchild"); err != nil {
|
||||||
|
t.Errorf("Command child must not fail: %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This tests that the context passed to a subcommand propagates to the root.
|
||||||
|
// If the entry point happens to be different from the root command, the
|
||||||
|
// context should still propagate throughout the execution.
|
||||||
|
func TestExecuteContextOnSubcommand(t *testing.T) {
|
||||||
|
var key string
|
||||||
|
|
||||||
|
// Define unique contexts so we can tell them apart below.
|
||||||
|
ctxs := []context.Context{
|
||||||
|
context.WithValue(context.Background(), &key, "1"),
|
||||||
|
context.WithValue(context.Background(), &key, "2"),
|
||||||
|
context.WithValue(context.Background(), &key, "3"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared reference to the context in the current iteration.
|
||||||
|
var currentCtx context.Context
|
||||||
|
|
||||||
|
ctxRun := func(cmd *Command, args []string) {
|
||||||
|
if cmd.Context() != currentCtx {
|
||||||
|
t.Errorf("Command %q must have context with value %s", cmd.Use, currentCtx.Value(&key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd := &Command{Use: "root", Run: ctxRun, PreRun: ctxRun}
|
||||||
|
childCmd := &Command{Use: "child", Run: ctxRun, PreRun: ctxRun}
|
||||||
|
granchildCmd := &Command{Use: "grandchild", Run: ctxRun, PreRun: ctxRun}
|
||||||
|
|
||||||
|
childCmd.AddCommand(granchildCmd)
|
||||||
|
rootCmd.AddCommand(childCmd)
|
||||||
|
|
||||||
|
currentCtx = ctxs[0]
|
||||||
|
if _, err := executeCommandWithContext(currentCtx, rootCmd, ""); err != nil {
|
||||||
|
t.Errorf("Root command must not fail: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCtx = ctxs[1]
|
||||||
|
if _, err := executeCommandWithContext(currentCtx, childCmd, "child"); err != nil {
|
||||||
|
t.Errorf("Subcommand must not fail: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCtx = ctxs[2]
|
||||||
|
if _, err := executeCommandWithContext(currentCtx, granchildCmd, "child", "grandchild"); err != nil {
|
||||||
|
t.Errorf("Command child must not fail: %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestExecute_NoContext(t *testing.T) {
|
func TestExecute_NoContext(t *testing.T) {
|
||||||
run := func(cmd *Command, args []string) {
|
run := func(cmd *Command, args []string) {
|
||||||
if cmd.Context() != context.Background() {
|
if cmd.Context() != context.Background() {
|
||||||
|
|
Loading…
Reference in New Issue