update dependencies
This commit is contained in:
parent
79b0d82993
commit
1dd3fba125
37 changed files with 65 additions and 7419 deletions
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2016 Martin Lindhe
|
Copyright (c) 2016-2019 Martin Lindhe
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -1,4 +1,5 @@
|
||||||
update-vendor:
|
update-vendor:
|
||||||
|
rm -rf vendor
|
||||||
dep ensure
|
dep ensure
|
||||||
dep ensure -update
|
dep ensure -update
|
||||||
|
|
||||||
|
|
5
vendor/github.com/BurntSushi/toml/.gitignore
generated
vendored
5
vendor/github.com/BurntSushi/toml/.gitignore
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
TAGS
|
|
||||||
tags
|
|
||||||
.*.swp
|
|
||||||
tomlcheck/tomlcheck
|
|
||||||
toml.test
|
|
15
vendor/github.com/BurntSushi/toml/.travis.yml
generated
vendored
15
vendor/github.com/BurntSushi/toml/.travis.yml
generated
vendored
|
@ -1,15 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.1
|
|
||||||
- 1.2
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- tip
|
|
||||||
install:
|
|
||||||
- go install ./...
|
|
||||||
- go get github.com/BurntSushi/toml-test
|
|
||||||
script:
|
|
||||||
- export PATH="$PATH:$HOME/gopath/bin"
|
|
||||||
- make test
|
|
3
vendor/github.com/BurntSushi/toml/COMPATIBLE
generated
vendored
3
vendor/github.com/BurntSushi/toml/COMPATIBLE
generated
vendored
|
@ -1,3 +0,0 @@
|
||||||
Compatible with TOML version
|
|
||||||
[v0.4.0](https://github.com/toml-lang/toml/blob/v0.4.0/versions/en/toml-v0.4.0.md)
|
|
||||||
|
|
19
vendor/github.com/BurntSushi/toml/Makefile
generated
vendored
19
vendor/github.com/BurntSushi/toml/Makefile
generated
vendored
|
@ -1,19 +0,0 @@
|
||||||
install:
|
|
||||||
go install ./...
|
|
||||||
|
|
||||||
test: install
|
|
||||||
go test -v
|
|
||||||
toml-test toml-test-decoder
|
|
||||||
toml-test -encoder toml-test-encoder
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
gofmt -w *.go */*.go
|
|
||||||
colcheck *.go */*.go
|
|
||||||
|
|
||||||
tags:
|
|
||||||
find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS
|
|
||||||
|
|
||||||
push:
|
|
||||||
git push origin master
|
|
||||||
git push github master
|
|
||||||
|
|
218
vendor/github.com/BurntSushi/toml/README.md
generated
vendored
218
vendor/github.com/BurntSushi/toml/README.md
generated
vendored
|
@ -1,218 +0,0 @@
|
||||||
## TOML parser and encoder for Go with reflection
|
|
||||||
|
|
||||||
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
|
|
||||||
reflection interface similar to Go's standard library `json` and `xml`
|
|
||||||
packages. This package also supports the `encoding.TextUnmarshaler` and
|
|
||||||
`encoding.TextMarshaler` interfaces so that you can define custom data
|
|
||||||
representations. (There is an example of this below.)
|
|
||||||
|
|
||||||
Spec: https://github.com/toml-lang/toml
|
|
||||||
|
|
||||||
Compatible with TOML version
|
|
||||||
[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
|
|
||||||
|
|
||||||
Documentation: https://godoc.org/github.com/BurntSushi/toml
|
|
||||||
|
|
||||||
Installation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/BurntSushi/toml
|
|
||||||
```
|
|
||||||
|
|
||||||
Try the toml validator:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/BurntSushi/toml/cmd/tomlv
|
|
||||||
tomlv some-toml-file.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/BurntSushi/toml.svg?branch=master)](https://travis-ci.org/BurntSushi/toml) [![GoDoc](https://godoc.org/github.com/BurntSushi/toml?status.svg)](https://godoc.org/github.com/BurntSushi/toml)
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
This package passes all tests in
|
|
||||||
[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder
|
|
||||||
and the encoder.
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
This package works similarly to how the Go standard library handles `XML`
|
|
||||||
and `JSON`. Namely, data is loaded into Go values via reflection.
|
|
||||||
|
|
||||||
For the simplest example, consider some TOML file as just a list of keys
|
|
||||||
and values:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
Age = 25
|
|
||||||
Cats = [ "Cauchy", "Plato" ]
|
|
||||||
Pi = 3.14
|
|
||||||
Perfection = [ 6, 28, 496, 8128 ]
|
|
||||||
DOB = 1987-07-05T05:45:00Z
|
|
||||||
```
|
|
||||||
|
|
||||||
Which could be defined in Go as:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Config struct {
|
|
||||||
Age int
|
|
||||||
Cats []string
|
|
||||||
Pi float64
|
|
||||||
Perfection []int
|
|
||||||
DOB time.Time // requires `import time`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And then decoded with:
|
|
||||||
|
|
||||||
```go
|
|
||||||
var conf Config
|
|
||||||
if _, err := toml.Decode(tomlData, &conf); err != nil {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use struct tags if your struct field name doesn't map to a TOML
|
|
||||||
key value directly:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
some_key_NAME = "wat"
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
type TOML struct {
|
|
||||||
ObscureKey string `toml:"some_key_NAME"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the `encoding.TextUnmarshaler` interface
|
|
||||||
|
|
||||||
Here's an example that automatically parses duration strings into
|
|
||||||
`time.Duration` values:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[song]]
|
|
||||||
name = "Thunder Road"
|
|
||||||
duration = "4m49s"
|
|
||||||
|
|
||||||
[[song]]
|
|
||||||
name = "Stairway to Heaven"
|
|
||||||
duration = "8m03s"
|
|
||||||
```
|
|
||||||
|
|
||||||
Which can be decoded with:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type song struct {
|
|
||||||
Name string
|
|
||||||
Duration duration
|
|
||||||
}
|
|
||||||
type songs struct {
|
|
||||||
Song []song
|
|
||||||
}
|
|
||||||
var favorites songs
|
|
||||||
if _, err := toml.Decode(blob, &favorites); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range favorites.Song {
|
|
||||||
fmt.Printf("%s (%s)\n", s.Name, s.Duration)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And you'll also need a `duration` type that satisfies the
|
|
||||||
`encoding.TextUnmarshaler` interface:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type duration struct {
|
|
||||||
time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *duration) UnmarshalText(text []byte) error {
|
|
||||||
var err error
|
|
||||||
d.Duration, err = time.ParseDuration(string(text))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### More complex usage
|
|
||||||
|
|
||||||
Here's an example of how to load the example from the official spec page:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# This is a TOML document. Boom.
|
|
||||||
|
|
||||||
title = "TOML Example"
|
|
||||||
|
|
||||||
[owner]
|
|
||||||
name = "Tom Preston-Werner"
|
|
||||||
organization = "GitHub"
|
|
||||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
|
||||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
|
||||||
|
|
||||||
[database]
|
|
||||||
server = "192.168.1.1"
|
|
||||||
ports = [ 8001, 8001, 8002 ]
|
|
||||||
connection_max = 5000
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
[servers]
|
|
||||||
|
|
||||||
# You can indent as you please. Tabs or spaces. TOML don't care.
|
|
||||||
[servers.alpha]
|
|
||||||
ip = "10.0.0.1"
|
|
||||||
dc = "eqdc10"
|
|
||||||
|
|
||||||
[servers.beta]
|
|
||||||
ip = "10.0.0.2"
|
|
||||||
dc = "eqdc10"
|
|
||||||
|
|
||||||
[clients]
|
|
||||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
|
||||||
|
|
||||||
# Line breaks are OK when inside arrays
|
|
||||||
hosts = [
|
|
||||||
"alpha",
|
|
||||||
"omega"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
And the corresponding Go types are:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type tomlConfig struct {
|
|
||||||
Title string
|
|
||||||
Owner ownerInfo
|
|
||||||
DB database `toml:"database"`
|
|
||||||
Servers map[string]server
|
|
||||||
Clients clients
|
|
||||||
}
|
|
||||||
|
|
||||||
type ownerInfo struct {
|
|
||||||
Name string
|
|
||||||
Org string `toml:"organization"`
|
|
||||||
Bio string
|
|
||||||
DOB time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type database struct {
|
|
||||||
Server string
|
|
||||||
Ports []int
|
|
||||||
ConnMax int `toml:"connection_max"`
|
|
||||||
Enabled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type server struct {
|
|
||||||
IP string
|
|
||||||
DC string
|
|
||||||
}
|
|
||||||
|
|
||||||
type clients struct {
|
|
||||||
Data [][]interface{}
|
|
||||||
Hosts []string
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that a case insensitive match will be tried if an exact match can't be
|
|
||||||
found.
|
|
||||||
|
|
||||||
A working example of the above can be found in `_examples/example.{go,toml}`.
|
|
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
generated
vendored
Normal file
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 TOML authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
generated
vendored
Normal file
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 TOML authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
21
vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING
generated
vendored
Normal file
21
vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 TOML authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
1461
vendor/github.com/BurntSushi/toml/decode_test.go
generated
vendored
1461
vendor/github.com/BurntSushi/toml/decode_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
615
vendor/github.com/BurntSushi/toml/encode_test.go
generated
vendored
615
vendor/github.com/BurntSushi/toml/encode_test.go
generated
vendored
|
@ -1,615 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncodeRoundTrip(t *testing.T) {
|
|
||||||
type Config struct {
|
|
||||||
Age int
|
|
||||||
Cats []string
|
|
||||||
Pi float64
|
|
||||||
Perfection []int
|
|
||||||
DOB time.Time
|
|
||||||
Ipaddress net.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
var inputs = Config{
|
|
||||||
13,
|
|
||||||
[]string{"one", "two", "three"},
|
|
||||||
3.145,
|
|
||||||
[]int{11, 2, 3, 4},
|
|
||||||
time.Now(),
|
|
||||||
net.ParseIP("192.168.59.254"),
|
|
||||||
}
|
|
||||||
|
|
||||||
var firstBuffer bytes.Buffer
|
|
||||||
e := NewEncoder(&firstBuffer)
|
|
||||||
err := e.Encode(inputs)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var outputs Config
|
|
||||||
if _, err := Decode(firstBuffer.String(), &outputs); err != nil {
|
|
||||||
t.Logf("Could not decode:\n-----\n%s\n-----\n",
|
|
||||||
firstBuffer.String())
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// could test each value individually, but I'm lazy
|
|
||||||
var secondBuffer bytes.Buffer
|
|
||||||
e2 := NewEncoder(&secondBuffer)
|
|
||||||
err = e2.Encode(outputs)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if firstBuffer.String() != secondBuffer.String() {
|
|
||||||
t.Error(
|
|
||||||
firstBuffer.String(),
|
|
||||||
"\n\n is not identical to\n\n",
|
|
||||||
secondBuffer.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX(burntsushi)
|
|
||||||
// I think these tests probably should be removed. They are good, but they
|
|
||||||
// ought to be obsolete by toml-test.
|
|
||||||
func TestEncode(t *testing.T) {
|
|
||||||
type Embedded struct {
|
|
||||||
Int int `toml:"_int"`
|
|
||||||
}
|
|
||||||
type NonStruct int
|
|
||||||
|
|
||||||
date := time.Date(2014, 5, 11, 20, 30, 40, 0, time.FixedZone("IST", 3600))
|
|
||||||
dateStr := "2014-05-11T19:30:40Z"
|
|
||||||
|
|
||||||
tests := map[string]struct {
|
|
||||||
input interface{}
|
|
||||||
wantOutput string
|
|
||||||
wantError error
|
|
||||||
}{
|
|
||||||
"bool field": {
|
|
||||||
input: struct {
|
|
||||||
BoolTrue bool
|
|
||||||
BoolFalse bool
|
|
||||||
}{true, false},
|
|
||||||
wantOutput: "BoolTrue = true\nBoolFalse = false\n",
|
|
||||||
},
|
|
||||||
"int fields": {
|
|
||||||
input: struct {
|
|
||||||
Int int
|
|
||||||
Int8 int8
|
|
||||||
Int16 int16
|
|
||||||
Int32 int32
|
|
||||||
Int64 int64
|
|
||||||
}{1, 2, 3, 4, 5},
|
|
||||||
wantOutput: "Int = 1\nInt8 = 2\nInt16 = 3\nInt32 = 4\nInt64 = 5\n",
|
|
||||||
},
|
|
||||||
"uint fields": {
|
|
||||||
input: struct {
|
|
||||||
Uint uint
|
|
||||||
Uint8 uint8
|
|
||||||
Uint16 uint16
|
|
||||||
Uint32 uint32
|
|
||||||
Uint64 uint64
|
|
||||||
}{1, 2, 3, 4, 5},
|
|
||||||
wantOutput: "Uint = 1\nUint8 = 2\nUint16 = 3\nUint32 = 4" +
|
|
||||||
"\nUint64 = 5\n",
|
|
||||||
},
|
|
||||||
"float fields": {
|
|
||||||
input: struct {
|
|
||||||
Float32 float32
|
|
||||||
Float64 float64
|
|
||||||
}{1.5, 2.5},
|
|
||||||
wantOutput: "Float32 = 1.5\nFloat64 = 2.5\n",
|
|
||||||
},
|
|
||||||
"string field": {
|
|
||||||
input: struct{ String string }{"foo"},
|
|
||||||
wantOutput: "String = \"foo\"\n",
|
|
||||||
},
|
|
||||||
"string field and unexported field": {
|
|
||||||
input: struct {
|
|
||||||
String string
|
|
||||||
unexported int
|
|
||||||
}{"foo", 0},
|
|
||||||
wantOutput: "String = \"foo\"\n",
|
|
||||||
},
|
|
||||||
"datetime field in UTC": {
|
|
||||||
input: struct{ Date time.Time }{date},
|
|
||||||
wantOutput: fmt.Sprintf("Date = %s\n", dateStr),
|
|
||||||
},
|
|
||||||
"datetime field as primitive": {
|
|
||||||
// Using a map here to fail if isStructOrMap() returns true for
|
|
||||||
// time.Time.
|
|
||||||
input: map[string]interface{}{
|
|
||||||
"Date": date,
|
|
||||||
"Int": 1,
|
|
||||||
},
|
|
||||||
wantOutput: fmt.Sprintf("Date = %s\nInt = 1\n", dateStr),
|
|
||||||
},
|
|
||||||
"array fields": {
|
|
||||||
input: struct {
|
|
||||||
IntArray0 [0]int
|
|
||||||
IntArray3 [3]int
|
|
||||||
}{[0]int{}, [3]int{1, 2, 3}},
|
|
||||||
wantOutput: "IntArray0 = []\nIntArray3 = [1, 2, 3]\n",
|
|
||||||
},
|
|
||||||
"slice fields": {
|
|
||||||
input: struct{ IntSliceNil, IntSlice0, IntSlice3 []int }{
|
|
||||||
nil, []int{}, []int{1, 2, 3},
|
|
||||||
},
|
|
||||||
wantOutput: "IntSlice0 = []\nIntSlice3 = [1, 2, 3]\n",
|
|
||||||
},
|
|
||||||
"datetime slices": {
|
|
||||||
input: struct{ DatetimeSlice []time.Time }{
|
|
||||||
[]time.Time{date, date},
|
|
||||||
},
|
|
||||||
wantOutput: fmt.Sprintf("DatetimeSlice = [%s, %s]\n",
|
|
||||||
dateStr, dateStr),
|
|
||||||
},
|
|
||||||
"nested arrays and slices": {
|
|
||||||
input: struct {
|
|
||||||
SliceOfArrays [][2]int
|
|
||||||
ArrayOfSlices [2][]int
|
|
||||||
SliceOfArraysOfSlices [][2][]int
|
|
||||||
ArrayOfSlicesOfArrays [2][][2]int
|
|
||||||
SliceOfMixedArrays [][2]interface{}
|
|
||||||
ArrayOfMixedSlices [2][]interface{}
|
|
||||||
}{
|
|
||||||
[][2]int{{1, 2}, {3, 4}},
|
|
||||||
[2][]int{{1, 2}, {3, 4}},
|
|
||||||
[][2][]int{
|
|
||||||
{
|
|
||||||
{1, 2}, {3, 4},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{5, 6}, {7, 8},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[2][][2]int{
|
|
||||||
{
|
|
||||||
{1, 2}, {3, 4},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{5, 6}, {7, 8},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[][2]interface{}{
|
|
||||||
{1, 2}, {"a", "b"},
|
|
||||||
},
|
|
||||||
[2][]interface{}{
|
|
||||||
{1, 2}, {"a", "b"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantOutput: `SliceOfArrays = [[1, 2], [3, 4]]
|
|
||||||
ArrayOfSlices = [[1, 2], [3, 4]]
|
|
||||||
SliceOfArraysOfSlices = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
|
||||||
ArrayOfSlicesOfArrays = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
|
||||||
SliceOfMixedArrays = [[1, 2], ["a", "b"]]
|
|
||||||
ArrayOfMixedSlices = [[1, 2], ["a", "b"]]
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
"empty slice": {
|
|
||||||
input: struct{ Empty []interface{} }{[]interface{}{}},
|
|
||||||
wantOutput: "Empty = []\n",
|
|
||||||
},
|
|
||||||
"(error) slice with element type mismatch (string and integer)": {
|
|
||||||
input: struct{ Mixed []interface{} }{[]interface{}{1, "a"}},
|
|
||||||
wantError: errArrayMixedElementTypes,
|
|
||||||
},
|
|
||||||
"(error) slice with element type mismatch (integer and float)": {
|
|
||||||
input: struct{ Mixed []interface{} }{[]interface{}{1, 2.5}},
|
|
||||||
wantError: errArrayMixedElementTypes,
|
|
||||||
},
|
|
||||||
"slice with elems of differing Go types, same TOML types": {
|
|
||||||
input: struct {
|
|
||||||
MixedInts []interface{}
|
|
||||||
MixedFloats []interface{}
|
|
||||||
}{
|
|
||||||
[]interface{}{
|
|
||||||
int(1), int8(2), int16(3), int32(4), int64(5),
|
|
||||||
uint(1), uint8(2), uint16(3), uint32(4), uint64(5),
|
|
||||||
},
|
|
||||||
[]interface{}{float32(1.5), float64(2.5)},
|
|
||||||
},
|
|
||||||
wantOutput: "MixedInts = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]\n" +
|
|
||||||
"MixedFloats = [1.5, 2.5]\n",
|
|
||||||
},
|
|
||||||
"(error) slice w/ element type mismatch (one is nested array)": {
|
|
||||||
input: struct{ Mixed []interface{} }{
|
|
||||||
[]interface{}{1, []interface{}{2}},
|
|
||||||
},
|
|
||||||
wantError: errArrayMixedElementTypes,
|
|
||||||
},
|
|
||||||
"(error) slice with 1 nil element": {
|
|
||||||
input: struct{ NilElement1 []interface{} }{[]interface{}{nil}},
|
|
||||||
wantError: errArrayNilElement,
|
|
||||||
},
|
|
||||||
"(error) slice with 1 nil element (and other non-nil elements)": {
|
|
||||||
input: struct{ NilElement []interface{} }{
|
|
||||||
[]interface{}{1, nil},
|
|
||||||
},
|
|
||||||
wantError: errArrayNilElement,
|
|
||||||
},
|
|
||||||
"simple map": {
|
|
||||||
input: map[string]int{"a": 1, "b": 2},
|
|
||||||
wantOutput: "a = 1\nb = 2\n",
|
|
||||||
},
|
|
||||||
"map with interface{} value type": {
|
|
||||||
input: map[string]interface{}{"a": 1, "b": "c"},
|
|
||||||
wantOutput: "a = 1\nb = \"c\"\n",
|
|
||||||
},
|
|
||||||
"map with interface{} value type, some of which are structs": {
|
|
||||||
input: map[string]interface{}{
|
|
||||||
"a": struct{ Int int }{2},
|
|
||||||
"b": 1,
|
|
||||||
},
|
|
||||||
wantOutput: "b = 1\n\n[a]\n Int = 2\n",
|
|
||||||
},
|
|
||||||
"nested map": {
|
|
||||||
input: map[string]map[string]int{
|
|
||||||
"a": {"b": 1},
|
|
||||||
"c": {"d": 2},
|
|
||||||
},
|
|
||||||
wantOutput: "[a]\n b = 1\n\n[c]\n d = 2\n",
|
|
||||||
},
|
|
||||||
"nested struct": {
|
|
||||||
input: struct{ Struct struct{ Int int } }{
|
|
||||||
struct{ Int int }{1},
|
|
||||||
},
|
|
||||||
wantOutput: "[Struct]\n Int = 1\n",
|
|
||||||
},
|
|
||||||
"nested struct and non-struct field": {
|
|
||||||
input: struct {
|
|
||||||
Struct struct{ Int int }
|
|
||||||
Bool bool
|
|
||||||
}{struct{ Int int }{1}, true},
|
|
||||||
wantOutput: "Bool = true\n\n[Struct]\n Int = 1\n",
|
|
||||||
},
|
|
||||||
"2 nested structs": {
|
|
||||||
input: struct{ Struct1, Struct2 struct{ Int int } }{
|
|
||||||
struct{ Int int }{1}, struct{ Int int }{2},
|
|
||||||
},
|
|
||||||
wantOutput: "[Struct1]\n Int = 1\n\n[Struct2]\n Int = 2\n",
|
|
||||||
},
|
|
||||||
"deeply nested structs": {
|
|
||||||
input: struct {
|
|
||||||
Struct1, Struct2 struct{ Struct3 *struct{ Int int } }
|
|
||||||
}{
|
|
||||||
struct{ Struct3 *struct{ Int int } }{&struct{ Int int }{1}},
|
|
||||||
struct{ Struct3 *struct{ Int int } }{nil},
|
|
||||||
},
|
|
||||||
wantOutput: "[Struct1]\n [Struct1.Struct3]\n Int = 1" +
|
|
||||||
"\n\n[Struct2]\n",
|
|
||||||
},
|
|
||||||
"nested struct with nil struct elem": {
|
|
||||||
input: struct {
|
|
||||||
Struct struct{ Inner *struct{ Int int } }
|
|
||||||
}{
|
|
||||||
struct{ Inner *struct{ Int int } }{nil},
|
|
||||||
},
|
|
||||||
wantOutput: "[Struct]\n",
|
|
||||||
},
|
|
||||||
"nested struct with no fields": {
|
|
||||||
input: struct {
|
|
||||||
Struct struct{ Inner struct{} }
|
|
||||||
}{
|
|
||||||
struct{ Inner struct{} }{struct{}{}},
|
|
||||||
},
|
|
||||||
wantOutput: "[Struct]\n [Struct.Inner]\n",
|
|
||||||
},
|
|
||||||
"struct with tags": {
|
|
||||||
input: struct {
|
|
||||||
Struct struct {
|
|
||||||
Int int `toml:"_int"`
|
|
||||||
} `toml:"_struct"`
|
|
||||||
Bool bool `toml:"_bool"`
|
|
||||||
}{
|
|
||||||
struct {
|
|
||||||
Int int `toml:"_int"`
|
|
||||||
}{1}, true,
|
|
||||||
},
|
|
||||||
wantOutput: "_bool = true\n\n[_struct]\n _int = 1\n",
|
|
||||||
},
|
|
||||||
"embedded struct": {
|
|
||||||
input: struct{ Embedded }{Embedded{1}},
|
|
||||||
wantOutput: "_int = 1\n",
|
|
||||||
},
|
|
||||||
"embedded *struct": {
|
|
||||||
input: struct{ *Embedded }{&Embedded{1}},
|
|
||||||
wantOutput: "_int = 1\n",
|
|
||||||
},
|
|
||||||
"nested embedded struct": {
|
|
||||||
input: struct {
|
|
||||||
Struct struct{ Embedded } `toml:"_struct"`
|
|
||||||
}{struct{ Embedded }{Embedded{1}}},
|
|
||||||
wantOutput: "[_struct]\n _int = 1\n",
|
|
||||||
},
|
|
||||||
"nested embedded *struct": {
|
|
||||||
input: struct {
|
|
||||||
Struct struct{ *Embedded } `toml:"_struct"`
|
|
||||||
}{struct{ *Embedded }{&Embedded{1}}},
|
|
||||||
wantOutput: "[_struct]\n _int = 1\n",
|
|
||||||
},
|
|
||||||
"embedded non-struct": {
|
|
||||||
input: struct{ NonStruct }{5},
|
|
||||||
wantOutput: "NonStruct = 5\n",
|
|
||||||
},
|
|
||||||
"array of tables": {
|
|
||||||
input: struct {
|
|
||||||
Structs []*struct{ Int int } `toml:"struct"`
|
|
||||||
}{
|
|
||||||
[]*struct{ Int int }{{1}, {3}},
|
|
||||||
},
|
|
||||||
wantOutput: "[[struct]]\n Int = 1\n\n[[struct]]\n Int = 3\n",
|
|
||||||
},
|
|
||||||
"array of tables order": {
|
|
||||||
input: map[string]interface{}{
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"zero": 5,
|
|
||||||
"arr": []map[string]int{
|
|
||||||
{
|
|
||||||
"friend": 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantOutput: "[map]\n zero = 5\n\n [[map.arr]]\n friend = 5\n",
|
|
||||||
},
|
|
||||||
"(error) top-level slice": {
|
|
||||||
input: []struct{ Int int }{{1}, {2}, {3}},
|
|
||||||
wantError: errNoKey,
|
|
||||||
},
|
|
||||||
"(error) slice of slice": {
|
|
||||||
input: struct {
|
|
||||||
Slices [][]struct{ Int int }
|
|
||||||
}{
|
|
||||||
[][]struct{ Int int }{{{1}}, {{2}}, {{3}}},
|
|
||||||
},
|
|
||||||
wantError: errArrayNoTable,
|
|
||||||
},
|
|
||||||
"(error) map no string key": {
|
|
||||||
input: map[int]string{1: ""},
|
|
||||||
wantError: errNonString,
|
|
||||||
},
|
|
||||||
"(error) empty key name": {
|
|
||||||
input: map[string]int{"": 1},
|
|
||||||
wantError: errAnything,
|
|
||||||
},
|
|
||||||
"(error) empty map name": {
|
|
||||||
input: map[string]interface{}{
|
|
||||||
"": map[string]int{"v": 1},
|
|
||||||
},
|
|
||||||
wantError: errAnything,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for label, test := range tests {
|
|
||||||
encodeExpected(t, label, test.input, test.wantOutput, test.wantError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeNestedTableArrays(t *testing.T) {
|
|
||||||
type song struct {
|
|
||||||
Name string `toml:"name"`
|
|
||||||
}
|
|
||||||
type album struct {
|
|
||||||
Name string `toml:"name"`
|
|
||||||
Songs []song `toml:"songs"`
|
|
||||||
}
|
|
||||||
type springsteen struct {
|
|
||||||
Albums []album `toml:"albums"`
|
|
||||||
}
|
|
||||||
value := springsteen{
|
|
||||||
[]album{
|
|
||||||
{"Born to Run",
|
|
||||||
[]song{{"Jungleland"}, {"Meeting Across the River"}}},
|
|
||||||
{"Born in the USA",
|
|
||||||
[]song{{"Glory Days"}, {"Dancing in the Dark"}}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
expected := `[[albums]]
|
|
||||||
name = "Born to Run"
|
|
||||||
|
|
||||||
[[albums.songs]]
|
|
||||||
name = "Jungleland"
|
|
||||||
|
|
||||||
[[albums.songs]]
|
|
||||||
name = "Meeting Across the River"
|
|
||||||
|
|
||||||
[[albums]]
|
|
||||||
name = "Born in the USA"
|
|
||||||
|
|
||||||
[[albums.songs]]
|
|
||||||
name = "Glory Days"
|
|
||||||
|
|
||||||
[[albums.songs]]
|
|
||||||
name = "Dancing in the Dark"
|
|
||||||
`
|
|
||||||
encodeExpected(t, "nested table arrays", value, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeArrayHashWithNormalHashOrder(t *testing.T) {
|
|
||||||
type Alpha struct {
|
|
||||||
V int
|
|
||||||
}
|
|
||||||
type Beta struct {
|
|
||||||
V int
|
|
||||||
}
|
|
||||||
type Conf struct {
|
|
||||||
V int
|
|
||||||
A Alpha
|
|
||||||
B []Beta
|
|
||||||
}
|
|
||||||
|
|
||||||
val := Conf{
|
|
||||||
V: 1,
|
|
||||||
A: Alpha{2},
|
|
||||||
B: []Beta{{3}},
|
|
||||||
}
|
|
||||||
expected := "V = 1\n\n[A]\n V = 2\n\n[[B]]\n V = 3\n"
|
|
||||||
encodeExpected(t, "array hash with normal hash order", val, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeWithOmitEmpty(t *testing.T) {
|
|
||||||
type simple struct {
|
|
||||||
Bool bool `toml:"bool,omitempty"`
|
|
||||||
String string `toml:"string,omitempty"`
|
|
||||||
Array [0]byte `toml:"array,omitempty"`
|
|
||||||
Slice []int `toml:"slice,omitempty"`
|
|
||||||
Map map[string]string `toml:"map,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var v simple
|
|
||||||
encodeExpected(t, "fields with omitempty are omitted when empty", v, "", nil)
|
|
||||||
v = simple{
|
|
||||||
Bool: true,
|
|
||||||
String: " ",
|
|
||||||
Slice: []int{2, 3, 4},
|
|
||||||
Map: map[string]string{"foo": "bar"},
|
|
||||||
}
|
|
||||||
expected := `bool = true
|
|
||||||
string = " "
|
|
||||||
slice = [2, 3, 4]
|
|
||||||
|
|
||||||
[map]
|
|
||||||
foo = "bar"
|
|
||||||
`
|
|
||||||
encodeExpected(t, "fields with omitempty are not omitted when non-empty",
|
|
||||||
v, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeWithOmitZero(t *testing.T) {
|
|
||||||
type simple struct {
|
|
||||||
Number int `toml:"number,omitzero"`
|
|
||||||
Real float64 `toml:"real,omitzero"`
|
|
||||||
Unsigned uint `toml:"unsigned,omitzero"`
|
|
||||||
}
|
|
||||||
|
|
||||||
value := simple{0, 0.0, uint(0)}
|
|
||||||
expected := ""
|
|
||||||
|
|
||||||
encodeExpected(t, "simple with omitzero, all zero", value, expected, nil)
|
|
||||||
|
|
||||||
value.Number = 10
|
|
||||||
value.Real = 20
|
|
||||||
value.Unsigned = 5
|
|
||||||
expected = `number = 10
|
|
||||||
real = 20.0
|
|
||||||
unsigned = 5
|
|
||||||
`
|
|
||||||
encodeExpected(t, "simple with omitzero, non-zero", value, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeOmitemptyWithEmptyName(t *testing.T) {
|
|
||||||
type simple struct {
|
|
||||||
S []int `toml:",omitempty"`
|
|
||||||
}
|
|
||||||
v := simple{[]int{1, 2, 3}}
|
|
||||||
expected := "S = [1, 2, 3]\n"
|
|
||||||
encodeExpected(t, "simple with omitempty, no name, non-empty field",
|
|
||||||
v, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeAnonymousStruct(t *testing.T) {
|
|
||||||
type Inner struct{ N int }
|
|
||||||
type Outer0 struct{ Inner }
|
|
||||||
type Outer1 struct {
|
|
||||||
Inner `toml:"inner"`
|
|
||||||
}
|
|
||||||
|
|
||||||
v0 := Outer0{Inner{3}}
|
|
||||||
expected := "N = 3\n"
|
|
||||||
encodeExpected(t, "embedded anonymous untagged struct", v0, expected, nil)
|
|
||||||
|
|
||||||
v1 := Outer1{Inner{3}}
|
|
||||||
expected = "[inner]\n N = 3\n"
|
|
||||||
encodeExpected(t, "embedded anonymous tagged struct", v1, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeAnonymousStructPointerField(t *testing.T) {
|
|
||||||
type Inner struct{ N int }
|
|
||||||
type Outer0 struct{ *Inner }
|
|
||||||
type Outer1 struct {
|
|
||||||
*Inner `toml:"inner"`
|
|
||||||
}
|
|
||||||
|
|
||||||
v0 := Outer0{}
|
|
||||||
expected := ""
|
|
||||||
encodeExpected(t, "nil anonymous untagged struct pointer field", v0, expected, nil)
|
|
||||||
|
|
||||||
v0 = Outer0{&Inner{3}}
|
|
||||||
expected = "N = 3\n"
|
|
||||||
encodeExpected(t, "non-nil anonymous untagged struct pointer field", v0, expected, nil)
|
|
||||||
|
|
||||||
v1 := Outer1{}
|
|
||||||
expected = ""
|
|
||||||
encodeExpected(t, "nil anonymous tagged struct pointer field", v1, expected, nil)
|
|
||||||
|
|
||||||
v1 = Outer1{&Inner{3}}
|
|
||||||
expected = "[inner]\n N = 3\n"
|
|
||||||
encodeExpected(t, "non-nil anonymous tagged struct pointer field", v1, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeIgnoredFields(t *testing.T) {
|
|
||||||
type simple struct {
|
|
||||||
Number int `toml:"-"`
|
|
||||||
}
|
|
||||||
value := simple{}
|
|
||||||
expected := ""
|
|
||||||
encodeExpected(t, "ignored field", value, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeExpected(
|
|
||||||
t *testing.T, label string, val interface{}, wantStr string, wantErr error,
|
|
||||||
) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
enc := NewEncoder(&buf)
|
|
||||||
err := enc.Encode(val)
|
|
||||||
if err != wantErr {
|
|
||||||
if wantErr != nil {
|
|
||||||
if wantErr == errAnything && err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Errorf("%s: want Encode error %v, got %v", label, wantErr, err)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%s: Encode failed: %s", label, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if got := buf.String(); wantStr != got {
|
|
||||||
t.Errorf("%s: want\n-----\n%q\n-----\nbut got\n-----\n%q\n-----\n",
|
|
||||||
label, wantStr, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleEncoder_Encode() {
|
|
||||||
date, _ := time.Parse(time.RFC822, "14 Mar 10 18:00 UTC")
|
|
||||||
var config = map[string]interface{}{
|
|
||||||
"date": date,
|
|
||||||
"counts": []int{1, 1, 2, 3, 5, 8},
|
|
||||||
"hash": map[string]string{
|
|
||||||
"key1": "val1",
|
|
||||||
"key2": "val2",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
if err := NewEncoder(buf).Encode(config); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Println(buf.String())
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// counts = [1, 1, 2, 3, 5, 8]
|
|
||||||
// date = 2010-03-14T18:00:00Z
|
|
||||||
//
|
|
||||||
// [hash]
|
|
||||||
// key1 = "val1"
|
|
||||||
// key2 = "val2"
|
|
||||||
}
|
|
1
vendor/github.com/BurntSushi/toml/session.vim
generated
vendored
1
vendor/github.com/BurntSushi/toml/session.vim
generated
vendored
|
@ -1 +0,0 @@
|
||||||
au BufWritePost *.go silent!make tags > /dev/null 2>&1
|
|
25
vendor/github.com/alecthomas/template/README.md
generated
vendored
25
vendor/github.com/alecthomas/template/README.md
generated
vendored
|
@ -1,25 +0,0 @@
|
||||||
# Go's `text/template` package with newline elision
|
|
||||||
|
|
||||||
This is a fork of Go 1.4's [text/template](http://golang.org/pkg/text/template/) package with one addition: a backslash immediately after a closing delimiter will delete all subsequent newlines until a non-newline.
|
|
||||||
|
|
||||||
eg.
|
|
||||||
|
|
||||||
```
|
|
||||||
{{if true}}\
|
|
||||||
hello
|
|
||||||
{{end}}\
|
|
||||||
```
|
|
||||||
|
|
||||||
Will result in:
|
|
||||||
|
|
||||||
```
|
|
||||||
hello\n
|
|
||||||
```
|
|
||||||
|
|
||||||
Rather than:
|
|
||||||
|
|
||||||
```
|
|
||||||
\n
|
|
||||||
hello\n
|
|
||||||
\n
|
|
||||||
```
|
|
72
vendor/github.com/alecthomas/template/example_test.go
generated
vendored
72
vendor/github.com/alecthomas/template/example_test.go
generated
vendored
|
@ -1,72 +0,0 @@
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package template_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/alecthomas/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleTemplate() {
|
|
||||||
// Define a template.
|
|
||||||
const letter = `
|
|
||||||
Dear {{.Name}},
|
|
||||||
{{if .Attended}}
|
|
||||||
It was a pleasure to see you at the wedding.{{else}}
|
|
||||||
It is a shame you couldn't make it to the wedding.{{end}}
|
|
||||||
{{with .Gift}}Thank you for the lovely {{.}}.
|
|
||||||
{{end}}
|
|
||||||
Best wishes,
|
|
||||||
Josie
|
|
||||||
`
|
|
||||||
|
|
||||||
// Prepare some data to insert into the template.
|
|
||||||
type Recipient struct {
|
|
||||||
Name, Gift string
|
|
||||||
Attended bool
|
|
||||||
}
|
|
||||||
var recipients = []Recipient{
|
|
||||||
{"Aunt Mildred", "bone china tea set", true},
|
|
||||||
{"Uncle John", "moleskin pants", false},
|
|
||||||
{"Cousin Rodney", "", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new template and parse the letter into it.
|
|
||||||
t := template.Must(template.New("letter").Parse(letter))
|
|
||||||
|
|
||||||
// Execute the template for each recipient.
|
|
||||||
for _, r := range recipients {
|
|
||||||
err := t.Execute(os.Stdout, r)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("executing template:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Dear Aunt Mildred,
|
|
||||||
//
|
|
||||||
// It was a pleasure to see you at the wedding.
|
|
||||||
// Thank you for the lovely bone china tea set.
|
|
||||||
//
|
|
||||||
// Best wishes,
|
|
||||||
// Josie
|
|
||||||
//
|
|
||||||
// Dear Uncle John,
|
|
||||||
//
|
|
||||||
// It is a shame you couldn't make it to the wedding.
|
|
||||||
// Thank you for the lovely moleskin pants.
|
|
||||||
//
|
|
||||||
// Best wishes,
|
|
||||||
// Josie
|
|
||||||
//
|
|
||||||
// Dear Cousin Rodney,
|
|
||||||
//
|
|
||||||
// It is a shame you couldn't make it to the wedding.
|
|
||||||
//
|
|
||||||
// Best wishes,
|
|
||||||
// Josie
|
|
||||||
}
|
|
183
vendor/github.com/alecthomas/template/examplefiles_test.go
generated
vendored
183
vendor/github.com/alecthomas/template/examplefiles_test.go
generated
vendored
|
@ -1,183 +0,0 @@
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package template_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/alecthomas/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
// templateFile defines the contents of a template to be stored in a file, for testing.
|
|
||||||
type templateFile struct {
|
|
||||||
name string
|
|
||||||
contents string
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestDir(files []templateFile) string {
|
|
||||||
dir, err := ioutil.TempDir("", "template")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
for _, file := range files {
|
|
||||||
f, err := os.Create(filepath.Join(dir, file.name))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
_, err = io.WriteString(f, file.contents)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dir
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we demonstrate loading a set of templates from a directory.
|
|
||||||
func ExampleTemplate_glob() {
|
|
||||||
// Here we create a temporary directory and populate it with our sample
|
|
||||||
// template definition files; usually the template files would already
|
|
||||||
// exist in some location known to the program.
|
|
||||||
dir := createTestDir([]templateFile{
|
|
||||||
// T0.tmpl is a plain template file that just invokes T1.
|
|
||||||
{"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
|
|
||||||
// T1.tmpl defines a template, T1 that invokes T2.
|
|
||||||
{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
|
|
||||||
// T2.tmpl defines a template T2.
|
|
||||||
{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
|
|
||||||
})
|
|
||||||
// Clean up after the test; another quirk of running as an example.
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
// pattern is the glob pattern used to find all the template files.
|
|
||||||
pattern := filepath.Join(dir, "*.tmpl")
|
|
||||||
|
|
||||||
// Here starts the example proper.
|
|
||||||
// T0.tmpl is the first name matched, so it becomes the starting template,
|
|
||||||
// the value returned by ParseGlob.
|
|
||||||
tmpl := template.Must(template.ParseGlob(pattern))
|
|
||||||
|
|
||||||
err := tmpl.Execute(os.Stdout, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("template execution: %s", err)
|
|
||||||
}
|
|
||||||
// Output:
|
|
||||||
// T0 invokes T1: (T1 invokes T2: (This is T2))
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates one way to share some templates
|
|
||||||
// and use them in different contexts. In this variant we add multiple driver
|
|
||||||
// templates by hand to an existing bundle of templates.
|
|
||||||
func ExampleTemplate_helpers() {
|
|
||||||
// Here we create a temporary directory and populate it with our sample
|
|
||||||
// template definition files; usually the template files would already
|
|
||||||
// exist in some location known to the program.
|
|
||||||
dir := createTestDir([]templateFile{
|
|
||||||
// T1.tmpl defines a template, T1 that invokes T2.
|
|
||||||
{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
|
|
||||||
// T2.tmpl defines a template T2.
|
|
||||||
{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
|
|
||||||
})
|
|
||||||
// Clean up after the test; another quirk of running as an example.
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
// pattern is the glob pattern used to find all the template files.
|
|
||||||
pattern := filepath.Join(dir, "*.tmpl")
|
|
||||||
|
|
||||||
// Here starts the example proper.
|
|
||||||
// Load the helpers.
|
|
||||||
templates := template.Must(template.ParseGlob(pattern))
|
|
||||||
// Add one driver template to the bunch; we do this with an explicit template definition.
|
|
||||||
_, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("parsing driver1: ", err)
|
|
||||||
}
|
|
||||||
// Add another driver template.
|
|
||||||
_, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("parsing driver2: ", err)
|
|
||||||
}
|
|
||||||
// We load all the templates before execution. This package does not require
|
|
||||||
// that behavior but html/template's escaping does, so it's a good habit.
|
|
||||||
err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("driver1 execution: %s", err)
|
|
||||||
}
|
|
||||||
err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("driver2 execution: %s", err)
|
|
||||||
}
|
|
||||||
// Output:
|
|
||||||
// Driver 1 calls T1: (T1 invokes T2: (This is T2))
|
|
||||||
// Driver 2 calls T2: (This is T2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates how to use one group of driver
|
|
||||||
// templates with distinct sets of helper templates.
|
|
||||||
func ExampleTemplate_share() {
|
|
||||||
// Here we create a temporary directory and populate it with our sample
|
|
||||||
// template definition files; usually the template files would already
|
|
||||||
// exist in some location known to the program.
|
|
||||||
dir := createTestDir([]templateFile{
|
|
||||||
// T0.tmpl is a plain template file that just invokes T1.
|
|
||||||
{"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
|
|
||||||
// T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
|
|
||||||
{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
|
|
||||||
})
|
|
||||||
// Clean up after the test; another quirk of running as an example.
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
// pattern is the glob pattern used to find all the template files.
|
|
||||||
pattern := filepath.Join(dir, "*.tmpl")
|
|
||||||
|
|
||||||
// Here starts the example proper.
|
|
||||||
// Load the drivers.
|
|
||||||
drivers := template.Must(template.ParseGlob(pattern))
|
|
||||||
|
|
||||||
// We must define an implementation of the T2 template. First we clone
|
|
||||||
// the drivers, then add a definition of T2 to the template name space.
|
|
||||||
|
|
||||||
// 1. Clone the helper set to create a new name space from which to run them.
|
|
||||||
first, err := drivers.Clone()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("cloning helpers: ", err)
|
|
||||||
}
|
|
||||||
// 2. Define T2, version A, and parse it.
|
|
||||||
_, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("parsing T2: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now repeat the whole thing, using a different version of T2.
|
|
||||||
// 1. Clone the drivers.
|
|
||||||
second, err := drivers.Clone()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("cloning drivers: ", err)
|
|
||||||
}
|
|
||||||
// 2. Define T2, version B, and parse it.
|
|
||||||
_, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("parsing T2: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the templates in the reverse order to verify the
|
|
||||||
// first is unaffected by the second.
|
|
||||||
err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("second execution: %s", err)
|
|
||||||
}
|
|
||||||
err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("first: execution: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
|
|
||||||
// T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
|
|
||||||
}
|
|
55
vendor/github.com/alecthomas/template/examplefunc_test.go
generated
vendored
55
vendor/github.com/alecthomas/template/examplefunc_test.go
generated
vendored
|
@ -1,55 +0,0 @@
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package template_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/alecthomas/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This example demonstrates a custom function to process template text.
|
|
||||||
// It installs the strings.Title function and uses it to
|
|
||||||
// Make Title Text Look Good In Our Template's Output.
|
|
||||||
func ExampleTemplate_func() {
|
|
||||||
// First we create a FuncMap with which to register the function.
|
|
||||||
funcMap := template.FuncMap{
|
|
||||||
// The name "title" is what the function will be called in the template text.
|
|
||||||
"title": strings.Title,
|
|
||||||
}
|
|
||||||
|
|
||||||
// A simple template definition to test our function.
|
|
||||||
// We print the input text several ways:
|
|
||||||
// - the original
|
|
||||||
// - title-cased
|
|
||||||
// - title-cased and then printed with %q
|
|
||||||
// - printed with %q and then title-cased.
|
|
||||||
const templateText = `
|
|
||||||
Input: {{printf "%q" .}}
|
|
||||||
Output 0: {{title .}}
|
|
||||||
Output 1: {{title . | printf "%q"}}
|
|
||||||
Output 2: {{printf "%q" . | title}}
|
|
||||||
`
|
|
||||||
|
|
||||||
// Create a template, add the function map, and parse the text.
|
|
||||||
tmpl, err := template.New("titleTest").Funcs(funcMap).Parse(templateText)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("parsing: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the template to verify the output.
|
|
||||||
err = tmpl.Execute(os.Stdout, "the go programming language")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("execution: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Input: "the go programming language"
|
|
||||||
// Output 0: The Go Programming Language
|
|
||||||
// Output 1: "The Go Programming Language"
|
|
||||||
// Output 2: "The Go Programming Language"
|
|
||||||
}
|
|
1044
vendor/github.com/alecthomas/template/exec_test.go
generated
vendored
1044
vendor/github.com/alecthomas/template/exec_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
293
vendor/github.com/alecthomas/template/multi_test.go
generated
vendored
293
vendor/github.com/alecthomas/template/multi_test.go
generated
vendored
|
@ -1,293 +0,0 @@
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package template
|
|
||||||
|
|
||||||
// Tests for mulitple-template parsing and execution.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/template/parse"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
noError = true
|
|
||||||
hasError = false
|
|
||||||
)
|
|
||||||
|
|
||||||
type multiParseTest struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
ok bool
|
|
||||||
names []string
|
|
||||||
results []string
|
|
||||||
}
|
|
||||||
|
|
||||||
var multiParseTests = []multiParseTest{
|
|
||||||
{"empty", "", noError,
|
|
||||||
nil,
|
|
||||||
nil},
|
|
||||||
{"one", `{{define "foo"}} FOO {{end}}`, noError,
|
|
||||||
[]string{"foo"},
|
|
||||||
[]string{" FOO "}},
|
|
||||||
{"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError,
|
|
||||||
[]string{"foo", "bar"},
|
|
||||||
[]string{" FOO ", " BAR "}},
|
|
||||||
// errors
|
|
||||||
{"missing end", `{{define "foo"}} FOO `, hasError,
|
|
||||||
nil,
|
|
||||||
nil},
|
|
||||||
{"malformed name", `{{define "foo}} FOO `, hasError,
|
|
||||||
nil,
|
|
||||||
nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultiParse(t *testing.T) {
|
|
||||||
for _, test := range multiParseTests {
|
|
||||||
template, err := New("root").Parse(test.input)
|
|
||||||
switch {
|
|
||||||
case err == nil && !test.ok:
|
|
||||||
t.Errorf("%q: expected error; got none", test.name)
|
|
||||||
continue
|
|
||||||
case err != nil && test.ok:
|
|
||||||
t.Errorf("%q: unexpected error: %v", test.name, err)
|
|
||||||
continue
|
|
||||||
case err != nil && !test.ok:
|
|
||||||
// expected error, got one
|
|
||||||
if *debug {
|
|
||||||
fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if template == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(template.tmpl) != len(test.names)+1 { // +1 for root
|
|
||||||
t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(template.tmpl))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for i, name := range test.names {
|
|
||||||
tmpl, ok := template.tmpl[name]
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("%s: can't find template %q", test.name, name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result := tmpl.Root.String()
|
|
||||||
if result != test.results[i] {
|
|
||||||
t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var multiExecTests = []execTest{
|
|
||||||
{"empty", "", "", nil, true},
|
|
||||||
{"text", "some text", "some text", nil, true},
|
|
||||||
{"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true},
|
|
||||||
{"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true},
|
|
||||||
{"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true},
|
|
||||||
{"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
|
|
||||||
{"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
|
|
||||||
{"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
|
|
||||||
{"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true},
|
|
||||||
|
|
||||||
// User-defined function: test argument evaluator.
|
|
||||||
{"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
|
|
||||||
{"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true},
|
|
||||||
}
|
|
||||||
|
|
||||||
// These strings are also in testdata/*.
|
|
||||||
const multiText1 = `
|
|
||||||
{{define "x"}}TEXT{{end}}
|
|
||||||
{{define "dotV"}}{{.V}}{{end}}
|
|
||||||
`
|
|
||||||
|
|
||||||
const multiText2 = `
|
|
||||||
{{define "dot"}}{{.}}{{end}}
|
|
||||||
{{define "nested"}}{{template "dot" .}}{{end}}
|
|
||||||
`
|
|
||||||
|
|
||||||
func TestMultiExecute(t *testing.T) {
|
|
||||||
// Declare a couple of templates first.
|
|
||||||
template, err := New("root").Parse(multiText1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("parse error for 1: %s", err)
|
|
||||||
}
|
|
||||||
_, err = template.Parse(multiText2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("parse error for 2: %s", err)
|
|
||||||
}
|
|
||||||
testExecute(multiExecTests, template, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseFiles(t *testing.T) {
|
|
||||||
_, err := ParseFiles("DOES NOT EXIST")
|
|
||||||
if err == nil {
|
|
||||||
t.Error("expected error for non-existent file; got none")
|
|
||||||
}
|
|
||||||
template := New("root")
|
|
||||||
_, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error parsing files: %v", err)
|
|
||||||
}
|
|
||||||
testExecute(multiExecTests, template, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseGlob(t *testing.T) {
|
|
||||||
_, err := ParseGlob("DOES NOT EXIST")
|
|
||||||
if err == nil {
|
|
||||||
t.Error("expected error for non-existent file; got none")
|
|
||||||
}
|
|
||||||
_, err = New("error").ParseGlob("[x")
|
|
||||||
if err == nil {
|
|
||||||
t.Error("expected error for bad pattern; got none")
|
|
||||||
}
|
|
||||||
template := New("root")
|
|
||||||
_, err = template.ParseGlob("testdata/file*.tmpl")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error parsing files: %v", err)
|
|
||||||
}
|
|
||||||
testExecute(multiExecTests, template, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// In these tests, actual content (not just template definitions) comes from the parsed files.
|
|
||||||
|
|
||||||
var templateFileExecTests = []execTest{
|
|
||||||
{"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseFilesWithData(t *testing.T) {
|
|
||||||
template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error parsing files: %v", err)
|
|
||||||
}
|
|
||||||
testExecute(templateFileExecTests, template, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseGlobWithData(t *testing.T) {
|
|
||||||
template, err := New("root").ParseGlob("testdata/tmpl*.tmpl")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error parsing files: %v", err)
|
|
||||||
}
|
|
||||||
testExecute(templateFileExecTests, template, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}`
|
|
||||||
cloneText2 = `{{define "b"}}b{{end}}`
|
|
||||||
cloneText3 = `{{define "c"}}root{{end}}`
|
|
||||||
cloneText4 = `{{define "c"}}clone{{end}}`
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestClone(t *testing.T) {
|
|
||||||
// Create some templates and clone the root.
|
|
||||||
root, err := New("root").Parse(cloneText1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = root.Parse(cloneText2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
clone := Must(root.Clone())
|
|
||||||
// Add variants to both.
|
|
||||||
_, err = root.Parse(cloneText3)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = clone.Parse(cloneText4)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Verify that the clone is self-consistent.
|
|
||||||
for k, v := range clone.tmpl {
|
|
||||||
if k == clone.name && v.tmpl[k] != clone {
|
|
||||||
t.Error("clone does not contain root")
|
|
||||||
}
|
|
||||||
if v != v.tmpl[v.name] {
|
|
||||||
t.Errorf("clone does not contain self for %q", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Execute root.
|
|
||||||
var b bytes.Buffer
|
|
||||||
err = root.ExecuteTemplate(&b, "a", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if b.String() != "broot" {
|
|
||||||
t.Errorf("expected %q got %q", "broot", b.String())
|
|
||||||
}
|
|
||||||
// Execute copy.
|
|
||||||
b.Reset()
|
|
||||||
err = clone.ExecuteTemplate(&b, "a", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if b.String() != "bclone" {
|
|
||||||
t.Errorf("expected %q got %q", "bclone", b.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddParseTree(t *testing.T) {
|
|
||||||
// Create some templates.
|
|
||||||
root, err := New("root").Parse(cloneText1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = root.Parse(cloneText2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Add a new parse tree.
|
|
||||||
tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
added, err := root.AddParseTree("c", tree["c"])
|
|
||||||
// Execute.
|
|
||||||
var b bytes.Buffer
|
|
||||||
err = added.ExecuteTemplate(&b, "a", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if b.String() != "broot" {
|
|
||||||
t.Errorf("expected %q got %q", "broot", b.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue 7032
|
|
||||||
func TestAddParseTreeToUnparsedTemplate(t *testing.T) {
|
|
||||||
master := "{{define \"master\"}}{{end}}"
|
|
||||||
tmpl := New("master")
|
|
||||||
tree, err := parse.Parse("master", master, "", "", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected parse err: %v", err)
|
|
||||||
}
|
|
||||||
masterTree := tree["master"]
|
|
||||||
tmpl.AddParseTree("master", masterTree) // used to panic
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRedefinition(t *testing.T) {
|
|
||||||
var tmpl *Template
|
|
||||||
var err error
|
|
||||||
if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil {
|
|
||||||
t.Fatalf("parse 1: %v", err)
|
|
||||||
}
|
|
||||||
if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
if !strings.Contains(err.Error(), "redefinition") {
|
|
||||||
t.Fatalf("expected redefinition error; got %v", err)
|
|
||||||
}
|
|
||||||
if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
if !strings.Contains(err.Error(), "redefinition") {
|
|
||||||
t.Fatalf("expected redefinition error; got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
468
vendor/github.com/alecthomas/template/parse/lex_test.go
generated
vendored
468
vendor/github.com/alecthomas/template/parse/lex_test.go
generated
vendored
|
@ -1,468 +0,0 @@
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package parse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Make the types prettyprint.
|
|
||||||
var itemName = map[itemType]string{
|
|
||||||
itemError: "error",
|
|
||||||
itemBool: "bool",
|
|
||||||
itemChar: "char",
|
|
||||||
itemCharConstant: "charconst",
|
|
||||||
itemComplex: "complex",
|
|
||||||
itemColonEquals: ":=",
|
|
||||||
itemEOF: "EOF",
|
|
||||||
itemField: "field",
|
|
||||||
itemIdentifier: "identifier",
|
|
||||||
itemLeftDelim: "left delim",
|
|
||||||
itemLeftParen: "(",
|
|
||||||
itemNumber: "number",
|
|
||||||
itemPipe: "pipe",
|
|
||||||
itemRawString: "raw string",
|
|
||||||
itemRightDelim: "right delim",
|
|
||||||
itemElideNewline: "elide newline",
|
|
||||||
itemRightParen: ")",
|
|
||||||
itemSpace: "space",
|
|
||||||
itemString: "string",
|
|
||||||
itemVariable: "variable",
|
|
||||||
|
|
||||||
// keywords
|
|
||||||
itemDot: ".",
|
|
||||||
itemDefine: "define",
|
|
||||||
itemElse: "else",
|
|
||||||
itemIf: "if",
|
|
||||||
itemEnd: "end",
|
|
||||||
itemNil: "nil",
|
|
||||||
itemRange: "range",
|
|
||||||
itemTemplate: "template",
|
|
||||||
itemWith: "with",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i itemType) String() string {
|
|
||||||
s := itemName[i]
|
|
||||||
if s == "" {
|
|
||||||
return fmt.Sprintf("item%d", int(i))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
type lexTest struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
items []item
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
tEOF = item{itemEOF, 0, ""}
|
|
||||||
tFor = item{itemIdentifier, 0, "for"}
|
|
||||||
tLeft = item{itemLeftDelim, 0, "{{"}
|
|
||||||
tLpar = item{itemLeftParen, 0, "("}
|
|
||||||
tPipe = item{itemPipe, 0, "|"}
|
|
||||||
tQuote = item{itemString, 0, `"abc \n\t\" "`}
|
|
||||||
tRange = item{itemRange, 0, "range"}
|
|
||||||
tRight = item{itemRightDelim, 0, "}}"}
|
|
||||||
tElideNewline = item{itemElideNewline, 0, "\\"}
|
|
||||||
tRpar = item{itemRightParen, 0, ")"}
|
|
||||||
tSpace = item{itemSpace, 0, " "}
|
|
||||||
raw = "`" + `abc\n\t\" ` + "`"
|
|
||||||
tRawQuote = item{itemRawString, 0, raw}
|
|
||||||
)
|
|
||||||
|
|
||||||
var lexTests = []lexTest{
|
|
||||||
{"empty", "", []item{tEOF}},
|
|
||||||
{"spaces", " \t\n", []item{{itemText, 0, " \t\n"}, tEOF}},
|
|
||||||
{"text", `now is the time`, []item{{itemText, 0, "now is the time"}, tEOF}},
|
|
||||||
{"elide newline", "{{}}\\", []item{tLeft, tRight, tElideNewline, tEOF}},
|
|
||||||
{"text with comment", "hello-{{/* this is a comment */}}-world", []item{
|
|
||||||
{itemText, 0, "hello-"},
|
|
||||||
{itemText, 0, "-world"},
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"punctuation", "{{,@% }}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemChar, 0, ","},
|
|
||||||
{itemChar, 0, "@"},
|
|
||||||
{itemChar, 0, "%"},
|
|
||||||
tSpace,
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"parens", "{{((3))}}", []item{
|
|
||||||
tLeft,
|
|
||||||
tLpar,
|
|
||||||
tLpar,
|
|
||||||
{itemNumber, 0, "3"},
|
|
||||||
tRpar,
|
|
||||||
tRpar,
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
|
|
||||||
{"for", `{{for}}`, []item{tLeft, tFor, tRight, tEOF}},
|
|
||||||
{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
|
|
||||||
{"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}},
|
|
||||||
{"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemNumber, 0, "1"},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "02"},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "0x14"},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "-7.2i"},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "1e3"},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "+1.2e-4"},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "4.2i"},
|
|
||||||
tSpace,
|
|
||||||
{itemComplex, 0, "1+2i"},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{
|
|
||||||
tLeft,
|
|
||||||
{itemCharConstant, 0, `'a'`},
|
|
||||||
tSpace,
|
|
||||||
{itemCharConstant, 0, `'\n'`},
|
|
||||||
tSpace,
|
|
||||||
{itemCharConstant, 0, `'\''`},
|
|
||||||
tSpace,
|
|
||||||
{itemCharConstant, 0, `'\\'`},
|
|
||||||
tSpace,
|
|
||||||
{itemCharConstant, 0, `'\u00FF'`},
|
|
||||||
tSpace,
|
|
||||||
{itemCharConstant, 0, `'\xFF'`},
|
|
||||||
tSpace,
|
|
||||||
{itemCharConstant, 0, `'本'`},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"bools", "{{true false}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemBool, 0, "true"},
|
|
||||||
tSpace,
|
|
||||||
{itemBool, 0, "false"},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"dot", "{{.}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemDot, 0, "."},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"nil", "{{nil}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemNil, 0, "nil"},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"dots", "{{.x . .2 .x.y.z}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemField, 0, ".x"},
|
|
||||||
tSpace,
|
|
||||||
{itemDot, 0, "."},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, ".2"},
|
|
||||||
tSpace,
|
|
||||||
{itemField, 0, ".x"},
|
|
||||||
{itemField, 0, ".y"},
|
|
||||||
{itemField, 0, ".z"},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"keywords", "{{range if else end with}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemRange, 0, "range"},
|
|
||||||
tSpace,
|
|
||||||
{itemIf, 0, "if"},
|
|
||||||
tSpace,
|
|
||||||
{itemElse, 0, "else"},
|
|
||||||
tSpace,
|
|
||||||
{itemEnd, 0, "end"},
|
|
||||||
tSpace,
|
|
||||||
{itemWith, 0, "with"},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemVariable, 0, "$c"},
|
|
||||||
tSpace,
|
|
||||||
{itemColonEquals, 0, ":="},
|
|
||||||
tSpace,
|
|
||||||
{itemIdentifier, 0, "printf"},
|
|
||||||
tSpace,
|
|
||||||
{itemVariable, 0, "$"},
|
|
||||||
tSpace,
|
|
||||||
{itemVariable, 0, "$hello"},
|
|
||||||
tSpace,
|
|
||||||
{itemVariable, 0, "$23"},
|
|
||||||
tSpace,
|
|
||||||
{itemVariable, 0, "$"},
|
|
||||||
tSpace,
|
|
||||||
{itemVariable, 0, "$var"},
|
|
||||||
{itemField, 0, ".Field"},
|
|
||||||
tSpace,
|
|
||||||
{itemField, 0, ".Method"},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"variable invocation", "{{$x 23}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemVariable, 0, "$x"},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "23"},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{
|
|
||||||
{itemText, 0, "intro "},
|
|
||||||
tLeft,
|
|
||||||
{itemIdentifier, 0, "echo"},
|
|
||||||
tSpace,
|
|
||||||
{itemIdentifier, 0, "hi"},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "1.2"},
|
|
||||||
tSpace,
|
|
||||||
tPipe,
|
|
||||||
{itemIdentifier, 0, "noargs"},
|
|
||||||
tPipe,
|
|
||||||
{itemIdentifier, 0, "args"},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "1"},
|
|
||||||
tSpace,
|
|
||||||
{itemString, 0, `"hi"`},
|
|
||||||
tRight,
|
|
||||||
{itemText, 0, " outro"},
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"declaration", "{{$v := 3}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemVariable, 0, "$v"},
|
|
||||||
tSpace,
|
|
||||||
{itemColonEquals, 0, ":="},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "3"},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"2 declarations", "{{$v , $w := 3}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemVariable, 0, "$v"},
|
|
||||||
tSpace,
|
|
||||||
{itemChar, 0, ","},
|
|
||||||
tSpace,
|
|
||||||
{itemVariable, 0, "$w"},
|
|
||||||
tSpace,
|
|
||||||
{itemColonEquals, 0, ":="},
|
|
||||||
tSpace,
|
|
||||||
{itemNumber, 0, "3"},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"field of parenthesized expression", "{{(.X).Y}}", []item{
|
|
||||||
tLeft,
|
|
||||||
tLpar,
|
|
||||||
{itemField, 0, ".X"},
|
|
||||||
tRpar,
|
|
||||||
{itemField, 0, ".Y"},
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
// errors
|
|
||||||
{"badchar", "#{{\x01}}", []item{
|
|
||||||
{itemText, 0, "#"},
|
|
||||||
tLeft,
|
|
||||||
{itemError, 0, "unrecognized character in action: U+0001"},
|
|
||||||
}},
|
|
||||||
{"unclosed action", "{{\n}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemError, 0, "unclosed action"},
|
|
||||||
}},
|
|
||||||
{"EOF in action", "{{range", []item{
|
|
||||||
tLeft,
|
|
||||||
tRange,
|
|
||||||
{itemError, 0, "unclosed action"},
|
|
||||||
}},
|
|
||||||
{"unclosed quote", "{{\"\n\"}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemError, 0, "unterminated quoted string"},
|
|
||||||
}},
|
|
||||||
{"unclosed raw quote", "{{`xx\n`}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemError, 0, "unterminated raw quoted string"},
|
|
||||||
}},
|
|
||||||
{"unclosed char constant", "{{'\n}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemError, 0, "unterminated character constant"},
|
|
||||||
}},
|
|
||||||
{"bad number", "{{3k}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemError, 0, `bad number syntax: "3k"`},
|
|
||||||
}},
|
|
||||||
{"unclosed paren", "{{(3}}", []item{
|
|
||||||
tLeft,
|
|
||||||
tLpar,
|
|
||||||
{itemNumber, 0, "3"},
|
|
||||||
{itemError, 0, `unclosed left paren`},
|
|
||||||
}},
|
|
||||||
{"extra right paren", "{{3)}}", []item{
|
|
||||||
tLeft,
|
|
||||||
{itemNumber, 0, "3"},
|
|
||||||
tRpar,
|
|
||||||
{itemError, 0, `unexpected right paren U+0029 ')'`},
|
|
||||||
}},
|
|
||||||
|
|
||||||
// Fixed bugs
|
|
||||||
// Many elements in an action blew the lookahead until
|
|
||||||
// we made lexInsideAction not loop.
|
|
||||||
{"long pipeline deadlock", "{{|||||}}", []item{
|
|
||||||
tLeft,
|
|
||||||
tPipe,
|
|
||||||
tPipe,
|
|
||||||
tPipe,
|
|
||||||
tPipe,
|
|
||||||
tPipe,
|
|
||||||
tRight,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"text with bad comment", "hello-{{/*/}}-world", []item{
|
|
||||||
{itemText, 0, "hello-"},
|
|
||||||
{itemError, 0, `unclosed comment`},
|
|
||||||
}},
|
|
||||||
{"text with comment close separted from delim", "hello-{{/* */ }}-world", []item{
|
|
||||||
{itemText, 0, "hello-"},
|
|
||||||
{itemError, 0, `comment ends before closing delimiter`},
|
|
||||||
}},
|
|
||||||
// This one is an error that we can't catch because it breaks templates with
|
|
||||||
// minimized JavaScript. Should have fixed it before Go 1.1.
|
|
||||||
{"unmatched right delimiter", "hello-{.}}-world", []item{
|
|
||||||
{itemText, 0, "hello-{.}}-world"},
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect gathers the emitted items into a slice.
|
|
||||||
func collect(t *lexTest, left, right string) (items []item) {
|
|
||||||
l := lex(t.name, t.input, left, right)
|
|
||||||
for {
|
|
||||||
item := l.nextItem()
|
|
||||||
items = append(items, item)
|
|
||||||
if item.typ == itemEOF || item.typ == itemError {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func equal(i1, i2 []item, checkPos bool) bool {
|
|
||||||
if len(i1) != len(i2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for k := range i1 {
|
|
||||||
if i1[k].typ != i2[k].typ {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if i1[k].val != i2[k].val {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if checkPos && i1[k].pos != i2[k].pos {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLex(t *testing.T) {
|
|
||||||
for _, test := range lexTests {
|
|
||||||
items := collect(&test, "", "")
|
|
||||||
if !equal(items, test.items, false) {
|
|
||||||
t.Errorf("%s: got\n\t%+v\nexpected\n\t%v", test.name, items, test.items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some easy cases from above, but with delimiters $$ and @@
|
|
||||||
var lexDelimTests = []lexTest{
|
|
||||||
{"punctuation", "$$,@%{{}}@@", []item{
|
|
||||||
tLeftDelim,
|
|
||||||
{itemChar, 0, ","},
|
|
||||||
{itemChar, 0, "@"},
|
|
||||||
{itemChar, 0, "%"},
|
|
||||||
{itemChar, 0, "{"},
|
|
||||||
{itemChar, 0, "{"},
|
|
||||||
{itemChar, 0, "}"},
|
|
||||||
{itemChar, 0, "}"},
|
|
||||||
tRightDelim,
|
|
||||||
tEOF,
|
|
||||||
}},
|
|
||||||
{"empty action", `$$@@`, []item{tLeftDelim, tRightDelim, tEOF}},
|
|
||||||
{"for", `$$for@@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}},
|
|
||||||
{"quote", `$$"abc \n\t\" "@@`, []item{tLeftDelim, tQuote, tRightDelim, tEOF}},
|
|
||||||
{"raw quote", "$$" + raw + "@@", []item{tLeftDelim, tRawQuote, tRightDelim, tEOF}},
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
tLeftDelim = item{itemLeftDelim, 0, "$$"}
|
|
||||||
tRightDelim = item{itemRightDelim, 0, "@@"}
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDelims(t *testing.T) {
|
|
||||||
for _, test := range lexDelimTests {
|
|
||||||
items := collect(&test, "$$", "@@")
|
|
||||||
if !equal(items, test.items, false) {
|
|
||||||
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lexPosTests = []lexTest{
|
|
||||||
{"empty", "", []item{tEOF}},
|
|
||||||
{"punctuation", "{{,@%#}}", []item{
|
|
||||||
{itemLeftDelim, 0, "{{"},
|
|
||||||
{itemChar, 2, ","},
|
|
||||||
{itemChar, 3, "@"},
|
|
||||||
{itemChar, 4, "%"},
|
|
||||||
{itemChar, 5, "#"},
|
|
||||||
{itemRightDelim, 6, "}}"},
|
|
||||||
{itemEOF, 8, ""},
|
|
||||||
}},
|
|
||||||
{"sample", "0123{{hello}}xyz", []item{
|
|
||||||
{itemText, 0, "0123"},
|
|
||||||
{itemLeftDelim, 4, "{{"},
|
|
||||||
{itemIdentifier, 6, "hello"},
|
|
||||||
{itemRightDelim, 11, "}}"},
|
|
||||||
{itemText, 13, "xyz"},
|
|
||||||
{itemEOF, 16, ""},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
// The other tests don't check position, to make the test cases easier to construct.
|
|
||||||
// This one does.
|
|
||||||
func TestPos(t *testing.T) {
|
|
||||||
for _, test := range lexPosTests {
|
|
||||||
items := collect(&test, "", "")
|
|
||||||
if !equal(items, test.items, true) {
|
|
||||||
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
|
|
||||||
if len(items) == len(test.items) {
|
|
||||||
// Detailed print; avoid item.String() to expose the position value.
|
|
||||||
for i := range items {
|
|
||||||
if !equal(items[i:i+1], test.items[i:i+1], true) {
|
|
||||||
i1 := items[i]
|
|
||||||
i2 := test.items[i]
|
|
||||||
t.Errorf("\t#%d: got {%v %d %q} expected {%v %d %q}", i, i1.typ, i1.pos, i1.val, i2.typ, i2.pos, i2.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
426
vendor/github.com/alecthomas/template/parse/parse_test.go
generated
vendored
426
vendor/github.com/alecthomas/template/parse/parse_test.go
generated
vendored
|
@ -1,426 +0,0 @@
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package parse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var debug = flag.Bool("debug", false, "show the errors produced by the main tests")
|
|
||||||
|
|
||||||
type numberTest struct {
|
|
||||||
text string
|
|
||||||
isInt bool
|
|
||||||
isUint bool
|
|
||||||
isFloat bool
|
|
||||||
isComplex bool
|
|
||||||
int64
|
|
||||||
uint64
|
|
||||||
float64
|
|
||||||
complex128
|
|
||||||
}
|
|
||||||
|
|
||||||
var numberTests = []numberTest{
|
|
||||||
// basics
|
|
||||||
{"0", true, true, true, false, 0, 0, 0, 0},
|
|
||||||
{"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint.
|
|
||||||
{"73", true, true, true, false, 73, 73, 73, 0},
|
|
||||||
{"073", true, true, true, false, 073, 073, 073, 0},
|
|
||||||
{"0x73", true, true, true, false, 0x73, 0x73, 0x73, 0},
|
|
||||||
{"-73", true, false, true, false, -73, 0, -73, 0},
|
|
||||||
{"+73", true, false, true, false, 73, 0, 73, 0},
|
|
||||||
{"100", true, true, true, false, 100, 100, 100, 0},
|
|
||||||
{"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0},
|
|
||||||
{"-1e9", true, false, true, false, -1e9, 0, -1e9, 0},
|
|
||||||
{"-1.2", false, false, true, false, 0, 0, -1.2, 0},
|
|
||||||
{"1e19", false, true, true, false, 0, 1e19, 1e19, 0},
|
|
||||||
{"-1e19", false, false, true, false, 0, 0, -1e19, 0},
|
|
||||||
{"4i", false, false, false, true, 0, 0, 0, 4i},
|
|
||||||
{"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i},
|
|
||||||
{"073i", false, false, false, true, 0, 0, 0, 73i}, // not octal!
|
|
||||||
// complex with 0 imaginary are float (and maybe integer)
|
|
||||||
{"0i", true, true, true, true, 0, 0, 0, 0},
|
|
||||||
{"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2},
|
|
||||||
{"-12+0i", true, false, true, true, -12, 0, -12, -12},
|
|
||||||
{"13+0i", true, true, true, true, 13, 13, 13, 13},
|
|
||||||
// funny bases
|
|
||||||
{"0123", true, true, true, false, 0123, 0123, 0123, 0},
|
|
||||||
{"-0x0", true, true, true, false, 0, 0, 0, 0},
|
|
||||||
{"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0},
|
|
||||||
// character constants
|
|
||||||
{`'a'`, true, true, true, false, 'a', 'a', 'a', 0},
|
|
||||||
{`'\n'`, true, true, true, false, '\n', '\n', '\n', 0},
|
|
||||||
{`'\\'`, true, true, true, false, '\\', '\\', '\\', 0},
|
|
||||||
{`'\''`, true, true, true, false, '\'', '\'', '\'', 0},
|
|
||||||
{`'\xFF'`, true, true, true, false, 0xFF, 0xFF, 0xFF, 0},
|
|
||||||
{`'パ'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
|
|
||||||
{`'\u30d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
|
|
||||||
{`'\U000030d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
|
|
||||||
// some broken syntax
|
|
||||||
{text: "+-2"},
|
|
||||||
{text: "0x123."},
|
|
||||||
{text: "1e."},
|
|
||||||
{text: "0xi."},
|
|
||||||
{text: "1+2."},
|
|
||||||
{text: "'x"},
|
|
||||||
{text: "'xx'"},
|
|
||||||
// Issue 8622 - 0xe parsed as floating point. Very embarrassing.
|
|
||||||
{"0xef", true, true, true, false, 0xef, 0xef, 0xef, 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNumberParse(t *testing.T) {
|
|
||||||
for _, test := range numberTests {
|
|
||||||
// If fmt.Sscan thinks it's complex, it's complex. We can't trust the output
|
|
||||||
// because imaginary comes out as a number.
|
|
||||||
var c complex128
|
|
||||||
typ := itemNumber
|
|
||||||
var tree *Tree
|
|
||||||
if test.text[0] == '\'' {
|
|
||||||
typ = itemCharConstant
|
|
||||||
} else {
|
|
||||||
_, err := fmt.Sscan(test.text, &c)
|
|
||||||
if err == nil {
|
|
||||||
typ = itemComplex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n, err := tree.newNumber(0, test.text, typ)
|
|
||||||
ok := test.isInt || test.isUint || test.isFloat || test.isComplex
|
|
||||||
if ok && err != nil {
|
|
||||||
t.Errorf("unexpected error for %q: %s", test.text, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !ok && err == nil {
|
|
||||||
t.Errorf("expected error for %q", test.text)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
if *debug {
|
|
||||||
fmt.Printf("%s\n\t%s\n", test.text, err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if n.IsComplex != test.isComplex {
|
|
||||||
t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex)
|
|
||||||
}
|
|
||||||
if test.isInt {
|
|
||||||
if !n.IsInt {
|
|
||||||
t.Errorf("expected integer for %q", test.text)
|
|
||||||
}
|
|
||||||
if n.Int64 != test.int64 {
|
|
||||||
t.Errorf("int64 for %q should be %d Is %d", test.text, test.int64, n.Int64)
|
|
||||||
}
|
|
||||||
} else if n.IsInt {
|
|
||||||
t.Errorf("did not expect integer for %q", test.text)
|
|
||||||
}
|
|
||||||
if test.isUint {
|
|
||||||
if !n.IsUint {
|
|
||||||
t.Errorf("expected unsigned integer for %q", test.text)
|
|
||||||
}
|
|
||||||
if n.Uint64 != test.uint64 {
|
|
||||||
t.Errorf("uint64 for %q should be %d Is %d", test.text, test.uint64, n.Uint64)
|
|
||||||
}
|
|
||||||
} else if n.IsUint {
|
|
||||||
t.Errorf("did not expect unsigned integer for %q", test.text)
|
|
||||||
}
|
|
||||||
if test.isFloat {
|
|
||||||
if !n.IsFloat {
|
|
||||||
t.Errorf("expected float for %q", test.text)
|
|
||||||
}
|
|
||||||
if n.Float64 != test.float64 {
|
|
||||||
t.Errorf("float64 for %q should be %g Is %g", test.text, test.float64, n.Float64)
|
|
||||||
}
|
|
||||||
} else if n.IsFloat {
|
|
||||||
t.Errorf("did not expect float for %q", test.text)
|
|
||||||
}
|
|
||||||
if test.isComplex {
|
|
||||||
if !n.IsComplex {
|
|
||||||
t.Errorf("expected complex for %q", test.text)
|
|
||||||
}
|
|
||||||
if n.Complex128 != test.complex128 {
|
|
||||||
t.Errorf("complex128 for %q should be %g Is %g", test.text, test.complex128, n.Complex128)
|
|
||||||
}
|
|
||||||
} else if n.IsComplex {
|
|
||||||
t.Errorf("did not expect complex for %q", test.text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type parseTest struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
ok bool
|
|
||||||
result string // what the user would see in an error message.
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
noError = true
|
|
||||||
hasError = false
|
|
||||||
)
|
|
||||||
|
|
||||||
var parseTests = []parseTest{
|
|
||||||
{"empty", "", noError,
|
|
||||||
``},
|
|
||||||
{"comment", "{{/*\n\n\n*/}}", noError,
|
|
||||||
``},
|
|
||||||
{"spaces", " \t\n", noError,
|
|
||||||
`" \t\n"`},
|
|
||||||
{"text", "some text", noError,
|
|
||||||
`"some text"`},
|
|
||||||
{"emptyAction", "{{}}", hasError,
|
|
||||||
`{{}}`},
|
|
||||||
{"field", "{{.X}}", noError,
|
|
||||||
`{{.X}}`},
|
|
||||||
{"simple command", "{{printf}}", noError,
|
|
||||||
`{{printf}}`},
|
|
||||||
{"$ invocation", "{{$}}", noError,
|
|
||||||
"{{$}}"},
|
|
||||||
{"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError,
|
|
||||||
"{{with $x := 3}}{{$x 23}}{{end}}"},
|
|
||||||
{"variable with fields", "{{$.I}}", noError,
|
|
||||||
"{{$.I}}"},
|
|
||||||
{"multi-word command", "{{printf `%d` 23}}", noError,
|
|
||||||
"{{printf `%d` 23}}"},
|
|
||||||
{"pipeline", "{{.X|.Y}}", noError,
|
|
||||||
`{{.X | .Y}}`},
|
|
||||||
{"pipeline with decl", "{{$x := .X|.Y}}", noError,
|
|
||||||
`{{$x := .X | .Y}}`},
|
|
||||||
{"nested pipeline", "{{.X (.Y .Z) (.A | .B .C) (.E)}}", noError,
|
|
||||||
`{{.X (.Y .Z) (.A | .B .C) (.E)}}`},
|
|
||||||
{"field applied to parentheses", "{{(.Y .Z).Field}}", noError,
|
|
||||||
`{{(.Y .Z).Field}}`},
|
|
||||||
{"simple if", "{{if .X}}hello{{end}}", noError,
|
|
||||||
`{{if .X}}"hello"{{end}}`},
|
|
||||||
{"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
|
|
||||||
`{{if .X}}"true"{{else}}"false"{{end}}`},
|
|
||||||
{"if with else if", "{{if .X}}true{{else if .Y}}false{{end}}", noError,
|
|
||||||
`{{if .X}}"true"{{else}}{{if .Y}}"false"{{end}}{{end}}`},
|
|
||||||
{"if else chain", "+{{if .X}}X{{else if .Y}}Y{{else if .Z}}Z{{end}}+", noError,
|
|
||||||
`"+"{{if .X}}"X"{{else}}{{if .Y}}"Y"{{else}}{{if .Z}}"Z"{{end}}{{end}}{{end}}"+"`},
|
|
||||||
{"simple range", "{{range .X}}hello{{end}}", noError,
|
|
||||||
`{{range .X}}"hello"{{end}}`},
|
|
||||||
{"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError,
|
|
||||||
`{{range .X.Y.Z}}"hello"{{end}}`},
|
|
||||||
{"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError,
|
|
||||||
`{{range .X}}"hello"{{range .Y}}"goodbye"{{end}}{{end}}`},
|
|
||||||
{"range with else", "{{range .X}}true{{else}}false{{end}}", noError,
|
|
||||||
`{{range .X}}"true"{{else}}"false"{{end}}`},
|
|
||||||
{"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError,
|
|
||||||
`{{range .X | .M}}"true"{{else}}"false"{{end}}`},
|
|
||||||
{"range []int", "{{range .SI}}{{.}}{{end}}", noError,
|
|
||||||
`{{range .SI}}{{.}}{{end}}`},
|
|
||||||
{"range 1 var", "{{range $x := .SI}}{{.}}{{end}}", noError,
|
|
||||||
`{{range $x := .SI}}{{.}}{{end}}`},
|
|
||||||
{"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError,
|
|
||||||
`{{range $x, $y := .SI}}{{.}}{{end}}`},
|
|
||||||
{"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError,
|
|
||||||
`{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`},
|
|
||||||
{"template", "{{template `x`}}", noError,
|
|
||||||
`{{template "x"}}`},
|
|
||||||
{"template with arg", "{{template `x` .Y}}", noError,
|
|
||||||
`{{template "x" .Y}}`},
|
|
||||||
{"with", "{{with .X}}hello{{end}}", noError,
|
|
||||||
`{{with .X}}"hello"{{end}}`},
|
|
||||||
{"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
|
|
||||||
`{{with .X}}"hello"{{else}}"goodbye"{{end}}`},
|
|
||||||
{"elide newline", "{{true}}\\\n ", noError,
|
|
||||||
`{{true}}" "`},
|
|
||||||
// Errors.
|
|
||||||
{"unclosed action", "hello{{range", hasError, ""},
|
|
||||||
{"unmatched end", "{{end}}", hasError, ""},
|
|
||||||
{"missing end", "hello{{range .x}}", hasError, ""},
|
|
||||||
{"missing end after else", "hello{{range .x}}{{else}}", hasError, ""},
|
|
||||||
{"undefined function", "hello{{undefined}}", hasError, ""},
|
|
||||||
{"undefined variable", "{{$x}}", hasError, ""},
|
|
||||||
{"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""},
|
|
||||||
{"variable undefined in template", "{{template $v}}", hasError, ""},
|
|
||||||
{"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""},
|
|
||||||
{"template with field ref", "{{template .X}}", hasError, ""},
|
|
||||||
{"template with var", "{{template $v}}", hasError, ""},
|
|
||||||
{"invalid punctuation", "{{printf 3, 4}}", hasError, ""},
|
|
||||||
{"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""},
|
|
||||||
{"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""},
|
|
||||||
{"dot applied to parentheses", "{{printf (printf .).}}", hasError, ""},
|
|
||||||
{"adjacent args", "{{printf 3`x`}}", hasError, ""},
|
|
||||||
{"adjacent args with .", "{{printf `x`.}}", hasError, ""},
|
|
||||||
{"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""},
|
|
||||||
{"invalid newline elision", "{{true}}\\{{true}}", hasError, ""},
|
|
||||||
// Equals (and other chars) do not assignments make (yet).
|
|
||||||
{"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"},
|
|
||||||
{"bug0b", "{{$x = 1}}{{$x}}", hasError, ""},
|
|
||||||
{"bug0c", "{{$x ! 2}}{{$x}}", hasError, ""},
|
|
||||||
{"bug0d", "{{$x % 3}}{{$x}}", hasError, ""},
|
|
||||||
// Check the parse fails for := rather than comma.
|
|
||||||
{"bug0e", "{{range $x := $y := 3}}{{end}}", hasError, ""},
|
|
||||||
// Another bug: variable read must ignore following punctuation.
|
|
||||||
{"bug1a", "{{$x:=.}}{{$x!2}}", hasError, ""}, // ! is just illegal here.
|
|
||||||
{"bug1b", "{{$x:=.}}{{$x+2}}", hasError, ""}, // $x+2 should not parse as ($x) (+2).
|
|
||||||
{"bug1c", "{{$x:=.}}{{$x +2}}", noError, "{{$x := .}}{{$x +2}}"}, // It's OK with a space.
|
|
||||||
}
|
|
||||||
|
|
||||||
var builtins = map[string]interface{}{
|
|
||||||
"printf": fmt.Sprintf,
|
|
||||||
}
|
|
||||||
|
|
||||||
func testParse(doCopy bool, t *testing.T) {
|
|
||||||
textFormat = "%q"
|
|
||||||
defer func() { textFormat = "%s" }()
|
|
||||||
for _, test := range parseTests {
|
|
||||||
tmpl, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree), builtins)
|
|
||||||
switch {
|
|
||||||
case err == nil && !test.ok:
|
|
||||||
t.Errorf("%q: expected error; got none", test.name)
|
|
||||||
continue
|
|
||||||
case err != nil && test.ok:
|
|
||||||
t.Errorf("%q: unexpected error: %v", test.name, err)
|
|
||||||
continue
|
|
||||||
case err != nil && !test.ok:
|
|
||||||
// expected error, got one
|
|
||||||
if *debug {
|
|
||||||
fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var result string
|
|
||||||
if doCopy {
|
|
||||||
result = tmpl.Root.Copy().String()
|
|
||||||
} else {
|
|
||||||
result = tmpl.Root.String()
|
|
||||||
}
|
|
||||||
if result != test.result {
|
|
||||||
t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
|
||||||
testParse(false, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same as TestParse, but we copy the node first
|
|
||||||
func TestParseCopy(t *testing.T) {
|
|
||||||
testParse(true, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
type isEmptyTest struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
empty bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var isEmptyTests = []isEmptyTest{
|
|
||||||
{"empty", ``, true},
|
|
||||||
{"nonempty", `hello`, false},
|
|
||||||
{"spaces only", " \t\n \t\n", true},
|
|
||||||
{"definition", `{{define "x"}}something{{end}}`, true},
|
|
||||||
{"definitions and space", "{{define `x`}}something{{end}}\n\n{{define `y`}}something{{end}}\n\n", true},
|
|
||||||
{"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n", false},
|
|
||||||
{"definition and action", "{{define `x`}}something{{end}}{{if 3}}foo{{end}}", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsEmpty(t *testing.T) {
|
|
||||||
if !IsEmptyTree(nil) {
|
|
||||||
t.Errorf("nil tree is not empty")
|
|
||||||
}
|
|
||||||
for _, test := range isEmptyTests {
|
|
||||||
tree, err := New("root").Parse(test.input, "", "", make(map[string]*Tree), nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%q: unexpected error: %v", test.name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if empty := IsEmptyTree(tree.Root); empty != test.empty {
|
|
||||||
t.Errorf("%q: expected %t got %t", test.name, test.empty, empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorContextWithTreeCopy(t *testing.T) {
|
|
||||||
tree, err := New("root").Parse("{{if true}}{{end}}", "", "", make(map[string]*Tree), nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected tree parse failure: %v", err)
|
|
||||||
}
|
|
||||||
treeCopy := tree.Copy()
|
|
||||||
wantLocation, wantContext := tree.ErrorContext(tree.Root.Nodes[0])
|
|
||||||
gotLocation, gotContext := treeCopy.ErrorContext(treeCopy.Root.Nodes[0])
|
|
||||||
if wantLocation != gotLocation {
|
|
||||||
t.Errorf("wrong error location want %q got %q", wantLocation, gotLocation)
|
|
||||||
}
|
|
||||||
if wantContext != gotContext {
|
|
||||||
t.Errorf("wrong error location want %q got %q", wantContext, gotContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All failures, and the result is a string that must appear in the error message.
|
|
||||||
var errorTests = []parseTest{
|
|
||||||
// Check line numbers are accurate.
|
|
||||||
{"unclosed1",
|
|
||||||
"line1\n{{",
|
|
||||||
hasError, `unclosed1:2: unexpected unclosed action in command`},
|
|
||||||
{"unclosed2",
|
|
||||||
"line1\n{{define `x`}}line2\n{{",
|
|
||||||
hasError, `unclosed2:3: unexpected unclosed action in command`},
|
|
||||||
// Specific errors.
|
|
||||||
{"function",
|
|
||||||
"{{foo}}",
|
|
||||||
hasError, `function "foo" not defined`},
|
|
||||||
{"comment",
|
|
||||||
"{{/*}}",
|
|
||||||
hasError, `unclosed comment`},
|
|
||||||
{"lparen",
|
|
||||||
"{{.X (1 2 3}}",
|
|
||||||
hasError, `unclosed left paren`},
|
|
||||||
{"rparen",
|
|
||||||
"{{.X 1 2 3)}}",
|
|
||||||
hasError, `unexpected ")"`},
|
|
||||||
{"space",
|
|
||||||
"{{`x`3}}",
|
|
||||||
hasError, `missing space?`},
|
|
||||||
{"idchar",
|
|
||||||
"{{a#}}",
|
|
||||||
hasError, `'#'`},
|
|
||||||
{"charconst",
|
|
||||||
"{{'a}}",
|
|
||||||
hasError, `unterminated character constant`},
|
|
||||||
{"stringconst",
|
|
||||||
`{{"a}}`,
|
|
||||||
hasError, `unterminated quoted string`},
|
|
||||||
{"rawstringconst",
|
|
||||||
"{{`a}}",
|
|
||||||
hasError, `unterminated raw quoted string`},
|
|
||||||
{"number",
|
|
||||||
"{{0xi}}",
|
|
||||||
hasError, `number syntax`},
|
|
||||||
{"multidefine",
|
|
||||||
"{{define `a`}}a{{end}}{{define `a`}}b{{end}}",
|
|
||||||
hasError, `multiple definition of template`},
|
|
||||||
{"eof",
|
|
||||||
"{{range .X}}",
|
|
||||||
hasError, `unexpected EOF`},
|
|
||||||
{"variable",
|
|
||||||
// Declare $x so it's defined, to avoid that error, and then check we don't parse a declaration.
|
|
||||||
"{{$x := 23}}{{with $x.y := 3}}{{$x 23}}{{end}}",
|
|
||||||
hasError, `unexpected ":="`},
|
|
||||||
{"multidecl",
|
|
||||||
"{{$a,$b,$c := 23}}",
|
|
||||||
hasError, `too many declarations`},
|
|
||||||
{"undefvar",
|
|
||||||
"{{$a}}",
|
|
||||||
hasError, `undefined variable`},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrors(t *testing.T) {
|
|
||||||
for _, test := range errorTests {
|
|
||||||
_, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree))
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("%q: expected error", test.name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !strings.Contains(err.Error(), test.result) {
|
|
||||||
t.Errorf("%q: error %q does not contain %q", test.name, err, test.result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
11
vendor/github.com/alecthomas/units/README.md
generated
vendored
11
vendor/github.com/alecthomas/units/README.md
generated
vendored
|
@ -1,11 +0,0 @@
|
||||||
# Units - Helpful unit multipliers and functions for Go
|
|
||||||
|
|
||||||
The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package.
|
|
||||||
|
|
||||||
It allows for code like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
n, err := ParseBase2Bytes("1KB")
|
|
||||||
// n == 1024
|
|
||||||
n = units.Mebibyte * 512
|
|
||||||
```
|
|
49
vendor/github.com/alecthomas/units/bytes_test.go
generated
vendored
49
vendor/github.com/alecthomas/units/bytes_test.go
generated
vendored
|
@ -1,49 +0,0 @@
|
||||||
package units
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBase2BytesString(t *testing.T) {
|
|
||||||
assert.Equal(t, Base2Bytes(0).String(), "0B")
|
|
||||||
assert.Equal(t, Base2Bytes(1025).String(), "1KiB1B")
|
|
||||||
assert.Equal(t, Base2Bytes(1048577).String(), "1MiB1B")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseBase2Bytes(t *testing.T) {
|
|
||||||
n, err := ParseBase2Bytes("0B")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 0, int(n))
|
|
||||||
n, err = ParseBase2Bytes("1KB")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1024, int(n))
|
|
||||||
n, err = ParseBase2Bytes("1MB1KB25B")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1049625, int(n))
|
|
||||||
n, err = ParseBase2Bytes("1.5MB")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1572864, int(n))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMetricBytesString(t *testing.T) {
|
|
||||||
assert.Equal(t, MetricBytes(0).String(), "0B")
|
|
||||||
assert.Equal(t, MetricBytes(1001).String(), "1KB1B")
|
|
||||||
assert.Equal(t, MetricBytes(1001025).String(), "1MB1KB25B")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseMetricBytes(t *testing.T) {
|
|
||||||
n, err := ParseMetricBytes("0B")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 0, int(n))
|
|
||||||
n, err = ParseMetricBytes("1KB1B")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1001, int(n))
|
|
||||||
n, err = ParseMetricBytes("1MB1KB25B")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1001025, int(n))
|
|
||||||
n, err = ParseMetricBytes("1.5MB")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1500000, int(n))
|
|
||||||
}
|
|
15
vendor/github.com/andrew-d/go-termutil/README.md
generated
vendored
15
vendor/github.com/andrew-d/go-termutil/README.md
generated
vendored
|
@ -1,15 +0,0 @@
|
||||||
# go-termutil
|
|
||||||
|
|
||||||
This package exposes some very basic, useful functions:
|
|
||||||
|
|
||||||
Isatty(file *os.File) bool
|
|
||||||
|
|
||||||
This function will return whether or not the given file is a TTY, attempting to use native
|
|
||||||
operations when possible. It wil fall back to using the `isatty()` function from `unistd.h`
|
|
||||||
through cgo if on an unknown platform.
|
|
||||||
|
|
||||||
GetPass(prompt string, prompt_fd, input_fd uintptr) ([]byte, error)
|
|
||||||
|
|
||||||
This function will print the `prompt` string to the file identified by `prompt_fd`, prompt the user
|
|
||||||
for a password without echoing the password to the terminal, print a newline, and then return the
|
|
||||||
given password to the user. NOTE: not yet tested on anything except Linux & OS X.
|
|
4
vendor/gopkg.in/alecthomas/kingpin.v2/.travis.yml
generated
vendored
4
vendor/gopkg.in/alecthomas/kingpin.v2/.travis.yml
generated
vendored
|
@ -1,4 +0,0 @@
|
||||||
sudo: false
|
|
||||||
language: go
|
|
||||||
install: go get -t -v ./...
|
|
||||||
go: 1.2
|
|
674
vendor/gopkg.in/alecthomas/kingpin.v2/README.md
generated
vendored
674
vendor/gopkg.in/alecthomas/kingpin.v2/README.md
generated
vendored
|
@ -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 5
|
|
||||||
```
|
|
||||||
|
|
||||||
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
vendor/gopkg.in/alecthomas/kingpin.v2/app_test.go
generated
vendored
404
vendor/gopkg.in/alecthomas/kingpin.v2/app_test.go
generated
vendored
|
@ -1,404 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/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
vendor/gopkg.in/alecthomas/kingpin.v2/args_test.go
generated
vendored
84
vendor/gopkg.in/alecthomas/kingpin.v2/args_test.go
generated
vendored
|
@ -1,84 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/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
vendor/gopkg.in/alecthomas/kingpin.v2/cmd_test.go
generated
vendored
374
vendor/gopkg.in/alecthomas/kingpin.v2/cmd_test.go
generated
vendored
|
@ -1,374 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/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
vendor/gopkg.in/alecthomas/kingpin.v2/completions_test.go
generated
vendored
78
vendor/gopkg.in/alecthomas/kingpin.v2/completions_test.go
generated
vendored
|
@ -1,78 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/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
vendor/gopkg.in/alecthomas/kingpin.v2/examples_test.go
generated
vendored
46
vendor/gopkg.in/alecthomas/kingpin.v2/examples_test.go
generated
vendored
|
@ -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
vendor/gopkg.in/alecthomas/kingpin.v2/flags_test.go
generated
vendored
368
vendor/gopkg.in/alecthomas/kingpin.v2/flags_test.go
generated
vendored
|
@ -1,368 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/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
vendor/gopkg.in/alecthomas/kingpin.v2/parser_test.go
generated
vendored
122
vendor/gopkg.in/alecthomas/kingpin.v2/parser_test.go
generated
vendored
|
@ -1,122 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/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
vendor/gopkg.in/alecthomas/kingpin.v2/parsers_test.go
generated
vendored
98
vendor/gopkg.in/alecthomas/kingpin.v2/parsers_test.go
generated
vendored
|
@ -1,98 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/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
vendor/gopkg.in/alecthomas/kingpin.v2/usage_test.go
generated
vendored
65
vendor/gopkg.in/alecthomas/kingpin.v2/usage_test.go
generated
vendored
|
@ -1,65 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/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
vendor/gopkg.in/alecthomas/kingpin.v2/values.json
generated
vendored
25
vendor/gopkg.in/alecthomas/kingpin.v2/values.json
generated
vendored
|
@ -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
vendor/gopkg.in/alecthomas/kingpin.v2/values_test.go
generated
vendored
98
vendor/gopkg.in/alecthomas/kingpin.v2/values_test.go
generated
vendored
|
@ -1,98 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/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)
|
|
||||||
}
|
|
Loading…
Reference in a new issue