// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package doc import ( "bufio" "bytes" "fmt" "os" "path/filepath" "strings" "testing" "github.com/spf13/cobra" ) func assertNoErr(t *testing.T, e error) { if e != nil { t.Error(e) } } func translate(in string) string { return strings.ReplaceAll(in, "-", "\\-") } func TestGenManDoc(t *testing.T) { header := &GenManHeader{ Title: "Project", Section: "2", } // We generate on a subcommand so we have both subcommands and parents buf := new(bytes.Buffer) if err := GenMan(echoCmd, header, buf); err != nil { t.Fatal(err) } output := buf.String() // Make sure parent has - in CommandPath() in SEE ALSO: parentPath := echoCmd.Parent().CommandPath() dashParentPath := strings.ReplaceAll(parentPath, " ", "-") expected := translate(dashParentPath) expected = expected + "(" + header.Section + ")" checkStringContains(t, output, expected) checkStringContains(t, output, translate(echoCmd.Name())) checkStringContains(t, output, translate(echoCmd.Name())) checkStringContains(t, output, "boolone") checkStringContains(t, output, "rootflag") checkStringContains(t, output, translate(rootCmd.Name())) checkStringContains(t, output, translate(echoSubCmd.Name())) checkStringOmits(t, output, translate(deprecatedCmd.Name())) checkStringContains(t, output, translate("Auto generated")) } func TestGenManNoHiddenParents(t *testing.T) { header := &GenManHeader{ Title: "Project", Section: "2", } // We generate on a subcommand so we have both subcommands and parents for _, name := range []string{"rootflag", "strtwo"} { f := rootCmd.PersistentFlags().Lookup(name) f.Hidden = true defer func() { f.Hidden = false }() } buf := new(bytes.Buffer) if err := GenMan(echoCmd, header, buf); err != nil { t.Fatal(err) } output := buf.String() // Make sure parent has - in CommandPath() in SEE ALSO: parentPath := echoCmd.Parent().CommandPath() dashParentPath := strings.ReplaceAll(parentPath, " ", "-") expected := translate(dashParentPath) expected = expected + "(" + header.Section + ")" checkStringContains(t, output, expected) checkStringContains(t, output, translate(echoCmd.Name())) checkStringContains(t, output, translate(echoCmd.Name())) checkStringContains(t, output, "boolone") checkStringOmits(t, output, "rootflag") checkStringContains(t, output, translate(rootCmd.Name())) checkStringContains(t, output, translate(echoSubCmd.Name())) checkStringOmits(t, output, translate(deprecatedCmd.Name())) checkStringContains(t, output, translate("Auto generated")) checkStringOmits(t, output, "OPTIONS INHERITED FROM PARENT COMMANDS") } func TestGenManNoGenTag(t *testing.T) { echoCmd.DisableAutoGenTag = true defer func() { echoCmd.DisableAutoGenTag = false }() header := &GenManHeader{ Title: "Project", Section: "2", } // We generate on a subcommand so we have both subcommands and parents buf := new(bytes.Buffer) if err := GenMan(echoCmd, header, buf); err != nil { t.Fatal(err) } output := buf.String() unexpected := translate("#HISTORY") checkStringOmits(t, output, unexpected) unexpected = translate("Auto generated by spf13/cobra") checkStringOmits(t, output, unexpected) } func TestGenManSeeAlso(t *testing.T) { rootCmd := &cobra.Command{Use: "root", Run: emptyRun} aCmd := &cobra.Command{Use: "aaa", Run: emptyRun, Hidden: true} // #229 bCmd := &cobra.Command{Use: "bbb", Run: emptyRun} cCmd := &cobra.Command{Use: "ccc", Run: emptyRun} rootCmd.AddCommand(aCmd, bCmd, cCmd) buf := new(bytes.Buffer) header := &GenManHeader{} if err := GenMan(rootCmd, header, buf); err != nil { t.Fatal(err) } scanner := bufio.NewScanner(buf) if err := assertLineFound(scanner, ".SH SEE ALSO"); err != nil { t.Fatalf("Couldn't find SEE ALSO section header: %v", err) } if err := assertNextLineEquals(scanner, `\fBroot-bbb(1)\fP, \fBroot-ccc(1)\fP`); err != nil { t.Fatalf("Second line after SEE ALSO wasn't correct: %v", err) } } func TestManPrintFlagsHidesShortDeprecated(t *testing.T) { c := &cobra.Command{} c.Flags().StringP("foo", "f", "default", "Foo flag") assertNoErr(t, c.Flags().MarkShorthandDeprecated("foo", "don't use it no more")) buf := new(bytes.Buffer) manPrintFlags(buf, c.Flags()) got := buf.String() expected := "**--foo**=\"default\"\n\tFoo flag\n\n" if got != expected { t.Errorf("Expected %v, got %v", expected, got) } } func TestGenManTree(t *testing.T) { c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"} header := &GenManHeader{Section: "2"} tmpdir, err := os.MkdirTemp("", "test-gen-man-tree") if err != nil { t.Fatalf("Failed to create tmpdir: %s", err.Error()) } defer os.RemoveAll(tmpdir) if err := GenManTree(c, header, tmpdir); err != nil { t.Fatalf("GenManTree failed: %s", err.Error()) } if _, err := os.Stat(filepath.Join(tmpdir, "do.2")); err != nil { t.Fatalf("Expected file 'do.2' to exist") } if header.Title != "" { t.Fatalf("Expected header.Title to be unmodified") } } func assertLineFound(scanner *bufio.Scanner, expectedLine string) error { for scanner.Scan() { line := scanner.Text() if line == expectedLine { return nil } } if err := scanner.Err(); err != nil { return fmt.Errorf("scan failed: %s", err) } return fmt.Errorf("hit EOF before finding %v", expectedLine) } func assertNextLineEquals(scanner *bufio.Scanner, expectedLine string) error { if scanner.Scan() { line := scanner.Text() if line == expectedLine { return nil } return fmt.Errorf("got %v, not %v", line, expectedLine) } if err := scanner.Err(); err != nil { return fmt.Errorf("scan failed: %v", err) } return fmt.Errorf("hit EOF before finding %v", expectedLine) } func BenchmarkGenManToFile(b *testing.B) { file, err := os.CreateTemp("", "") if err != nil { b.Fatal(err) } defer os.Remove(file.Name()) defer file.Close() b.ResetTimer() for i := 0; i < b.N; i++ { if err := GenMan(rootCmd, nil, file); err != nil { b.Fatal(err) } } }