Skip to content

Commit

Permalink
Always propagate root context to child command
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
pietern committed Dec 9, 2022
1 parent a6f198b commit 5f0e7bb
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 5 deletions.
7 changes: 2 additions & 5 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -1059,11 +1059,8 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
cmd.commandCalledAs.name = cmd.Name()
}

// We have to pass global context to children command
// if context is present on the parent command.
if cmd.ctx == nil {
cmd.ctx = c.ctx
}
// Pass context of root command to child command.
cmd.ctx = c.ctx

err = cmd.execute(flags)
if err != nil {
Expand Down
38 changes: 38 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,44 @@ 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 TestExecuteContextMultipleTimes(t *testing.T) {
ctxRun := func(cmd *Command, args []string) {
err := cmd.Context().Err()
if err != nil {
t.Errorf("Context of command %q has error: %s", cmd.Use, err)
}
}

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++ {
ctx, cancel := context.WithCancel(context.Background())

if _, err := executeCommandWithContext(ctx, rootCmd, ""); err != nil {
t.Errorf("Root command must not fail: %+v", err)
}

if _, err := executeCommandWithContext(ctx, rootCmd, "child"); err != nil {
t.Errorf("Subcommand must not fail: %+v", err)
}

if _, err := executeCommandWithContext(ctx, rootCmd, "child", "grandchild"); err != nil {
t.Errorf("Command child must not fail: %+v", err)
}

// Invalidate the context of this iteration.
cancel()
}
}

func TestExecute_NoContext(t *testing.T) {
run := func(cmd *Command, args []string) {
if cmd.Context() != context.Background() {
Expand Down

0 comments on commit 5f0e7bb

Please sign in to comment.