package pflag import ( "bytes" "io" "strings" "testing" ) const expectedOutput = ` --long-form[=true|false] Some description --long-form2[=true|false] Some description with multiline -s, --long-name[=true|false] Some description -t, --long-name2[=true|false] Some description with multiline ` func setUpPFlagSet(buf io.Writer) *FlagSet { f := NewFlagSet("test", ExitOnError) f.Bool("long-form", false, "Some description") f.Bool("long-form2", false, "Some description\n with multiline") f.BoolP("long-name", "s", false, "Some description") f.BoolP("long-name2", "t", false, "Some description with\n multiline") f.SetOutput(buf) return f } func TestPrintUsage(t *testing.T) { buf := bytes.Buffer{} f := setUpPFlagSet(&buf) f.PrintDefaults() res := buf.String() if res != expectedOutput { t.Errorf("Expected \n%s \nActual \n%s", expectedOutput, res) } } func setUpPFlagSet2(buf io.Writer) *FlagSet { f := NewFlagSet("test", ExitOnError) f.Bool("long-form", false, "Some description") f.Bool("long-form2", false, "Some description\n with multiline") f.BoolP("long-name", "s", false, "Some description") f.BoolP("long-name2", "t", false, "Some description with\n multiline") f.StringP("some-very-long-arg", "l", "test", "Some very long description having break the limit") f.StringP("other-very-long-arg", "o", "long-default-value", "Some very long description having break the limit") f.String("some-very-long-arg2", "very long default value", "Some very long description\nwith line break\nmultiple") f.SetOutput(buf) return f } const expectedOutput2 = ` --long-form[=true|false] Some description --long-form2[=true|false] Some description with multiline -s, --long-name[=true|false] Some description -t, --long-name2[=true|false] Some description with multiline -o, --other-very-long-arg string Some very long description having break the limit (default "long-default-value") -l, --some-very-long-arg string Some very long description having break the limit (default "test") --some-very-long-arg2 string Some very long description with line break multiple (default "very long default value") ` func TestPrintUsage_2(t *testing.T) { buf := bytes.Buffer{} f := setUpPFlagSet2(&buf) res := f.FlagUsagesWrapped(80) if res != expectedOutput2 { t.Errorf("Expected \n%s \nActual \n%s", expectedOutput2, res) } } const expectedOutput3 = ` --mount string a mount specification e.g. 'type=bind,source=/opt,destination=/hostopt'. The rest of this description is wrapped. ` // A word wider than the description column has to overflow, but only that // word: wrapping resumes on the next one. func TestPrintUsage_WrapsAfterUnbreakableWord(t *testing.T) { f := NewFlagSet("test", ExitOnError) f.String("mount", "", "a mount specification e.g. 'type=bind,source=/opt,destination=/hostopt'. The rest of this description is wrapped.") res := f.FlagUsagesWrapped(60) if res != expectedOutput3 { t.Errorf("Expected \n%s \nActual \n%s", expectedOutput3, res) } } // No line may be longer than the requested width unless it is a single word // that cannot be broken at all. func TestPrintUsage_OnlyUnbreakableWordsOverflow(t *testing.T) { usages := []string{ "a mount specification e.g. 'type=bind,source=/opt,destination=/hostopt'. The rest of this description is wrapped.", "https://example.com/a/very/long/url/that/will/not/fit/in/the/column and some words after it to wrap", "averyveryverylongwordrightatthestart followed by several shorter words", "short words then averyveryverylongwordinthemiddle and more words after that one", "trailing text ending in anextremelylongunbreakabletokenattheveryend", } for _, width := range []int{40, 60, 80} { for _, usage := range usages { f := NewFlagSet("test", ExitOnError) f.String("mount", "", usage) lines := strings.Split(strings.TrimRight(f.FlagUsagesWrapped(width), "\n"), "\n") if len(lines) < 2 { t.Errorf("width %d, usage %q: expected the description to wrap", width, usage) continue } // Continuation lines are padded to the description column, so // their indent says where the description starts on every line. indent := len(lines[1]) - len(strings.TrimLeft(lines[1], " ")) for _, line := range lines { if len(line) <= width || indent >= len(line) { continue } if strings.Contains(strings.TrimSpace(line[indent:]), " ") { t.Errorf("width %d, usage %q: line is %d wide and still has a break point:\n%s", width, usage, len(line), line) } } } } }