udpate to use latest dep, update figtree

This commit is contained in:
Cory Bennett
2018-07-28 15:49:57 -07:00
parent eead13aef1
commit 462ef1c485
254 changed files with 698 additions and 43003 deletions
-4
View File
@@ -1,4 +0,0 @@
sudo: false
language: go
install: go get -t -v ./...
go: 1.2
-674
View File
@@ -1,674 +0,0 @@
# Kingpin - A Go (golang) command line and flag parser
[![](https://godoc.org/github.com/alecthomas/kingpin?status.svg)](http://godoc.org/github.com/alecthomas/kingpin) [![Build Status](https://travis-ci.org/alecthomas/kingpin.svg?branch=master)](https://travis-ci.org/alecthomas/kingpin) [![Gitter chat](https://badges.gitter.im/alecthomas.png)](https://gitter.im/alecthomas/Lobby)
<!-- MarkdownTOC -->
- [Overview](#overview)
- [Features](#features)
- [User-visible changes between v1 and v2](#user-visible-changes-between-v1-and-v2)
- [Flags can be used at any point after their definition.](#flags-can-be-used-at-any-point-after-their-definition)
- [Short flags can be combined with their parameters](#short-flags-can-be-combined-with-their-parameters)
- [API changes between v1 and v2](#api-changes-between-v1-and-v2)
- [Versions](#versions)
- [V2 is the current stable version](#v2-is-the-current-stable-version)
- [V1 is the OLD stable version](#v1-is-the-old-stable-version)
- [Change History](#change-history)
- [Examples](#examples)
- [Simple Example](#simple-example)
- [Complex Example](#complex-example)
- [Reference Documentation](#reference-documentation)
- [Displaying errors and usage information](#displaying-errors-and-usage-information)
- [Sub-commands](#sub-commands)
- [Custom Parsers](#custom-parsers)
- [Repeatable flags](#repeatable-flags)
- [Boolean Values](#boolean-values)
- [Default Values](#default-values)
- [Place-holders in Help](#place-holders-in-help)
- [Consuming all remaining arguments](#consuming-all-remaining-arguments)
- [Bash/ZSH Shell Completion](#bashzsh-shell-completion)
- [Supporting -h for help](#supporting--h-for-help)
- [Custom help](#custom-help)
<!-- /MarkdownTOC -->
## Overview
Kingpin is a [fluent-style](http://en.wikipedia.org/wiki/Fluent_interface),
type-safe command-line parser. It supports flags, nested commands, and
positional arguments.
Install it with:
$ go get gopkg.in/alecthomas/kingpin.v2
It looks like this:
```go
var (
verbose = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
name = kingpin.Arg("name", "Name of user.").Required().String()
)
func main() {
kingpin.Parse()
fmt.Printf("%v, %s\n", *verbose, *name)
}
```
More [examples](https://github.com/alecthomas/kingpin/tree/master/_examples) are available.
Second to parsing, providing the user with useful help is probably the most
important thing a command-line parser does. Kingpin tries to provide detailed
contextual help if `--help` is encountered at any point in the command line
(excluding after `--`).
## Features
- Help output that isn't as ugly as sin.
- Fully [customisable help](#custom-help), via Go templates.
- Parsed, type-safe flags (`kingpin.Flag("f", "help").Int()`)
- Parsed, type-safe positional arguments (`kingpin.Arg("a", "help").Int()`).
- Parsed, type-safe, arbitrarily deep commands (`kingpin.Command("c", "help")`).
- Support for required flags and required positional arguments (`kingpin.Flag("f", "").Required().Int()`).
- Support for arbitrarily nested default commands (`command.Default()`).
- Callbacks per command, flag and argument (`kingpin.Command("c", "").Action(myAction)`).
- POSIX-style short flag combining (`-a -b` -> `-ab`).
- Short-flag+parameter combining (`-a parm` -> `-aparm`).
- Read command-line from files (`@<file>`).
- Automatically generate man pages (`--help-man`).
## User-visible changes between v1 and v2
### Flags can be used at any point after their definition.
Flags can be specified at any point after their definition, not just
*immediately after their associated command*. From the chat example below, the
following used to be required:
```
$ chat --server=chat.server.com:8080 post --image=~/Downloads/owls.jpg pics
```
But the following will now work:
```
$ chat post --server=chat.server.com:8080 --image=~/Downloads/owls.jpg pics
```
### Short flags can be combined with their parameters
Previously, if a short flag was used, any argument to that flag would have to
be separated by a space. That is no longer the case.
## API changes between v1 and v2
- `ParseWithFileExpansion()` is gone. The new parser directly supports expanding `@<file>`.
- Added `FatalUsage()` and `FatalUsageContext()` for displaying an error + usage and terminating.
- `Dispatch()` renamed to `Action()`.
- Added `ParseContext()` for parsing a command line into its intermediate context form without executing.
- Added `Terminate()` function to override the termination function.
- Added `UsageForContextWithTemplate()` for printing usage via a custom template.
- Added `UsageTemplate()` for overriding the default template to use. Two templates are included:
1. `DefaultUsageTemplate` - default template.
2. `CompactUsageTemplate` - compact command template for larger applications.
## Versions
Kingpin uses [gopkg.in](https://gopkg.in/alecthomas/kingpin) for versioning.
The current stable version is [gopkg.in/alecthomas/kingpin.v2](https://gopkg.in/alecthomas/kingpin.v2). The previous version, [gopkg.in/alecthomas/kingpin.v1](https://gopkg.in/alecthomas/kingpin.v1), is deprecated and in maintenance mode.
### [V2](https://gopkg.in/alecthomas/kingpin.v2) is the current stable version
Installation:
```sh
$ go get gopkg.in/alecthomas/kingpin.v2
```
### [V1](https://gopkg.in/alecthomas/kingpin.v1) is the OLD stable version
Installation:
```sh
$ go get gopkg.in/alecthomas/kingpin.v1
```
## Change History
- *2015-09-19* -- Stable v2.1.0 release.
- Added `command.Default()` to specify a default command to use if no other
command matches. This allows for convenient user shortcuts.
- Exposed `HelpFlag` and `VersionFlag` for further customisation.
- `Action()` and `PreAction()` added and both now support an arbitrary
number of callbacks.
- `kingpin.SeparateOptionalFlagsUsageTemplate`.
- `--help-long` and `--help-man` (hidden by default) flags.
- Flags are "interspersed" by default, but can be disabled with `app.Interspersed(false)`.
- Added flags for all simple builtin types (int8, uint16, etc.) and slice variants.
- Use `app.Writer(os.Writer)` to specify the default writer for all output functions.
- Dropped `os.Writer` prefix from all printf-like functions.
- *2015-05-22* -- Stable v2.0.0 release.
- Initial stable release of v2.0.0.
- Fully supports interspersed flags, commands and arguments.
- Flags can be present at any point after their logical definition.
- Application.Parse() terminates if commands are present and a command is not parsed.
- Dispatch() -> Action().
- Actions are dispatched after all values are populated.
- Override termination function (defaults to os.Exit).
- Override output stream (defaults to os.Stderr).
- Templatised usage help, with default and compact templates.
- Make error/usage functions more consistent.
- Support argument expansion from files by default (with @<file>).
- Fully public data model is available via .Model().
- Parser has been completely refactored.
- Parsing and execution has been split into distinct stages.
- Use `go generate` to generate repeated flags.
- Support combined short-flag+argument: -fARG.
- *2015-01-23* -- Stable v1.3.4 release.
- Support "--" for separating flags from positional arguments.
- Support loading flags from files (ParseWithFileExpansion()). Use @FILE as an argument.
- Add post-app and post-cmd validation hooks. This allows arbitrary validation to be added.
- A bunch of improvements to help usage and formatting.
- Support arbitrarily nested sub-commands.
- *2014-07-08* -- Stable v1.2.0 release.
- Pass any value through to `Strings()` when final argument.
Allows for values that look like flags to be processed.
- Allow `--help` to be used with commands.
- Support `Hidden()` flags.
- Parser for [units.Base2Bytes](https://github.com/alecthomas/units)
type. Allows for flags like `--ram=512MB` or `--ram=1GB`.
- Add an `Enum()` value, allowing only one of a set of values
to be selected. eg. `Flag(...).Enum("debug", "info", "warning")`.
- *2014-06-27* -- Stable v1.1.0 release.
- Bug fixes.
- Always return an error (rather than panicing) when misconfigured.
- `OpenFile(flag, perm)` value type added, for finer control over opening files.
- Significantly improved usage formatting.
- *2014-06-19* -- Stable v1.0.0 release.
- Support [cumulative positional](#consuming-all-remaining-arguments) arguments.
- Return error rather than panic when there are fatal errors not caught by
the type system. eg. when a default value is invalid.
- Use gokpg.in.
- *2014-06-10* -- Place-holder streamlining.
- Renamed `MetaVar` to `PlaceHolder`.
- Removed `MetaVarFromDefault`. Kingpin now uses [heuristics](#place-holders-in-help)
to determine what to display.
## Examples
### Simple Example
Kingpin can be used for simple flag+arg applications like so:
```
$ ping --help
usage: ping [<flags>] <ip> [<count>]
Flags:
--debug Enable debug mode.
--help Show help.
-t, --timeout=5s Timeout waiting for ping.
Args:
<ip> IP address to ping.
[<count>] Number of packets to send
$ ping 1.2.3.4 5
Would ping: 1.2.3.4 with timeout 5s and count 0
```
From the following source:
```go
package main
import (
"fmt"
"gopkg.in/alecthomas/kingpin.v2"
)
var (
debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").OverrideDefaultFromEnvar("PING_TIMEOUT").Short('t').Duration()
ip = kingpin.Arg("ip", "IP address to ping.").Required().IP()
count = kingpin.Arg("count", "Number of packets to send").Int()
)
func main() {
kingpin.Version("0.0.1")
kingpin.Parse()
fmt.Printf("Would ping: %s with timeout %s and count %d\n", *ip, *timeout, *count)
}
```
### Complex Example
Kingpin can also produce complex command-line applications with global flags,
subcommands, and per-subcommand flags, like this:
```
$ chat --help
usage: chat [<flags>] <command> [<flags>] [<args> ...]
A command-line chat application.
Flags:
--help Show help.
--debug Enable debug mode.
--server=127.0.0.1 Server address.
Commands:
help [<command>]
Show help for a command.
register <nick> <name>
Register a new user.
post [<flags>] <channel> [<text>]
Post a message to a channel.
$ chat help post
usage: chat [<flags>] post [<flags>] <channel> [<text>]
Post a message to a channel.
Flags:
--image=IMAGE Image to post.
Args:
<channel> Channel to post to.
[<text>] Text to post.
$ chat post --image=~/Downloads/owls.jpg pics
...
```
From this code:
```go
package main
import (
"os"
"strings"
"gopkg.in/alecthomas/kingpin.v2"
)
var (
app = kingpin.New("chat", "A command-line chat application.")
debug = app.Flag("debug", "Enable debug mode.").Bool()
serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP()
register = app.Command("register", "Register a new user.")
registerNick = register.Arg("nick", "Nickname for user.").Required().String()
registerName = register.Arg("name", "Name of user.").Required().String()
post = app.Command("post", "Post a message to a channel.")
postImage = post.Flag("image", "Image to post.").File()
postChannel = post.Arg("channel", "Channel to post to.").Required().String()
postText = post.Arg("text", "Text to post.").Strings()
)
func main() {
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
// Register user
case register.FullCommand():
println(*registerNick)
// Post message
case post.FullCommand():
if *postImage != nil {
}
text := strings.Join(*postText, " ")
println("Post:", text)
}
}
```
## Reference Documentation
### Displaying errors and usage information
Kingpin exports a set of functions to provide consistent errors and usage
information to the user.
Error messages look something like this:
<app>: error: <message>
The functions on `Application` are:
Function | Purpose
---------|--------------
`Errorf(format, args)` | Display a printf formatted error to the user.
`Fatalf(format, args)` | As with Errorf, but also call the termination handler.
`FatalUsage(format, args)` | As with Fatalf, but also print contextual usage information.
`FatalUsageContext(context, format, args)` | As with Fatalf, but also print contextual usage information from a `ParseContext`.
`FatalIfError(err, format, args)` | Conditionally print an error prefixed with format+args, then call the termination handler
There are equivalent global functions in the kingpin namespace for the default
`kingpin.CommandLine` instance.
### Sub-commands
Kingpin supports nested sub-commands, with separate flag and positional
arguments per sub-command. Note that positional arguments may only occur after
sub-commands.
For example:
```go
var (
deleteCommand = kingpin.Command("delete", "Delete an object.")
deleteUserCommand = deleteCommand.Command("user", "Delete a user.")
deleteUserUIDFlag = deleteUserCommand.Flag("uid", "Delete user by UID rather than username.")
deleteUserUsername = deleteUserCommand.Arg("username", "Username to delete.")
deletePostCommand = deleteCommand.Command("post", "Delete a post.")
)
func main() {
switch kingpin.Parse() {
case "delete user":
case "delete post":
}
}
```
### Custom Parsers
Kingpin supports both flag and positional argument parsers for converting to
Go types. For example, some included parsers are `Int()`, `Float()`,
`Duration()` and `ExistingFile()` (see [parsers.go](./parsers.go) for a complete list of included parsers).
Parsers conform to Go's [`flag.Value`](http://godoc.org/flag#Value)
interface, so any existing implementations will work.
For example, a parser for accumulating HTTP header values might look like this:
```go
type HTTPHeaderValue http.Header
func (h *HTTPHeaderValue) Set(value string) error {
parts := strings.SplitN(value, ":", 2)
if len(parts) != 2 {
return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
}
(*http.Header)(h).Add(parts[0], parts[1])
return nil
}
func (h *HTTPHeaderValue) String() string {
return ""
}
```
As a convenience, I would recommend something like this:
```go
func HTTPHeader(s Settings) (target *http.Header) {
target = &http.Header{}
s.SetValue((*HTTPHeaderValue)(target))
return
}
```
You would use it like so:
```go
headers = HTTPHeader(kingpin.Flag("header", "Add a HTTP header to the request.").Short('H'))
```
### Repeatable flags
Depending on the `Value` they hold, some flags may be repeated. The
`IsCumulative() bool` function on `Value` tells if it's safe to call `Set()`
multiple times or if an error should be raised if several values are passed.
The built-in `Value`s returning slices and maps, as well as `Counter` are
examples of `Value`s that make a flag repeatable.
### Boolean values
Boolean values are uniquely managed by Kingpin. Each boolean flag will have a negative complement:
`--<name>` and `--no-<name>`.
### Default Values
The default value is the zero value for a type. This can be overridden with
the `Default(value...)` function on flags and arguments. This function accepts
one or several strings, which are parsed by the value itself, so they *must*
be compliant with the format expected.
### Place-holders in Help
The place-holder value for a flag is the value used in the help to describe
the value of a non-boolean flag.
The value provided to PlaceHolder() is used if provided, then the value
provided by Default() if provided, then finally the capitalised flag name is
used.
Here are some examples of flags with various permutations:
--name=NAME // Flag(...).String()
--name="Harry" // Flag(...).Default("Harry").String()
--name=FULL-NAME // flag(...).PlaceHolder("FULL-NAME").Default("Harry").String()
### Consuming all remaining arguments
A common command-line idiom is to use all remaining arguments for some
purpose. eg. The following command accepts an arbitrary number of
IP addresses as positional arguments:
./cmd ping 10.1.1.1 192.168.1.1
Such arguments are similar to [repeatable flags](#repeatable-flags), but for
arguments. Therefore they use the same `IsCumulative() bool` function on the
underlying `Value`, so the built-in `Value`s for which the `Set()` function
can be called several times will consume multiple arguments.
To implement the above example with a custom `Value`, we might do something
like this:
```go
type ipList []net.IP
func (i *ipList) Set(value string) error {
if ip := net.ParseIP(value); ip == nil {
return fmt.Errorf("'%s' is not an IP address", value)
} else {
*i = append(*i, ip)
return nil
}
}
func (i *ipList) String() string {
return ""
}
func (i *ipList) IsCumulative() bool {
return true
}
func IPList(s Settings) (target *[]net.IP) {
target = new([]net.IP)
s.SetValue((*ipList)(target))
return
}
```
And use it like so:
```go
ips := IPList(kingpin.Arg("ips", "IP addresses to ping."))
```
### Bash/ZSH Shell Completion
By default, all flags and commands/subcommands generate completions
internally.
Out of the box, CLI tools using kingpin should be able to take advantage
of completion hinting for flags and commands. By specifying
`--completion-bash` as the first argument, your CLI tool will show
possible subcommands. By ending your argv with `--`, hints for flags
will be shown.
To allow your end users to take advantage you must package a
`/etc/bash_completion.d` script with your distribution (or the equivalent
for your target platform/shell). An alternative is to instruct your end
user to source a script from their `bash_profile` (or equivalent).
Fortunately Kingpin makes it easy to generate or source a script for use
with end users shells. `./yourtool --completion-script-bash` and
`./yourtool --completion-script-zsh` will generate these scripts for you.
**Installation by Package**
For the best user experience, you should bundle your pre-created
completion script with your CLI tool and install it inside
`/etc/bash_completion.d` (or equivalent). A good suggestion is to add
this as an automated step to your build pipeline, in the implementation
is improved for bug fixed.
**Installation by `bash_profile`**
Alternatively, instruct your users to add an additional statement to
their `bash_profile` (or equivalent):
```
eval "$(your-cli-tool --completion-script-bash)"
```
Or for ZSH
```
eval "$(your-cli-tool --completion-script-zsh)"
```
#### Additional API
To provide more flexibility, a completion option API has been
exposed for flags to allow user defined completion options, to extend
completions further than just EnumVar/Enum.
**Provide Static Options**
When using an `Enum` or `EnumVar`, users are limited to only the options
given. Maybe we wish to hint possible options to the user, but also
allow them to provide their own custom option. `HintOptions` gives
this functionality to flags.
```
app := kingpin.New("completion", "My application with bash completion.")
app.Flag("port", "Provide a port to connect to").
Required().
HintOptions("80", "443", "8080").
IntVar(&c.port)
```
**Provide Dynamic Options**
Consider the case that you needed to read a local database or a file to
provide suggestions. You can dynamically generate the options
```
func listHosts() []string {
// Provide a dynamic list of hosts from a hosts file or otherwise
// for bash completion. In this example we simply return static slice.
// You could use this functionality to reach into a hosts file to provide
// completion for a list of known hosts.
return []string{"sshhost.example", "webhost.example", "ftphost.example"}
}
app := kingpin.New("completion", "My application with bash completion.")
app.Flag("flag-1", "").HintAction(listHosts).String()
```
**EnumVar/Enum**
When using `Enum` or `EnumVar`, any provided options will be automatically
used for bash autocompletion. However, if you wish to provide a subset or
different options, you can use `HintOptions` or `HintAction` which will override
the default completion options for `Enum`/`EnumVar`.
**Examples**
You can see an in depth example of the completion API within
`examples/completion/main.go`
### Supporting -h for help
`kingpin.CommandLine.HelpFlag.Short('h')`
### Custom help
Kingpin v2 supports templatised help using the text/template library (actually, [a fork](https://github.com/alecthomas/template)).
You can specify the template to use with the [Application.UsageTemplate()](http://godoc.org/gopkg.in/alecthomas/kingpin.v2#Application.UsageTemplate) function.
There are four included templates: `kingpin.DefaultUsageTemplate` is the default,
`kingpin.CompactUsageTemplate` provides a more compact representation for more complex command-line structures,
`kingpin.SeparateOptionalFlagsUsageTemplate` looks like the default template, but splits required
and optional command flags into separate lists, and `kingpin.ManPageTemplate` is used to generate man pages.
See the above templates for examples of usage, and the the function [UsageForContextWithTemplate()](https://github.com/alecthomas/kingpin/blob/master/usage.go#L198) method for details on the context.
#### Default help template
```
$ go run ./examples/curl/curl.go --help
usage: curl [<flags>] <command> [<args> ...]
An example implementation of curl.
Flags:
--help Show help.
-t, --timeout=5s Set connection timeout.
-H, --headers=HEADER=VALUE
Add HTTP headers to the request.
Commands:
help [<command>...]
Show help.
get url <url>
Retrieve a URL.
get file <file>
Retrieve a file.
post [<flags>] <url>
POST a resource.
```
#### Compact help template
```
$ go run ./examples/curl/curl.go --help
usage: curl [<flags>] <command> [<args> ...]
An example implementation of curl.
Flags:
--help Show help.
-t, --timeout=5s Set connection timeout.
-H, --headers=HEADER=VALUE
Add HTTP headers to the request.
Commands:
help [<command>...]
get [<flags>]
url <url>
file <file>
post [<flags>] <url>
```
-404
View File
@@ -1,404 +0,0 @@
package kingpin
import (
"io/ioutil"
"github.com/alecthomas/assert"
"sort"
"strings"
"testing"
"time"
)
func newTestApp() *Application {
return New("test", "").Terminate(nil)
}
func TestCommander(t *testing.T) {
c := newTestApp()
ping := c.Command("ping", "Ping an IP address.")
pingTTL := ping.Flag("ttl", "TTL for ICMP packets").Short('t').Default("5s").Duration()
selected, err := c.Parse([]string{"ping"})
assert.NoError(t, err)
assert.Equal(t, "ping", selected)
assert.Equal(t, 5*time.Second, *pingTTL)
selected, err = c.Parse([]string{"ping", "--ttl=10s"})
assert.NoError(t, err)
assert.Equal(t, "ping", selected)
assert.Equal(t, 10*time.Second, *pingTTL)
}
func TestRequiredFlags(t *testing.T) {
c := newTestApp()
c.Flag("a", "a").String()
c.Flag("b", "b").Required().String()
_, err := c.Parse([]string{"--a=foo"})
assert.Error(t, err)
_, err = c.Parse([]string{"--b=foo"})
assert.NoError(t, err)
}
func TestRepeatableFlags(t *testing.T) {
c := newTestApp()
c.Flag("a", "a").String()
c.Flag("b", "b").Strings()
_, err := c.Parse([]string{"--a=foo", "--a=bar"})
assert.Error(t, err)
_, err = c.Parse([]string{"--b=foo", "--b=bar"})
assert.NoError(t, err)
}
func TestInvalidDefaultFlagValueErrors(t *testing.T) {
c := newTestApp()
c.Flag("foo", "foo").Default("a").Int()
_, err := c.Parse([]string{})
assert.Error(t, err)
}
func TestInvalidDefaultArgValueErrors(t *testing.T) {
c := newTestApp()
cmd := c.Command("cmd", "cmd")
cmd.Arg("arg", "arg").Default("one").Int()
_, err := c.Parse([]string{"cmd"})
assert.Error(t, err)
}
func TestArgsRequiredAfterNonRequiredErrors(t *testing.T) {
c := newTestApp()
cmd := c.Command("cmd", "")
cmd.Arg("a", "a").String()
cmd.Arg("b", "b").Required().String()
_, err := c.Parse([]string{"cmd"})
assert.Error(t, err)
}
func TestArgsMultipleRequiredThenNonRequired(t *testing.T) {
c := newTestApp().Writer(ioutil.Discard)
cmd := c.Command("cmd", "")
cmd.Arg("a", "a").Required().String()
cmd.Arg("b", "b").Required().String()
cmd.Arg("c", "c").String()
cmd.Arg("d", "d").String()
_, err := c.Parse([]string{"cmd", "a", "b"})
assert.NoError(t, err)
_, err = c.Parse([]string{})
assert.Error(t, err)
}
func TestDispatchCallbackIsCalled(t *testing.T) {
dispatched := false
c := newTestApp()
c.Command("cmd", "").Action(func(*ParseContext) error {
dispatched = true
return nil
})
_, err := c.Parse([]string{"cmd"})
assert.NoError(t, err)
assert.True(t, dispatched)
}
func TestTopLevelArgWorks(t *testing.T) {
c := newTestApp()
s := c.Arg("arg", "help").String()
_, err := c.Parse([]string{"foo"})
assert.NoError(t, err)
assert.Equal(t, "foo", *s)
}
func TestTopLevelArgCantBeUsedWithCommands(t *testing.T) {
c := newTestApp()
c.Arg("arg", "help").String()
c.Command("cmd", "help")
_, err := c.Parse([]string{})
assert.Error(t, err)
}
func TestTooManyArgs(t *testing.T) {
a := newTestApp()
a.Arg("a", "").String()
_, err := a.Parse([]string{"a", "b"})
assert.Error(t, err)
}
func TestTooManyArgsAfterCommand(t *testing.T) {
a := newTestApp()
a.Command("a", "")
assert.NoError(t, a.init())
_, err := a.Parse([]string{"a", "b"})
assert.Error(t, err)
}
func TestArgsLooksLikeFlagsWithConsumeRemainder(t *testing.T) {
a := newTestApp()
a.Arg("opts", "").Required().Strings()
_, err := a.Parse([]string{"hello", "-world"})
assert.Error(t, err)
}
func TestCommandParseDoesNotResetFlagsToDefault(t *testing.T) {
app := newTestApp()
flag := app.Flag("flag", "").Default("default").String()
app.Command("cmd", "")
_, err := app.Parse([]string{"--flag=123", "cmd"})
assert.NoError(t, err)
assert.Equal(t, "123", *flag)
}
func TestCommandParseDoesNotFailRequired(t *testing.T) {
app := newTestApp()
flag := app.Flag("flag", "").Required().String()
app.Command("cmd", "")
_, err := app.Parse([]string{"cmd", "--flag=123"})
assert.NoError(t, err)
assert.Equal(t, "123", *flag)
}
func TestSelectedCommand(t *testing.T) {
app := newTestApp()
c0 := app.Command("c0", "")
c0.Command("c1", "")
s, err := app.Parse([]string{"c0", "c1"})
assert.NoError(t, err)
assert.Equal(t, "c0 c1", s)
}
func TestSubCommandRequired(t *testing.T) {
app := newTestApp()
c0 := app.Command("c0", "")
c0.Command("c1", "")
_, err := app.Parse([]string{"c0"})
assert.Error(t, err)
}
func TestInterspersedFalse(t *testing.T) {
app := newTestApp().Interspersed(false)
a1 := app.Arg("a1", "").String()
a2 := app.Arg("a2", "").String()
f1 := app.Flag("flag", "").String()
_, err := app.Parse([]string{"a1", "--flag=flag"})
assert.NoError(t, err)
assert.Equal(t, "a1", *a1)
assert.Equal(t, "--flag=flag", *a2)
assert.Equal(t, "", *f1)
}
func TestInterspersedTrue(t *testing.T) {
// test once with the default value and once with explicit true
for i := 0; i < 2; i++ {
app := newTestApp()
if i != 0 {
t.Log("Setting explicit")
app.Interspersed(true)
} else {
t.Log("Using default")
}
a1 := app.Arg("a1", "").String()
a2 := app.Arg("a2", "").String()
f1 := app.Flag("flag", "").String()
_, err := app.Parse([]string{"a1", "--flag=flag"})
assert.NoError(t, err)
assert.Equal(t, "a1", *a1)
assert.Equal(t, "", *a2)
assert.Equal(t, "flag", *f1)
}
}
func TestDefaultEnvars(t *testing.T) {
a := New("some-app", "").Terminate(nil).DefaultEnvars()
f0 := a.Flag("some-flag", "")
f0.Bool()
f1 := a.Flag("some-other-flag", "").NoEnvar()
f1.Bool()
f2 := a.Flag("a-1-flag", "")
f2.Bool()
_, err := a.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, "SOME_APP_SOME_FLAG", f0.envar)
assert.Equal(t, "", f1.envar)
assert.Equal(t, "SOME_APP_A_1_FLAG", f2.envar)
}
func TestBashCompletionOptionsWithEmptyApp(t *testing.T) {
a := newTestApp()
context, err := a.ParseContext([]string{"--completion-bash"})
if err != nil {
t.Errorf("Unexpected error whilst parsing context: [%v]", err)
}
args := a.completionOptions(context)
assert.Equal(t, []string(nil), args)
}
func TestBashCompletionOptions(t *testing.T) {
a := newTestApp()
a.Command("one", "")
a.Flag("flag-0", "").String()
a.Flag("flag-1", "").HintOptions("opt1", "opt2", "opt3").String()
two := a.Command("two", "")
two.Flag("flag-2", "").String()
two.Flag("flag-3", "").HintOptions("opt4", "opt5", "opt6").String()
three := a.Command("three", "")
three.Flag("flag-4", "").String()
three.Arg("arg-1", "").String()
three.Arg("arg-2", "").HintOptions("arg-2-opt-1", "arg-2-opt-2").String()
three.Arg("arg-3", "").String()
three.Arg("arg-4", "").HintAction(func() []string {
return []string{"arg-4-opt-1", "arg-4-opt-2"}
}).String()
cases := []struct {
Args string
ExpectedOptions []string
}{
{
Args: "--completion-bash",
ExpectedOptions: []string{"help", "one", "three", "two"},
},
{
Args: "--completion-bash --",
ExpectedOptions: []string{"--flag-0", "--flag-1", "--help"},
},
{
Args: "--completion-bash --fla",
ExpectedOptions: []string{"--flag-0", "--flag-1", "--help"},
},
{
// No options available for flag-0, return to cmd completion
Args: "--completion-bash --flag-0",
ExpectedOptions: []string{"help", "one", "three", "two"},
},
{
Args: "--completion-bash --flag-0 --",
ExpectedOptions: []string{"--flag-0", "--flag-1", "--help"},
},
{
Args: "--completion-bash --flag-1",
ExpectedOptions: []string{"opt1", "opt2", "opt3"},
},
{
Args: "--completion-bash --flag-1 opt",
ExpectedOptions: []string{"opt1", "opt2", "opt3"},
},
{
Args: "--completion-bash --flag-1 opt1",
ExpectedOptions: []string{"help", "one", "three", "two"},
},
{
Args: "--completion-bash --flag-1 opt1 --",
ExpectedOptions: []string{"--flag-0", "--flag-1", "--help"},
},
// Try Subcommand
{
Args: "--completion-bash two",
ExpectedOptions: []string(nil),
},
{
Args: "--completion-bash two --",
ExpectedOptions: []string{"--help", "--flag-2", "--flag-3", "--flag-0", "--flag-1"},
},
{
Args: "--completion-bash two --flag",
ExpectedOptions: []string{"--help", "--flag-2", "--flag-3", "--flag-0", "--flag-1"},
},
{
Args: "--completion-bash two --flag-2",
ExpectedOptions: []string(nil),
},
{
// Top level flags carry downwards
Args: "--completion-bash two --flag-1",
ExpectedOptions: []string{"opt1", "opt2", "opt3"},
},
{
// Top level flags carry downwards
Args: "--completion-bash two --flag-1 opt",
ExpectedOptions: []string{"opt1", "opt2", "opt3"},
},
{
// Top level flags carry downwards
Args: "--completion-bash two --flag-1 opt1",
ExpectedOptions: []string(nil),
},
{
Args: "--completion-bash two --flag-3",
ExpectedOptions: []string{"opt4", "opt5", "opt6"},
},
{
Args: "--completion-bash two --flag-3 opt",
ExpectedOptions: []string{"opt4", "opt5", "opt6"},
},
{
Args: "--completion-bash two --flag-3 opt4",
ExpectedOptions: []string(nil),
},
{
Args: "--completion-bash two --flag-3 opt4 --",
ExpectedOptions: []string{"--help", "--flag-2", "--flag-3", "--flag-0", "--flag-1"},
},
// Args complete
{
// After a command with an arg with no options, nothing should be
// shown
Args: "--completion-bash three ",
ExpectedOptions: []string(nil),
},
{
// After a command with an arg, explicitly starting a flag should
// complete flags
Args: "--completion-bash three --",
ExpectedOptions: []string{"--flag-0", "--flag-1", "--flag-4", "--help"},
},
{
// After a command with an arg that does have completions, they
// should be shown
Args: "--completion-bash three arg1 ",
ExpectedOptions: []string{"arg-2-opt-1", "arg-2-opt-2"},
},
{
// After a command with an arg that does have completions, but a
// flag is started, flag options should be completed
Args: "--completion-bash three arg1 --",
ExpectedOptions: []string{"--flag-0", "--flag-1", "--flag-4", "--help"},
},
{
// After a command with an arg that has no completions, and isn't first,
// nothing should be shown
Args: "--completion-bash three arg1 arg2 ",
ExpectedOptions: []string(nil),
},
{
// After a command with a different arg that also has completions,
// those different options should be shown
Args: "--completion-bash three arg1 arg2 arg3 ",
ExpectedOptions: []string{"arg-4-opt-1", "arg-4-opt-2"},
},
{
// After a command with all args listed, nothing should complete
Args: "--completion-bash three arg1 arg2 arg3 arg4",
ExpectedOptions: []string(nil),
},
}
for _, c := range cases {
context, _ := a.ParseContext(strings.Split(c.Args, " "))
args := a.completionOptions(context)
sort.Strings(args)
sort.Strings(c.ExpectedOptions)
assert.Equal(t, c.ExpectedOptions, args, "Expected != Actual: [%v] != [%v]. \nInput was: [%v]", c.ExpectedOptions, args, c.Args)
}
}
-84
View File
@@ -1,84 +0,0 @@
package kingpin
import (
"io/ioutil"
"os"
"testing"
"github.com/alecthomas/assert"
)
func TestArgRemainder(t *testing.T) {
app := New("test", "")
v := app.Arg("test", "").Strings()
args := []string{"hello", "world"}
_, err := app.Parse(args)
assert.NoError(t, err)
assert.Equal(t, args, *v)
}
func TestArgRemainderErrorsWhenNotLast(t *testing.T) {
a := newArgGroup()
a.Arg("test", "").Strings()
a.Arg("test2", "").String()
assert.Error(t, a.init())
}
func TestArgMultipleRequired(t *testing.T) {
terminated := false
app := New("test", "")
app.Version("0.0.0").Writer(ioutil.Discard)
app.Arg("a", "").Required().String()
app.Arg("b", "").Required().String()
app.Terminate(func(int) { terminated = true })
_, err := app.Parse([]string{})
assert.Error(t, err)
_, err = app.Parse([]string{"A"})
assert.Error(t, err)
_, err = app.Parse([]string{"A", "B"})
assert.NoError(t, err)
_, err = app.Parse([]string{"--version"})
assert.True(t, terminated)
}
func TestInvalidArgsDefaultCanBeOverridden(t *testing.T) {
app := New("test", "")
app.Arg("a", "").Default("invalid").Bool()
_, err := app.Parse([]string{})
assert.Error(t, err)
}
func TestArgMultipleValuesDefault(t *testing.T) {
app := New("test", "")
a := app.Arg("a", "").Default("default1", "default2").Strings()
_, err := app.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, []string{"default1", "default2"}, *a)
}
func TestRequiredArgWithEnvarMissingErrors(t *testing.T) {
app := newTestApp()
app.Arg("t", "").Envar("TEST_ARG_ENVAR").Required().Int()
_, err := app.Parse([]string{})
assert.Error(t, err)
}
func TestArgRequiredWithEnvar(t *testing.T) {
os.Setenv("TEST_ARG_ENVAR", "123")
app := newTestApp()
flag := app.Arg("t", "").Envar("TEST_ARG_ENVAR").Required().Int()
_, err := app.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, 123, *flag)
}
func TestSubcommandArgRequiredWithEnvar(t *testing.T) {
os.Setenv("TEST_ARG_ENVAR", "123")
app := newTestApp()
cmd := app.Command("command", "")
flag := cmd.Arg("t", "").Envar("TEST_ARG_ENVAR").Required().Int()
_, err := app.Parse([]string{"command"})
assert.NoError(t, err)
assert.Equal(t, 123, *flag)
}
-374
View File
@@ -1,374 +0,0 @@
package kingpin
import (
"sort"
"strings"
"github.com/alecthomas/assert"
"testing"
)
func parseAndExecute(app *Application, context *ParseContext) (string, error) {
if err := parse(context, app); err != nil {
return "", err
}
selected, err := app.setValues(context)
if err != nil {
return "", err
}
return app.execute(context, selected)
}
func complete(t *testing.T, app *Application, args ...string) []string {
context, err := app.ParseContext(args)
assert.NoError(t, err)
if err != nil {
return nil
}
completions := app.completionOptions(context)
sort.Strings(completions)
return completions
}
func TestNestedCommands(t *testing.T) {
app := New("app", "")
sub1 := app.Command("sub1", "")
sub1.Flag("sub1", "")
subsub1 := sub1.Command("sub1sub1", "")
subsub1.Command("sub1sub1end", "")
sub2 := app.Command("sub2", "")
sub2.Flag("sub2", "")
sub2.Command("sub2sub1", "")
context := tokenize([]string{"sub1", "sub1sub1", "sub1sub1end"}, false)
selected, err := parseAndExecute(app, context)
assert.NoError(t, err)
assert.True(t, context.EOL())
assert.Equal(t, "sub1 sub1sub1 sub1sub1end", selected)
}
func TestNestedCommandsWithArgs(t *testing.T) {
app := New("app", "")
cmd := app.Command("a", "").Command("b", "")
a := cmd.Arg("a", "").String()
b := cmd.Arg("b", "").String()
context := tokenize([]string{"a", "b", "c", "d"}, false)
selected, err := parseAndExecute(app, context)
assert.NoError(t, err)
assert.True(t, context.EOL())
assert.Equal(t, "a b", selected)
assert.Equal(t, "c", *a)
assert.Equal(t, "d", *b)
}
func TestNestedCommandsWithFlags(t *testing.T) {
app := New("app", "")
cmd := app.Command("a", "").Command("b", "")
a := cmd.Flag("aaa", "").Short('a').String()
b := cmd.Flag("bbb", "").Short('b').String()
err := app.init()
assert.NoError(t, err)
context := tokenize(strings.Split("a b --aaa x -b x", " "), false)
selected, err := parseAndExecute(app, context)
assert.NoError(t, err)
assert.True(t, context.EOL())
assert.Equal(t, "a b", selected)
assert.Equal(t, "x", *a)
assert.Equal(t, "x", *b)
}
func TestNestedCommandWithMergedFlags(t *testing.T) {
app := New("app", "")
cmd0 := app.Command("a", "")
cmd0f0 := cmd0.Flag("aflag", "").Bool()
// cmd1 := app.Command("b", "")
// cmd1f0 := cmd0.Flag("bflag", "").Bool()
cmd00 := cmd0.Command("aa", "")
cmd00f0 := cmd00.Flag("aaflag", "").Bool()
err := app.init()
assert.NoError(t, err)
context := tokenize(strings.Split("a aa --aflag --aaflag", " "), false)
selected, err := parseAndExecute(app, context)
assert.NoError(t, err)
assert.True(t, *cmd0f0)
assert.True(t, *cmd00f0)
assert.Equal(t, "a aa", selected)
}
func TestNestedCommandWithDuplicateFlagErrors(t *testing.T) {
app := New("app", "")
app.Flag("test", "").Bool()
app.Command("cmd0", "").Flag("test", "").Bool()
err := app.init()
assert.Error(t, err)
}
func TestNestedCommandWithArgAndMergedFlags(t *testing.T) {
app := New("app", "")
cmd0 := app.Command("a", "")
cmd0f0 := cmd0.Flag("aflag", "").Bool()
// cmd1 := app.Command("b", "")
// cmd1f0 := cmd0.Flag("bflag", "").Bool()
cmd00 := cmd0.Command("aa", "")
cmd00a0 := cmd00.Arg("arg", "").String()
cmd00f0 := cmd00.Flag("aaflag", "").Bool()
err := app.init()
assert.NoError(t, err)
context := tokenize(strings.Split("a aa hello --aflag --aaflag", " "), false)
selected, err := parseAndExecute(app, context)
assert.NoError(t, err)
assert.True(t, *cmd0f0)
assert.True(t, *cmd00f0)
assert.Equal(t, "a aa", selected)
assert.Equal(t, "hello", *cmd00a0)
}
func TestDefaultSubcommandEOL(t *testing.T) {
app := newTestApp()
c0 := app.Command("c0", "").Default()
c0.Command("c01", "").Default()
c0.Command("c02", "")
cmd, err := app.Parse([]string{"c0"})
assert.NoError(t, err)
assert.Equal(t, "c0 c01", cmd)
}
func TestDefaultSubcommandWithArg(t *testing.T) {
app := newTestApp()
c0 := app.Command("c0", "").Default()
c01 := c0.Command("c01", "").Default()
c012 := c01.Command("c012", "").Default()
a0 := c012.Arg("a0", "").String()
c0.Command("c02", "")
cmd, err := app.Parse([]string{"c0", "hello"})
assert.NoError(t, err)
assert.Equal(t, "c0 c01 c012", cmd)
assert.Equal(t, "hello", *a0)
}
func TestDefaultSubcommandWithFlags(t *testing.T) {
app := newTestApp()
c0 := app.Command("c0", "").Default()
_ = c0.Flag("f0", "").Int()
c0c1 := c0.Command("c1", "").Default()
c0c1f1 := c0c1.Flag("f1", "").Int()
selected, err := app.Parse([]string{"--f1=2"})
assert.NoError(t, err)
assert.Equal(t, "c0 c1", selected)
assert.Equal(t, 2, *c0c1f1)
_, err = app.Parse([]string{"--f2"})
assert.Error(t, err)
}
func TestMultipleDefaultCommands(t *testing.T) {
app := newTestApp()
app.Command("c0", "").Default()
app.Command("c1", "").Default()
_, err := app.Parse([]string{})
assert.Error(t, err)
}
func TestAliasedCommand(t *testing.T) {
app := newTestApp()
app.Command("one", "").Alias("two")
selected, _ := app.Parse([]string{"one"})
assert.Equal(t, "one", selected)
selected, _ = app.Parse([]string{"two"})
assert.Equal(t, "one", selected)
// 2 due to "help" and "one"
assert.Equal(t, 2, len(app.Model().FlattenedCommands()))
}
func TestDuplicateAlias(t *testing.T) {
app := newTestApp()
app.Command("one", "")
app.Command("two", "").Alias("one")
_, err := app.Parse([]string{"one"})
assert.Error(t, err)
}
func TestFlagCompletion(t *testing.T) {
app := newTestApp()
app.Command("one", "")
two := app.Command("two", "")
two.Flag("flag-1", "")
two.Flag("flag-2", "").HintOptions("opt1", "opt2", "opt3")
two.Flag("flag-3", "")
cases := []struct {
target cmdMixin
flagName string
flagValue string
expectedFlagMatch bool
expectedOptionMatch bool
expectedFlags []string
}{
{
// Test top level flags
target: app.cmdMixin,
flagName: "",
flagValue: "",
expectedFlagMatch: false,
expectedOptionMatch: false,
expectedFlags: []string{"--help"},
},
{
// Test no flag passed
target: two.cmdMixin,
flagName: "",
flagValue: "",
expectedFlagMatch: false,
expectedOptionMatch: false,
expectedFlags: []string{"--flag-1", "--flag-2", "--flag-3"},
},
{
// Test an incomplete flag. Should still give all options as if the flag wasn't given at all.
target: two.cmdMixin,
flagName: "flag-",
flagValue: "",
expectedFlagMatch: false,
expectedOptionMatch: false,
expectedFlags: []string{"--flag-1", "--flag-2", "--flag-3"},
},
{
// Test with a complete flag. Should show available choices for the flag
// This flag has no options. No options should be produced.
// Should also report an option was matched
target: two.cmdMixin,
flagName: "flag-1",
flagValue: "",
expectedFlagMatch: true,
expectedOptionMatch: true,
expectedFlags: []string(nil),
},
{
// Test with a complete flag. Should show available choices for the flag
target: two.cmdMixin,
flagName: "flag-2",
flagValue: "",
expectedFlagMatch: true,
expectedOptionMatch: false,
expectedFlags: []string{"opt1", "opt2", "opt3"},
},
{
// Test with a complete flag and complete option for that flag.
target: two.cmdMixin,
flagName: "flag-2",
flagValue: "opt1",
expectedFlagMatch: true,
expectedOptionMatch: true,
expectedFlags: []string{"opt1", "opt2", "opt3"},
},
}
for i, c := range cases {
choices, flagMatch, optionMatch := c.target.FlagCompletion(c.flagName, c.flagValue)
assert.Equal(t, c.expectedFlags, choices, "Test case %d: expectedFlags != actual flags", i+1)
assert.Equal(t, c.expectedFlagMatch, flagMatch, "Test case %d: expectedFlagMatch != flagMatch", i+1)
assert.Equal(t, c.expectedOptionMatch, optionMatch, "Test case %d: expectedOptionMatch != optionMatch", i+1)
}
}
func TestCmdCompletion(t *testing.T) {
app := newTestApp()
app.Command("one", "")
two := app.Command("two", "")
two.Command("sub1", "")
two.Command("sub2", "")
assert.Equal(t, []string{"help", "one", "two"}, complete(t, app))
assert.Equal(t, []string{"sub1", "sub2"}, complete(t, app, "two"))
}
func TestHiddenCmdCompletion(t *testing.T) {
app := newTestApp()
// top level visible & hidden cmds, with no sub-cmds
app.Command("visible1", "")
app.Command("hidden1", "").Hidden()
// visible cmd with visible & hidden sub-cmds
visible2 := app.Command("visible2", "")
visible2.Command("visible2-visible", "")
visible2.Command("visible2-hidden", "").Hidden()
// hidden cmd with visible & hidden sub-cmds
hidden2 := app.Command("hidden2", "").Hidden()
hidden2.Command("hidden2-visible", "")
hidden2.Command("hidden2-hidden", "").Hidden()
// Only top level visible cmds should show
assert.Equal(t, []string{"help", "visible1", "visible2"}, complete(t, app))
// Only visible sub-cmds should show
assert.Equal(t, []string{"visible2-visible"}, complete(t, app, "visible2"))
// Hidden commands should still complete visible sub-cmds
assert.Equal(t, []string{"hidden2-visible"}, complete(t, app, "hidden2"))
}
func TestDefaultCmdCompletion(t *testing.T) {
app := newTestApp()
cmd1 := app.Command("cmd1", "")
cmd1Sub1 := cmd1.Command("cmd1-sub1", "")
cmd1Sub1.Arg("cmd1-sub1-arg1", "").HintOptions("cmd1-arg1").String()
cmd2 := app.Command("cmd2", "").Default()
cmd2.Command("cmd2-sub1", "")
cmd2Sub2 := cmd2.Command("cmd2-sub2", "").Default()
cmd2Sub2Sub1 := cmd2Sub2.Command("cmd2-sub2-sub1", "").Default()
cmd2Sub2Sub1.Arg("cmd2-sub2-sub1-arg1", "").HintOptions("cmd2-sub2-sub1-arg1").String()
cmd2Sub2Sub1.Arg("cmd2-sub2-sub1-arg2", "").HintOptions("cmd2-sub2-sub1-arg2").String()
// Without args, should get:
// - root cmds (including implicit "help")
// - thread of default cmds
// - first arg hints for the final default cmd
assert.Equal(t, []string{"cmd1", "cmd2", "cmd2-sub1", "cmd2-sub2", "cmd2-sub2-sub1", "cmd2-sub2-sub1-arg1", "help"}, complete(t, app))
// With a non-default cmd already listed, should get:
// - sub cmds of that arg
assert.Equal(t, []string{"cmd1-sub1"}, complete(t, app, "cmd1"))
// With an explicit default cmd listed, should get:
// - default child-cmds
// - first arg hints for the final default cmd
assert.Equal(t, []string{"cmd2-sub1", "cmd2-sub2", "cmd2-sub2-sub1", "cmd2-sub2-sub1-arg1"}, complete(t, app, "cmd2"))
// Args should be completed when all preceding cmds are explicit, and when
// any of them are implicit (not listed). Check this by trying all possible
// combinations of choosing/excluding the three levels of cmds. This tests
// root-level default, middle default, and end default.
for i := 0; i < 8; i++ {
var cmdline []string
if i&1 != 0 {
cmdline = append(cmdline, "cmd2")
}
if i&2 != 0 {
cmdline = append(cmdline, "cmd2-sub2")
}
if i&4 != 0 {
cmdline = append(cmdline, "cmd2-sub2-sub1")
}
assert.Contains(t, complete(t, app, cmdline...), "cmd2-sub2-sub1-arg1", "with cmdline: %v", cmdline)
}
// With both args of a default sub cmd, should get no completions
assert.Empty(t, complete(t, app, "arg1", "arg2"))
}
-78
View File
@@ -1,78 +0,0 @@
package kingpin
import (
"testing"
"github.com/alecthomas/assert"
)
func TestResolveWithBuiltin(t *testing.T) {
a := completionsMixin{}
hintAction1 := func() []string {
return []string{"opt1", "opt2"}
}
hintAction2 := func() []string {
return []string{"opt3", "opt4"}
}
a.builtinHintActions = []HintAction{hintAction1, hintAction2}
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2", "opt3", "opt4"}, args)
}
func TestResolveWithUser(t *testing.T) {
a := completionsMixin{}
hintAction1 := func() []string {
return []string{"opt1", "opt2"}
}
hintAction2 := func() []string {
return []string{"opt3", "opt4"}
}
a.hintActions = []HintAction{hintAction1, hintAction2}
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2", "opt3", "opt4"}, args)
}
func TestResolveWithCombination(t *testing.T) {
a := completionsMixin{}
builtin := func() []string {
return []string{"opt1", "opt2"}
}
user := func() []string {
return []string{"opt3", "opt4"}
}
a.builtinHintActions = []HintAction{builtin}
a.hintActions = []HintAction{user}
args := a.resolveCompletions()
// User provided args take preference over builtin (enum-defined) args.
assert.Equal(t, []string{"opt3", "opt4"}, args)
}
func TestAddHintAction(t *testing.T) {
a := completionsMixin{}
hintFunc := func() []string {
return []string{"opt1", "opt2"}
}
a.addHintAction(hintFunc)
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2"}, args)
}
func TestAddHintActionBuiltin(t *testing.T) {
a := completionsMixin{}
hintFunc := func() []string {
return []string{"opt1", "opt2"}
}
a.addHintActionBuiltin(hintFunc)
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2"}, args)
}
-46
View File
@@ -1,46 +0,0 @@
package kingpin
import (
"fmt"
"net/http"
"strings"
)
type HTTPHeaderValue http.Header
func (h *HTTPHeaderValue) Set(value string) error {
parts := strings.SplitN(value, ":", 2)
if len(parts) != 2 {
return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
}
(*http.Header)(h).Add(parts[0], parts[1])
return nil
}
func (h *HTTPHeaderValue) Get() interface{} {
return (http.Header)(*h)
}
func (h *HTTPHeaderValue) String() string {
return ""
}
func HTTPHeader(s Settings) (target *http.Header) {
target = new(http.Header)
s.SetValue((*HTTPHeaderValue)(target))
return
}
// This example ilustrates how to define custom parsers. HTTPHeader
// cumulatively parses each encountered --header flag into a http.Header struct.
func ExampleValue() {
var (
curl = New("curl", "transfer a URL")
headers = HTTPHeader(curl.Flag("headers", "Add HTTP headers to the request.").Short('H').PlaceHolder("HEADER:VALUE"))
)
curl.Parse([]string{"-H Content-Type:application/octet-stream"})
for key, value := range *headers {
fmt.Printf("%s = %s\n", key, value)
}
}
-368
View File
@@ -1,368 +0,0 @@
package kingpin
import (
"io/ioutil"
"os"
"github.com/alecthomas/assert"
"testing"
)
func TestBool(t *testing.T) {
app := newTestApp()
b := app.Flag("b", "").Bool()
_, err := app.Parse([]string{"--b"})
assert.NoError(t, err)
assert.True(t, *b)
}
func TestNoBool(t *testing.T) {
fg := newFlagGroup()
f := fg.Flag("b", "").Default("true")
b := f.Bool()
fg.init("")
tokens := tokenize([]string{"--no-b"}, false)
_, err := fg.parse(tokens)
assert.NoError(t, err)
assert.False(t, *b)
}
func TestNegateNonBool(t *testing.T) {
fg := newFlagGroup()
f := fg.Flag("b", "")
f.Int()
fg.init("")
tokens := tokenize([]string{"--no-b"}, false)
_, err := fg.parse(tokens)
assert.Error(t, err)
}
func TestNegativePrefixLongFlag(t *testing.T) {
fg := newFlagGroup()
f := fg.Flag("no-comment", "")
b := f.Bool()
fg.init("")
tokens := tokenize([]string{"--no-comment"}, false)
_, err := fg.parse(tokens)
assert.NoError(t, err)
assert.False(t, *b)
}
func TestInvalidFlagDefaultCanBeOverridden(t *testing.T) {
app := newTestApp()
app.Flag("a", "").Default("invalid").Bool()
_, err := app.Parse([]string{})
assert.Error(t, err)
}
func TestRequiredFlag(t *testing.T) {
app := newTestApp()
app.Version("0.0.0").Writer(ioutil.Discard)
exits := 0
app.Terminate(func(int) { exits++ })
app.Flag("a", "").Required().Bool()
_, err := app.Parse([]string{"--a"})
assert.NoError(t, err)
_, err = app.Parse([]string{})
assert.Error(t, err)
_, err = app.Parse([]string{"--version"})
assert.Equal(t, 1, exits)
}
func TestShortFlag(t *testing.T) {
app := newTestApp()
f := app.Flag("long", "").Short('s').Bool()
_, err := app.Parse([]string{"-s"})
assert.NoError(t, err)
assert.True(t, *f)
}
func TestUnicodeShortFlag(t *testing.T) {
app := newTestApp()
f := app.Flag("aaa", "").Short('ä').Bool()
_, err := app.Parse([]string{"-ä"})
assert.NoError(t, err)
assert.True(t, *f)
}
func TestCombinedShortFlags(t *testing.T) {
app := newTestApp()
a := app.Flag("short0", "").Short('0').Bool()
b := app.Flag("short1", "").Short('1').Bool()
c := app.Flag("short2", "").Short('2').Bool()
_, err := app.Parse([]string{"-01"})
assert.NoError(t, err)
assert.True(t, *a)
assert.True(t, *b)
assert.False(t, *c)
}
func TestCombinedUnicodeShortFlags(t *testing.T) {
app := newTestApp()
a := app.Flag("short0", "").Short('0').Bool()
b := app.Flag("short1", "").Short('1').Bool()
c := app.Flag("short2", "").Short('ä').Bool()
d := app.Flag("short3", "").Short('2').Bool()
_, err := app.Parse([]string{"-0ä1"})
assert.NoError(t, err)
assert.True(t, *a)
assert.True(t, *b)
assert.True(t, *c)
assert.False(t, *d)
}
func TestCombinedShortFlagArg(t *testing.T) {
a := newTestApp()
n := a.Flag("short", "").Short('s').Int()
_, err := a.Parse([]string{"-s10"})
assert.NoError(t, err)
assert.Equal(t, 10, *n)
}
func TestCombinedUnicodeShortFlagArg(t *testing.T) {
app := newTestApp()
a := app.Flag("short", "").Short('ä').Int()
_, err := app.Parse([]string{"-ä10"})
assert.NoError(t, err)
assert.Equal(t, 10, *a)
}
func TestCombinedUnicodeShortFlagUnicodeArg(t *testing.T) {
app := newTestApp()
a := app.Flag("short", "").Short('ä').String()
_, err := app.Parse([]string{"-äöö"})
assert.NoError(t, err)
assert.Equal(t, "öö", *a)
}
func TestEmptyShortFlagIsAnError(t *testing.T) {
_, err := newTestApp().Parse([]string{"-"})
assert.Error(t, err)
}
func TestRequiredWithEnvarMissingErrors(t *testing.T) {
app := newTestApp()
app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int()
_, err := app.Parse([]string{})
assert.Error(t, err)
}
func TestRequiredWithEnvar(t *testing.T) {
os.Setenv("TEST_ENVAR", "123")
app := newTestApp()
flag := app.Flag("t", "").Envar("TEST_ENVAR").Required().Int()
_, err := app.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, 123, *flag)
}
func TestSubcommandFlagRequiredWithEnvar(t *testing.T) {
os.Setenv("TEST_ENVAR", "123")
app := newTestApp()
cmd := app.Command("command", "")
flag := cmd.Flag("t", "").Envar("TEST_ENVAR").Required().Int()
_, err := app.Parse([]string{"command"})
assert.NoError(t, err)
assert.Equal(t, 123, *flag)
}
func TestRegexp(t *testing.T) {
app := newTestApp()
flag := app.Flag("reg", "").Regexp()
_, err := app.Parse([]string{"--reg", "^abc$"})
assert.NoError(t, err)
assert.NotNil(t, *flag)
assert.Equal(t, "^abc$", (*flag).String())
assert.Regexp(t, *flag, "abc")
assert.NotRegexp(t, *flag, "abcd")
}
func TestDuplicateShortFlag(t *testing.T) {
app := newTestApp()
app.Flag("a", "").Short('a').String()
app.Flag("b", "").Short('a').String()
_, err := app.Parse([]string{})
assert.Error(t, err)
}
func TestDuplicateLongFlag(t *testing.T) {
app := newTestApp()
app.Flag("a", "").String()
app.Flag("a", "").String()
_, err := app.Parse([]string{})
assert.Error(t, err)
}
func TestGetFlagAndOverrideDefault(t *testing.T) {
app := newTestApp()
a := app.Flag("a", "").Default("default").String()
_, err := app.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, "default", *a)
app.GetFlag("a").Default("new")
_, err = app.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, "new", *a)
}
func TestEnvarOverrideDefault(t *testing.T) {
os.Setenv("TEST_ENVAR", "123")
app := newTestApp()
flag := app.Flag("t", "").Default("default").Envar("TEST_ENVAR").String()
_, err := app.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, "123", *flag)
}
func TestFlagMultipleValuesDefault(t *testing.T) {
app := newTestApp()
a := app.Flag("a", "").Default("default1", "default2").Strings()
_, err := app.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, []string{"default1", "default2"}, *a)
}
func TestFlagMultipleValuesDefaultNonRepeatable(t *testing.T) {
c := newTestApp()
c.Flag("foo", "foo").Default("a", "b").String()
_, err := c.Parse([]string{})
assert.Error(t, err)
}
func TestFlagMultipleValuesDefaultEnvarUnix(t *testing.T) {
app := newTestApp()
a := app.Flag("a", "").Envar("TEST_MULTIPLE_VALUES").Strings()
os.Setenv("TEST_MULTIPLE_VALUES", "123\n456\n")
_, err := app.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, []string{"123", "456"}, *a)
}
func TestFlagMultipleValuesDefaultEnvarWindows(t *testing.T) {
app := newTestApp()
a := app.Flag("a", "").Envar("TEST_MULTIPLE_VALUES").Strings()
os.Setenv("TEST_MULTIPLE_VALUES", "123\r\n456\r\n")
_, err := app.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, []string{"123", "456"}, *a)
}
func TestFlagMultipleValuesDefaultEnvarNonRepeatable(t *testing.T) {
c := newTestApp()
a := c.Flag("foo", "foo").Envar("TEST_MULTIPLE_VALUES_NON_REPEATABLE").String()
os.Setenv("TEST_MULTIPLE_VALUES_NON_REPEATABLE", "123\n456")
_, err := c.Parse([]string{})
assert.NoError(t, err)
assert.Equal(t, "123\n456", *a)
}
func TestFlagHintAction(t *testing.T) {
c := newTestApp()
action := func() []string {
return []string{"opt1", "opt2"}
}
a := c.Flag("foo", "foo").HintAction(action)
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2"}, args)
}
func TestFlagHintOptions(t *testing.T) {
c := newTestApp()
a := c.Flag("foo", "foo").HintOptions("opt1", "opt2")
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2"}, args)
}
func TestFlagEnumVar(t *testing.T) {
c := newTestApp()
var bar string
a := c.Flag("foo", "foo")
a.Enum("opt1", "opt2")
b := c.Flag("bar", "bar")
b.EnumVar(&bar, "opt3", "opt4")
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2"}, args)
args = b.resolveCompletions()
assert.Equal(t, []string{"opt3", "opt4"}, args)
}
func TestMultiHintOptions(t *testing.T) {
c := newTestApp()
a := c.Flag("foo", "foo").HintOptions("opt1").HintOptions("opt2")
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2"}, args)
}
func TestMultiHintActions(t *testing.T) {
c := newTestApp()
a := c.Flag("foo", "foo").
HintAction(func() []string {
return []string{"opt1"}
}).
HintAction(func() []string {
return []string{"opt2"}
})
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2"}, args)
}
func TestCombinationHintActionsOptions(t *testing.T) {
c := newTestApp()
a := c.Flag("foo", "foo").HintAction(func() []string {
return []string{"opt1"}
}).HintOptions("opt2")
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2"}, args)
}
func TestCombinationEnumActions(t *testing.T) {
c := newTestApp()
var foo string
a := c.Flag("foo", "foo").
HintAction(func() []string {
return []string{"opt1", "opt2"}
})
a.Enum("opt3", "opt4")
b := c.Flag("bar", "bar").
HintAction(func() []string {
return []string{"opt5", "opt6"}
})
b.EnumVar(&foo, "opt3", "opt4")
// Provided HintActions should override automatically generated Enum options.
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2"}, args)
args = b.resolveCompletions()
assert.Equal(t, []string{"opt5", "opt6"}, args)
}
func TestCombinationEnumOptions(t *testing.T) {
c := newTestApp()
var foo string
a := c.Flag("foo", "foo").HintOptions("opt1", "opt2")
a.Enum("opt3", "opt4")
b := c.Flag("bar", "bar").HintOptions("opt5", "opt6")
b.EnumVar(&foo, "opt3", "opt4")
// Provided HintOptions should override automatically generated Enum options.
args := a.resolveCompletions()
assert.Equal(t, []string{"opt1", "opt2"}, args)
args = b.resolveCompletions()
assert.Equal(t, []string{"opt5", "opt6"}, args)
}
-122
View File
@@ -1,122 +0,0 @@
package kingpin
import (
"io/ioutil"
"os"
"testing"
"github.com/alecthomas/assert"
)
func TestParserExpandFromFile(t *testing.T) {
f, err := ioutil.TempFile("", "")
assert.NoError(t, err)
defer os.Remove(f.Name())
f.WriteString("hello\nworld\n")
f.Close()
app := New("test", "")
arg0 := app.Arg("arg0", "").String()
arg1 := app.Arg("arg1", "").String()
_, err = app.Parse([]string{"@" + f.Name()})
assert.NoError(t, err)
assert.Equal(t, "hello", *arg0)
assert.Equal(t, "world", *arg1)
}
func TestParserExpandFromFileLeadingArg(t *testing.T) {
f, err := ioutil.TempFile("", "")
assert.NoError(t, err)
defer os.Remove(f.Name())
f.WriteString("hello\nworld\n")
f.Close()
app := New("test", "")
arg0 := app.Arg("arg0", "").String()
arg1 := app.Arg("arg1", "").String()
arg2 := app.Arg("arg2", "").String()
_, err = app.Parse([]string{"prefix", "@" + f.Name()})
assert.NoError(t, err)
assert.Equal(t, "prefix", *arg0)
assert.Equal(t, "hello", *arg1)
assert.Equal(t, "world", *arg2)
}
func TestParserExpandFromFileTrailingArg(t *testing.T) {
f, err := ioutil.TempFile("", "")
assert.NoError(t, err)
defer os.Remove(f.Name())
f.WriteString("hello\nworld\n")
f.Close()
app := New("test", "")
arg0 := app.Arg("arg0", "").String()
arg1 := app.Arg("arg1", "").String()
arg2 := app.Arg("arg2", "").String()
_, err = app.Parse([]string{"@" + f.Name(), "suffix"})
assert.NoError(t, err)
assert.Equal(t, "hello", *arg0)
assert.Equal(t, "world", *arg1)
assert.Equal(t, "suffix", *arg2)
}
func TestParserExpandFromFileMultipleSurroundingArgs(t *testing.T) {
f, err := ioutil.TempFile("", "")
assert.NoError(t, err)
defer os.Remove(f.Name())
f.WriteString("hello\nworld\n")
f.Close()
app := New("test", "")
arg0 := app.Arg("arg0", "").String()
arg1 := app.Arg("arg1", "").String()
arg2 := app.Arg("arg2", "").String()
arg3 := app.Arg("arg3", "").String()
_, err = app.Parse([]string{"prefix", "@" + f.Name(), "suffix"})
assert.NoError(t, err)
assert.Equal(t, "prefix", *arg0)
assert.Equal(t, "hello", *arg1)
assert.Equal(t, "world", *arg2)
assert.Equal(t, "suffix", *arg3)
}
func TestParserExpandFromFileMultipleFlags(t *testing.T) {
f, err := ioutil.TempFile("", "")
assert.NoError(t, err)
defer os.Remove(f.Name())
f.WriteString("--flag1=f1\n--flag2=f2\n")
f.Close()
app := New("test", "")
flag0 := app.Flag("flag0", "").String()
flag1 := app.Flag("flag1", "").String()
flag2 := app.Flag("flag2", "").String()
flag3 := app.Flag("flag3", "").String()
_, err = app.Parse([]string{"--flag0=f0", "@" + f.Name(), "--flag3=f3"})
assert.NoError(t, err)
assert.Equal(t, "f0", *flag0)
assert.Equal(t, "f1", *flag1)
assert.Equal(t, "f2", *flag2)
assert.Equal(t, "f3", *flag3)
}
func TestParseContextPush(t *testing.T) {
app := New("test", "")
app.Command("foo", "").Command("bar", "")
c := tokenize([]string{"foo", "bar"}, false)
a := c.Next()
assert.Equal(t, TokenArg, a.Type)
b := c.Next()
assert.Equal(t, TokenArg, b.Type)
c.Push(b)
c.Push(a)
a = c.Next()
assert.Equal(t, "foo", a.Value)
b = c.Next()
assert.Equal(t, "bar", b.Value)
}
-98
View File
@@ -1,98 +0,0 @@
package kingpin
import (
"io/ioutil"
"net"
"net/url"
"os"
"github.com/alecthomas/assert"
"testing"
)
func TestParseStrings(t *testing.T) {
p := parserMixin{}
v := p.Strings()
p.value.Set("a")
p.value.Set("b")
assert.Equal(t, []string{"a", "b"}, *v)
}
func TestStringsStringer(t *testing.T) {
target := []string{}
v := newAccumulator(&target, func(v interface{}) Value { return newStringValue(v.(*string)) })
v.Set("hello")
v.Set("world")
assert.Equal(t, "hello,world", v.String())
}
func TestParseStringMap(t *testing.T) {
p := parserMixin{}
v := p.StringMap()
p.value.Set("a:b")
p.value.Set("b:c")
assert.Equal(t, map[string]string{"a": "b", "b": "c"}, *v)
}
func TestParseIP(t *testing.T) {
p := parserMixin{}
v := p.IP()
p.value.Set("10.1.1.2")
ip := net.ParseIP("10.1.1.2")
assert.Equal(t, ip, *v)
}
func TestParseURL(t *testing.T) {
p := parserMixin{}
v := p.URL()
p.value.Set("http://w3.org")
u, err := url.Parse("http://w3.org")
assert.NoError(t, err)
assert.Equal(t, *u, **v)
}
func TestParseExistingFile(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatal(err)
}
defer f.Close()
defer os.Remove(f.Name())
p := parserMixin{}
v := p.ExistingFile()
err = p.value.Set(f.Name())
assert.NoError(t, err)
assert.Equal(t, f.Name(), *v)
err = p.value.Set("/etc/hostsDEFINITELYMISSING")
assert.Error(t, err)
}
func TestParseTCPAddr(t *testing.T) {
p := parserMixin{}
v := p.TCP()
err := p.value.Set("127.0.0.1:1234")
assert.NoError(t, err)
expected, err := net.ResolveTCPAddr("tcp", "127.0.0.1:1234")
assert.NoError(t, err)
assert.Equal(t, *expected, **v)
}
func TestParseTCPAddrList(t *testing.T) {
p := parserMixin{}
_ = p.TCPList()
err := p.value.Set("127.0.0.1:1234")
assert.NoError(t, err)
err = p.value.Set("127.0.0.1:1235")
assert.NoError(t, err)
assert.Equal(t, "127.0.0.1:1234,127.0.0.1:1235", p.value.String())
}
func TestFloat32(t *testing.T) {
p := parserMixin{}
v := p.Float32()
err := p.value.Set("123.45")
assert.NoError(t, err)
assert.InEpsilon(t, 123.45, *v, 0.001)
}
-65
View File
@@ -1,65 +0,0 @@
package kingpin
import (
"bytes"
"strings"
"testing"
"github.com/alecthomas/assert"
)
func TestFormatTwoColumns(t *testing.T) {
buf := bytes.NewBuffer(nil)
formatTwoColumns(buf, 2, 2, 20, [][2]string{
{"--hello", "Hello world help with something that is cool."},
})
expected := ` --hello Hello
world
help with
something
that is
cool.
`
assert.Equal(t, expected, buf.String())
}
func TestFormatTwoColumnsWide(t *testing.T) {
samples := [][2]string{
{strings.Repeat("x", 29), "29 chars"},
{strings.Repeat("x", 30), "30 chars"}}
buf := bytes.NewBuffer(nil)
formatTwoColumns(buf, 0, 0, 200, samples)
expected := `xxxxxxxxxxxxxxxxxxxxxxxxxxxxx29 chars
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
30 chars
`
assert.Equal(t, expected, buf.String())
}
func TestHiddenCommand(t *testing.T) {
templates := []struct{ name, template string }{
{"default", DefaultUsageTemplate},
{"Compact", CompactUsageTemplate},
{"Long", LongHelpTemplate},
{"Man", ManPageTemplate},
}
var buf bytes.Buffer
t.Log("1")
a := New("test", "Test").Writer(&buf).Terminate(nil)
a.Command("visible", "visible")
a.Command("hidden", "hidden").Hidden()
for _, tp := range templates {
buf.Reset()
a.UsageTemplate(tp.template)
a.Parse(nil)
// a.Parse([]string{"--help"})
usage := buf.String()
t.Logf("Usage for %s is:\n%s\n", tp.name, usage)
assert.NotContains(t, usage, "hidden")
assert.Contains(t, usage, "visible")
}
}
-25
View File
@@ -1,25 +0,0 @@
[
{"type": "bool", "parser": "strconv.ParseBool(s)"},
{"type": "string", "parser": "s, error(nil)", "format": "string(*f.v)", "plural": "Strings"},
{"type": "uint", "parser": "strconv.ParseUint(s, 0, 64)", "plural": "Uints"},
{"type": "uint8", "parser": "strconv.ParseUint(s, 0, 8)"},
{"type": "uint16", "parser": "strconv.ParseUint(s, 0, 16)"},
{"type": "uint32", "parser": "strconv.ParseUint(s, 0, 32)"},
{"type": "uint64", "parser": "strconv.ParseUint(s, 0, 64)"},
{"type": "int", "parser": "strconv.ParseFloat(s, 64)", "plural": "Ints"},
{"type": "int8", "parser": "strconv.ParseInt(s, 0, 8)"},
{"type": "int16", "parser": "strconv.ParseInt(s, 0, 16)"},
{"type": "int32", "parser": "strconv.ParseInt(s, 0, 32)"},
{"type": "int64", "parser": "strconv.ParseInt(s, 0, 64)"},
{"type": "float64", "parser": "strconv.ParseFloat(s, 64)"},
{"type": "float32", "parser": "strconv.ParseFloat(s, 32)"},
{"name": "Duration", "type": "time.Duration", "no_value_parser": true},
{"name": "IP", "type": "net.IP", "no_value_parser": true},
{"name": "TCPAddr", "Type": "*net.TCPAddr", "plural": "TCPList", "no_value_parser": true},
{"name": "ExistingFile", "Type": "string", "plural": "ExistingFiles", "no_value_parser": true},
{"name": "ExistingDir", "Type": "string", "plural": "ExistingDirs", "no_value_parser": true},
{"name": "ExistingFileOrDir", "Type": "string", "plural": "ExistingFilesOrDirs", "no_value_parser": true},
{"name": "Regexp", "Type": "*regexp.Regexp", "parser": "regexp.Compile(s)"},
{"name": "ResolvedIP", "Type": "net.IP", "parser": "resolveHost(s)", "help": "Resolve a hostname or IP to an IP."},
{"name": "HexBytes", "Type": "[]byte", "parser": "hex.DecodeString(s)", "help": "Bytes as a hex string."}
]
-98
View File
@@ -1,98 +0,0 @@
package kingpin
import (
"net"
"github.com/alecthomas/assert"
"testing"
)
func TestAccumulatorStrings(t *testing.T) {
target := []string{}
acc := newAccumulator(&target, func(v interface{}) Value { return newStringValue(v.(*string)) })
acc.Set("a")
assert.Equal(t, []string{"a"}, target)
acc.Set("b")
assert.Equal(t, []string{"a", "b"}, target)
}
func TestStrings(t *testing.T) {
app := New("", "")
app.Arg("a", "").Required().String()
app.Arg("b", "").Required().String()
c := app.Arg("c", "").Required().Strings()
app.Parse([]string{"a", "b", "a", "b"})
assert.Equal(t, []string{"a", "b"}, *c)
}
func TestEnum(t *testing.T) {
app := New("", "")
a := app.Arg("a", "").Enum("one", "two", "three")
_, err := app.Parse([]string{"moo"})
assert.Error(t, err)
_, err = app.Parse([]string{"one"})
assert.NoError(t, err)
assert.Equal(t, "one", *a)
}
func TestEnumVar(t *testing.T) {
app := New("", "")
var a string
app.Arg("a", "").EnumVar(&a, "one", "two", "three")
_, err := app.Parse([]string{"moo"})
assert.Error(t, err)
_, err = app.Parse([]string{"one"})
assert.NoError(t, err)
assert.Equal(t, "one", a)
}
func TestCounter(t *testing.T) {
app := New("", "")
c := app.Flag("f", "").Counter()
_, err := app.Parse([]string{"--f", "--f", "--f"})
assert.NoError(t, err)
assert.Equal(t, 3, *c)
}
func TestIPv4Addr(t *testing.T) {
app := newTestApp()
flag := app.Flag("addr", "").ResolvedIP()
_, err := app.Parse([]string{"--addr", net.IPv4(1, 2, 3, 4).String()})
assert.NoError(t, err)
assert.NotNil(t, *flag)
assert.Equal(t, net.IPv4(1, 2, 3, 4), *flag)
}
func TestInvalidIPv4Addr(t *testing.T) {
app := newTestApp()
app.Flag("addr", "").ResolvedIP()
_, err := app.Parse([]string{"--addr", "1.2.3.256"})
assert.Error(t, err)
}
func TestIPv6Addr(t *testing.T) {
app := newTestApp()
flag := app.Flag("addr", "").ResolvedIP()
_, err := app.Parse([]string{"--addr", net.IPv6interfacelocalallnodes.String()})
assert.NoError(t, err)
assert.NotNil(t, *flag)
assert.Equal(t, net.IPv6interfacelocalallnodes, *flag)
}
func TestHexBytes(t *testing.T) {
app := newTestApp()
actual := app.Arg("bytes", "").HexBytes()
_, err := app.Parse([]string{"01020aff"})
assert.NoError(t, err)
assert.Equal(t, []byte{0x01, 0x02, 0x0a, 0xff}, *actual)
}
func TestSetValueDoesNotReset(t *testing.T) {
app := newTestApp()
mapping := map[string]string{
"key": "value",
}
app.Flag("set", "").StringMapVar(&mapping)
assert.NotEmpty(t, mapping)
}