From 2e2a923f3f88168771f3a51335728718dc691320 Mon Sep 17 00:00:00 2001 From: Faris Huskovic Date: Fri, 31 Jul 2020 09:28:51 -0500 Subject: [PATCH 1/2] Update help output --- command_test.go | 74 ++++++++++++++++++++++++++++++++++++++++++++++--- help.go | 6 ++-- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/command_test.go b/command_test.go index 0640898..4fc3dbe 100644 --- a/command_test.go +++ b/command_test.go @@ -14,18 +14,23 @@ const ( expectedParentCmdHelpOutput = `Usage: mockParentCmd Mock parent command usage. -Mock parent command description. +Description: Mock parent command description. Commands: - s,sc,sub,mockSubCmd - A simple mock subcommand with aliases. + s, sc, sub, mockSubCmd - A simple mock subcommand with aliases and its own subcommand. ` ) var subCmd = &mockSubCmd{buf: new(bytes.Buffer)} type ( + // Mock for root/parent command. mockParentCmd struct{} - mockSubCmd struct{ buf *bytes.Buffer } + // Mock subcommand with aliases and a nested + // sucommand of its own. + mockSubCmd struct{ buf *bytes.Buffer } + // Mock subcommand with aliases and no nested subcommands. + mockSubCmdNoNested struct{} ) func (c *mockParentCmd) Run(fl *pflag.FlagSet) {} @@ -47,6 +52,10 @@ func (c *mockSubCmd) Run(fl *pflag.FlagSet) { } } +func (c *mockSubCmd) Subcommands() []Command { + return []Command{new(mockSubCmd)} +} + func (c *mockSubCmd) Write(b []byte) (int, error) { return c.buf.Write(b) } func (c *mockSubCmd) Spec() CommandSpec { @@ -54,7 +63,18 @@ func (c *mockSubCmd) Spec() CommandSpec { Name: "mockSubCmd", Usage: "Test a subcommand.", Aliases: []string{"s", "sc", "sub"}, - Desc: "A simple mock subcommand with aliases.", + Desc: "A simple mock subcommand with aliases and its own subcommand.", + } +} + +func (c *mockSubCmdNoNested) Run(fl *pflag.FlagSet) {} + +func (c *mockSubCmdNoNested) Spec() CommandSpec { + return CommandSpec{ + Name: "mockSubCmdNoNested", + Usage: "Used for help output tests.", + Aliases: []string{"s", "sc", "sub"}, + Desc: "A simple mock subcommand with aliases and no nested subcommands.", } } @@ -103,3 +123,49 @@ func TestSubcmdAliasesInParentCmdHelpOutput(t *testing.T) { expected := expectedParentCmdHelpOutput assert.Equal(t, "display_subcmd_aliases_in_parentcmd_help_output", expected, got) } + +func TestSubCmdHelpOutput(t *testing.T) { + withNested := `Usage: mockSubCmd Test a subcommand. + +Aliases: s, sc, sub + +Description: A simple mock subcommand with aliases and its own subcommand. + +Commands: + s, sc, sub, mockSubCmd - A simple mock subcommand with aliases and its own subcommand. +` + + noNested := `Usage: mockSubCmdNoNested Used for help output tests. + +Aliases: s, sc, sub + +Description: A simple mock subcommand with aliases and no nested subcommands. +` + + for _, test := range []struct { + cmd Command + name, expected string + }{ + { + name: "subcmd w/nested subcmd.", + expected: withNested, + cmd: new(mockSubCmd), + }, + { + name: "subcmd w/no nested subcmds.", + expected: noNested, + cmd: new(mockSubCmdNoNested), + }, + } { + t.Run(test.name, func(t *testing.T) { + buf := new(bytes.Buffer) + name := test.cmd.Spec().Name + fl := pflag.NewFlagSet(name, pflag.ExitOnError) + // If the help output is not written to the buffer + // in the format we expect then the test will fail. + renderHelp(name, test.cmd, fl, buf) + got := string(buf.Bytes()) + assert.Equal(t, t.Name(), test.expected, got) + }) + } +} diff --git a/help.go b/help.go index c04e2e5..714a761 100644 --- a/help.go +++ b/help.go @@ -52,10 +52,10 @@ func renderHelp(fullName string, cmd Command, fl *pflag.FlagSet, w io.Writer) { // If the command has aliases, add them to the output as a comma-separated list. if spec.HasAliases() { - fmt.Fprintf(&b, "Aliases: %v", spec.Aliases) + fmt.Fprintf(&b, "Aliases: %s\n\n", strings.Join(spec.Aliases, ", ")) } // Render usage and description. - usageAndDesc := fmt.Sprintf("%s%s\n", b.String(), spec.Desc) + usageAndDesc := fmt.Sprintf("%sDescription: %s\n", b.String(), spec.Desc) fmt.Fprint(w, usageAndDesc) tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', tabwriter.StripEscape) @@ -80,7 +80,7 @@ func renderHelp(fullName string, cmd Command, fl *pflag.FlagSet, w io.Writer) { continue } - allNames := strings.Join(append(spec.Aliases, spec.Name), ",") + allNames := strings.Join(append(spec.Aliases, spec.Name), ", ") fmt.Fprintf(tw, tabEscape+"\t"+tabEscape+"%v\t- %v\n", From 6615750932ecf4ea4a7bd5a702e9a700ed4f4118 Mon Sep 17 00:00:00 2001 From: Faris Huskovic Date: Tue, 4 Aug 2020 15:11:39 +0000 Subject: [PATCH 2/2] Update help output format --- command_test.go | 12 ++++++------ help.go | 7 +++---- run.go | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/command_test.go b/command_test.go index d8af9a1..4a3b6de 100644 --- a/command_test.go +++ b/command_test.go @@ -15,7 +15,7 @@ type ( // Mock for root/parent command. mockParentCmd struct{} // Mock subcommand with aliases and a nested sucommand of its own. - mockSubCmd struct{ + mockSubCmd struct { buf *bytes.Buffer } // Mock subcommand with aliases and no nested subcommands. @@ -103,8 +103,8 @@ Commands: fl := pflag.NewFlagSet(name, pflag.ExitOnError) // If the help output doesn't contain the subcommand and // isn't formatted the way we expect the test will fail. - renderHelp(name, cmd, fl, buf) - got := string(buf.Bytes()) + renderHelp(buf, name, cmd, fl) + got := buf.String() assert.Equal(t, t.Name(), expected, got) }) } @@ -128,8 +128,8 @@ Description: A simple mock subcommand with aliases and no nested subcommands. ` for _, test := range []struct { - cmd Command name, expected string + cmd Command }{ { name: "subcmd w/nested subcmd.", @@ -148,8 +148,8 @@ Description: A simple mock subcommand with aliases and no nested subcommands. fl := pflag.NewFlagSet(name, pflag.ExitOnError) // If the help output is not written to the buffer // in the format we expect then the test will fail. - renderHelp(name, test.cmd, fl, buf) - got := string(buf.Bytes()) + renderHelp(buf, name, test.cmd, fl) + got := buf.String() assert.Equal(t, t.Name(), test.expected, got) }) } diff --git a/help.go b/help.go index 714a761..8b90c2f 100644 --- a/help.go +++ b/help.go @@ -45,7 +45,7 @@ func renderFlagHelp(fullName string, fl *pflag.FlagSet, w io.Writer) { } // renderHelp generates a command's help page. -func renderHelp(fullName string, cmd Command, fl *pflag.FlagSet, w io.Writer) { +func renderHelp(w io.Writer, fullName string, cmd Command, fl *pflag.FlagSet) { var b strings.Builder spec := cmd.Spec() fmt.Fprintf(&b, "Usage: %v %v\n\n", fullName, spec.Usage) @@ -54,9 +54,8 @@ func renderHelp(fullName string, cmd Command, fl *pflag.FlagSet, w io.Writer) { if spec.HasAliases() { fmt.Fprintf(&b, "Aliases: %s\n\n", strings.Join(spec.Aliases, ", ")) } - // Render usage and description. - usageAndDesc := fmt.Sprintf("%sDescription: %s\n", b.String(), spec.Desc) - fmt.Fprint(w, usageAndDesc) + // Print usage and description. + fmt.Fprintf(w, "%sDescription: %s\n", b.String(), spec.Desc) tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', tabwriter.StripEscape) defer tw.Flush() diff --git a/run.go b/run.go index 34341a9..59d444a 100644 --- a/run.go +++ b/run.go @@ -94,7 +94,7 @@ func Run(cmd Command, args []string, parent string) { // Reassign the usage now that we've parsed the args // so that we can render it manually. fl.Usage = func() { - renderHelp(name, cmd, fl, os.Stderr) + renderHelp(os.Stderr, name, cmd, fl) } if err != nil { fl.Usage()