blob: 534a51cb2bcf4e4f29cb051ca350115bfaac505b [file] [log] [blame]
package flags
import (
"bytes"
"fmt"
"os"
"runtime"
"testing"
"time"
)
type helpOptions struct {
Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information" ini-name:"verbose"`
Call func(string) `short:"c" description:"Call phone number" ini-name:"call"`
PtrSlice []*string `long:"ptrslice" description:"A slice of pointers to string"`
EmptyDescription bool `long:"empty-description"`
Default string `long:"default" default:"Some\nvalue" description:"Test default value"`
DefaultArray []string `long:"default-array" default:"Some value" default:"Other\tvalue" description:"Test default array value"`
DefaultMap map[string]string `long:"default-map" default:"some:value" default:"another:value" description:"Testdefault map value"`
EnvDefault1 string `long:"env-default1" default:"Some value" env:"ENV_DEFAULT" description:"Test env-default1 value"`
EnvDefault2 string `long:"env-default2" env:"ENV_DEFAULT" description:"Test env-default2 value"`
OptionWithArgName string `long:"opt-with-arg-name" value-name:"something" description:"Option with named argument"`
OptionWithChoices string `long:"opt-with-choices" value-name:"choice" choice:"dog" choice:"cat" description:"Option with choices"`
Hidden string `long:"hidden" description:"Hidden option" hidden:"yes"`
OnlyIni string `ini-name:"only-ini" description:"Option only available in ini"`
Other struct {
StringSlice []string `short:"s" default:"some" default:"value" description:"A slice of strings"`
IntMap map[string]int `long:"intmap" default:"a:1" description:"A map from string to int" ini-name:"int-map"`
} `group:"Other Options"`
HiddenGroup struct {
InsideHiddenGroup string `long:"inside-hidden-group" description:"Inside hidden group"`
} `group:"Hidden group" hidden:"yes"`
Group struct {
Opt string `long:"opt" description:"This is a subgroup option"`
HiddenInsideGroup string `long:"hidden-inside-group" description:"Hidden inside group" hidden:"yes"`
NotHiddenInsideGroup string `long:"not-hidden-inside-group" description:"Not hidden inside group" hidden:"false"`
Group struct {
Opt string `long:"opt" description:"This is a subsubgroup option"`
} `group:"Subsubgroup" namespace:"sap"`
} `group:"Subgroup" namespace:"sip"`
Command struct {
ExtraVerbose []bool `long:"extra-verbose" description:"Use for extra verbosity"`
} `command:"command" alias:"cm" alias:"cmd" description:"A command"`
HiddenCommand struct {
ExtraVerbose []bool `long:"extra-verbose" description:"Use for extra verbosity"`
} `command:"hidden-command" description:"A hidden command" hidden:"yes"`
Args struct {
Filename string `positional-arg-name:"filename" description:"A filename with a long description to trigger line wrapping"`
Number int `positional-arg-name:"num" description:"A number"`
HiddenInHelp float32 `positional-arg-name:"hidden-in-help" required:"yes"`
} `positional-args:"yes"`
}
func TestHelp(t *testing.T) {
oldEnv := EnvSnapshot()
defer oldEnv.Restore()
os.Setenv("ENV_DEFAULT", "env-def")
var opts helpOptions
p := NewNamedParser("TestHelp", HelpFlag)
p.AddGroup("Application Options", "The application options", &opts)
_, err := p.ParseArgs([]string{"--help"})
if err == nil {
t.Fatalf("Expected help error")
}
if e, ok := err.(*Error); !ok {
t.Fatalf("Expected flags.Error, but got %T", err)
} else {
if e.Type != ErrHelp {
t.Errorf("Expected flags.ErrHelp type, but got %s", e.Type)
}
var expected string
if runtime.GOOS == "windows" {
expected = `Usage:
TestHelp [OPTIONS] [filename] [num] [hidden-in-help] <command>
Application Options:
/v, /verbose Show verbose debug information
/c: Call phone number
/ptrslice: A slice of pointers to string
/empty-description
/default: Test default value (default:
"Some\nvalue")
/default-array: Test default array value (default:
Some value, "Other\tvalue")
/default-map: Testdefault map value (default:
some:value, another:value)
/env-default1: Test env-default1 value (default:
Some value) [%ENV_DEFAULT%]
/env-default2: Test env-default2 value
[%ENV_DEFAULT%]
/opt-with-arg-name:something Option with named argument
/opt-with-choices:choice[dog|cat] Option with choices
Other Options:
/s: A slice of strings (default: some,
value)
/intmap: A map from string to int (default:
a:1)
Subgroup:
/sip.opt: This is a subgroup option
/sip.not-hidden-inside-group: Not hidden inside group
Subsubgroup:
/sip.sap.opt: This is a subsubgroup option
Help Options:
/? Show this help message
/h, /help Show this help message
Arguments:
filename: A filename with a long description
to trigger line wrapping
num: A number
Available commands:
command A command (aliases: cm, cmd)
`
} else {
expected = `Usage:
TestHelp [OPTIONS] [filename] [num] [hidden-in-help] <command>
Application Options:
-v, --verbose Show verbose debug information
-c= Call phone number
--ptrslice= A slice of pointers to string
--empty-description
--default= Test default value (default:
"Some\nvalue")
--default-array= Test default array value (default:
Some value, "Other\tvalue")
--default-map= Testdefault map value (default:
some:value, another:value)
--env-default1= Test env-default1 value (default:
Some value) [$ENV_DEFAULT]
--env-default2= Test env-default2 value
[$ENV_DEFAULT]
--opt-with-arg-name=something Option with named argument
--opt-with-choices=choice[dog|cat] Option with choices
Other Options:
-s= A slice of strings (default: some,
value)
--intmap= A map from string to int (default:
a:1)
Subgroup:
--sip.opt= This is a subgroup option
--sip.not-hidden-inside-group= Not hidden inside group
Subsubgroup:
--sip.sap.opt= This is a subsubgroup option
Help Options:
-h, --help Show this help message
Arguments:
filename: A filename with a long description
to trigger line wrapping
num: A number
Available commands:
command A command (aliases: cm, cmd)
`
}
assertDiff(t, e.Message, expected, "help message")
}
}
func TestMan(t *testing.T) {
oldEnv := EnvSnapshot()
defer oldEnv.Restore()
os.Setenv("ENV_DEFAULT", "env-def")
var opts helpOptions
p := NewNamedParser("TestMan", HelpFlag)
p.ShortDescription = "Test manpage generation"
p.LongDescription = "This is a somewhat `longer' description of what this does"
p.AddGroup("Application Options", "The application options", &opts)
p.Commands()[0].LongDescription = "Longer `command' description"
var buf bytes.Buffer
p.WriteManPage(&buf)
got := buf.String()
tt := time.Now()
var envDefaultName string
if runtime.GOOS == "windows" {
envDefaultName = "%ENV_DEFAULT%"
} else {
envDefaultName = "$ENV_DEFAULT"
}
expected := fmt.Sprintf(`.TH TestMan 1 "%s"
.SH NAME
TestMan \- Test manpage generation
.SH SYNOPSIS
\fBTestMan\fP [OPTIONS]
.SH DESCRIPTION
This is a somewhat \fBlonger\fP description of what this does
.SH OPTIONS
.SS Application Options
The application options
.TP
\fB\fB\-v\fR, \fB\-\-verbose\fR\fP
Show verbose debug information
.TP
\fB\fB\-c\fR\fP
Call phone number
.TP
\fB\fB\-\-ptrslice\fR\fP
A slice of pointers to string
.TP
\fB\fB\-\-empty-description\fR\fP
.TP
\fB\fB\-\-default\fR <default: \fI"Some\\nvalue"\fR>\fP
Test default value
.TP
\fB\fB\-\-default-array\fR <default: \fI"Some value", "Other\\tvalue"\fR>\fP
Test default array value
.TP
\fB\fB\-\-default-map\fR <default: \fI"some:value", "another:value"\fR>\fP
Testdefault map value
.TP
\fB\fB\-\-env-default1\fR <default: \fI"Some value"\fR>\fP
Test env-default1 value
.TP
\fB\fB\-\-env-default2\fR <default: \fI%s\fR>\fP
Test env-default2 value
.TP
\fB\fB\-\-opt-with-arg-name\fR \fIsomething\fR\fP
Option with named argument
.TP
\fB\fB\-\-opt-with-choices\fR \fIchoice\fR\fP
Option with choices
.SS Other Options
.TP
\fB\fB\-s\fR <default: \fI"some", "value"\fR>\fP
A slice of strings
.TP
\fB\fB\-\-intmap\fR <default: \fI"a:1"\fR>\fP
A map from string to int
.SS Subgroup
.TP
\fB\fB\-\-sip.opt\fR\fP
This is a subgroup option
.TP
\fB\fB\-\-sip.not-hidden-inside-group\fR\fP
Not hidden inside group
.SS Subsubgroup
.TP
\fB\fB\-\-sip.sap.opt\fR\fP
This is a subsubgroup option
.SH COMMANDS
.SS command
A command
Longer \fBcommand\fP description
\fBUsage\fP: TestMan [OPTIONS] command [command-OPTIONS]
.TP
\fBAliases\fP: cm, cmd
.TP
\fB\fB\-\-extra-verbose\fR\fP
Use for extra verbosity
`, tt.Format("2 January 2006"), envDefaultName)
assertDiff(t, got, expected, "man page")
}
type helpCommandNoOptions struct {
Command struct {
} `command:"command" description:"A command"`
}
func TestHelpCommand(t *testing.T) {
oldEnv := EnvSnapshot()
defer oldEnv.Restore()
os.Setenv("ENV_DEFAULT", "env-def")
var opts helpCommandNoOptions
p := NewNamedParser("TestHelpCommand", HelpFlag)
p.AddGroup("Application Options", "The application options", &opts)
_, err := p.ParseArgs([]string{"command", "--help"})
if err == nil {
t.Fatalf("Expected help error")
}
if e, ok := err.(*Error); !ok {
t.Fatalf("Expected flags.Error, but got %T", err)
} else {
if e.Type != ErrHelp {
t.Errorf("Expected flags.ErrHelp type, but got %s", e.Type)
}
var expected string
if runtime.GOOS == "windows" {
expected = `Usage:
TestHelpCommand [OPTIONS] command
Help Options:
/? Show this help message
/h, /help Show this help message
`
} else {
expected = `Usage:
TestHelpCommand [OPTIONS] command
Help Options:
-h, --help Show this help message
`
}
assertDiff(t, e.Message, expected, "help message")
}
}
func TestHelpDefaults(t *testing.T) {
var expected string
if runtime.GOOS == "windows" {
expected = `Usage:
TestHelpDefaults [OPTIONS]
Application Options:
/with-default: With default (default: default-value)
/without-default: Without default
/with-programmatic-default: With programmatic default (default:
default-value)
Help Options:
/? Show this help message
/h, /help Show this help message
`
} else {
expected = `Usage:
TestHelpDefaults [OPTIONS]
Application Options:
--with-default= With default (default: default-value)
--without-default= Without default
--with-programmatic-default= With programmatic default (default:
default-value)
Help Options:
-h, --help Show this help message
`
}
tests := []struct {
Args []string
Output string
}{
{
Args: []string{"-h"},
Output: expected,
},
{
Args: []string{"--with-default", "other-value", "--with-programmatic-default", "other-value", "-h"},
Output: expected,
},
}
for _, test := range tests {
var opts struct {
WithDefault string `long:"with-default" default:"default-value" description:"With default"`
WithoutDefault string `long:"without-default" description:"Without default"`
WithProgrammaticDefault string `long:"with-programmatic-default" description:"With programmatic default"`
}
opts.WithProgrammaticDefault = "default-value"
p := NewNamedParser("TestHelpDefaults", HelpFlag)
p.AddGroup("Application Options", "The application options", &opts)
_, err := p.ParseArgs(test.Args)
if err == nil {
t.Fatalf("Expected help error")
}
if e, ok := err.(*Error); !ok {
t.Fatalf("Expected flags.Error, but got %T", err)
} else {
if e.Type != ErrHelp {
t.Errorf("Expected flags.ErrHelp type, but got %s", e.Type)
}
assertDiff(t, e.Message, test.Output, "help message")
}
}
}
func TestHelpRestArgs(t *testing.T) {
opts := struct {
Verbose bool `short:"v"`
}{}
p := NewNamedParser("TestHelpDefaults", HelpFlag)
p.AddGroup("Application Options", "The application options", &opts)
retargs, err := p.ParseArgs([]string{"-h", "-v", "rest"})
if err == nil {
t.Fatalf("Expected help error")
}
assertStringArray(t, retargs, []string{"-v", "rest"})
}
func TestWrapText(t *testing.T) {
s := "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
got := wrapText(s, 60, " ")
expected := `Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.`
assertDiff(t, got, expected, "wrapped text")
}
func TestWrapParagraph(t *testing.T) {
s := "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n\n"
s += "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n\n"
s += "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n\n"
s += "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n"
got := wrapText(s, 60, " ")
expected := `Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
`
assertDiff(t, got, expected, "wrapped paragraph")
}