update vendor
This commit is contained in:
Generated
-68
@@ -1,68 +0,0 @@
|
|||||||
{
|
|
||||||
"ImportPath": "github.com/gostor/gotgt",
|
|
||||||
"GoVersion": "go1.6",
|
|
||||||
"GodepVersion": "v63",
|
|
||||||
"Packages": [
|
|
||||||
"./..."
|
|
||||||
],
|
|
||||||
"Deps": [
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/Microsoft/go-winio",
|
|
||||||
"Comment": "v0.1.0",
|
|
||||||
"Rev": "8f9387ea7efabb228a981b9c381142be7667967f"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/Sirupsen/logrus",
|
|
||||||
"Comment": "v0.10.0-19-gf3cfb45",
|
|
||||||
"Rev": "f3cfb454f4c209e6668c95216c4744b8fddb2356"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/coreos/go-systemd/activation",
|
|
||||||
"Comment": "v9",
|
|
||||||
"Rev": "6dc8b843c670f2027cc26b164935635840a40526"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/docker/go-connections/sockets",
|
|
||||||
"Comment": "v0.2.0-8-g990a1a1",
|
|
||||||
"Rev": "990a1a1a70b0da4c4cb70e117971a4f0babfbf1a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/gorilla/context",
|
|
||||||
"Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/gorilla/mux",
|
|
||||||
"Rev": "8096f47503459bcc74d1f4c487b7e6e42e5746b5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/inconshreveable/mousetrap",
|
|
||||||
"Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/opencontainers/runc/libcontainer/user",
|
|
||||||
"Comment": "v0.0.6-22-g47e3f83",
|
|
||||||
"Rev": "47e3f834d73e76bc2a6a585b48d2a93325b34979"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/spf13/cobra",
|
|
||||||
"Rev": "1238ba19d24b0b9ceee2094e1cb31947d45c3e86"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/spf13/pflag",
|
|
||||||
"Rev": "367864438f1b1a3c7db4da06a2f55b144e6784e0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/context",
|
|
||||||
"Rev": "d9558e5c97f85372afee28cf2b6059d7d3818919"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/proxy",
|
|
||||||
"Rev": "d9558e5c97f85372afee28cf2b6059d7d3818919"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/ceph/go-ceph",
|
|
||||||
"Rev": "bd5bc6d4cb3e3d3441f2ec4e9f89899178edfc71"
|
|
||||||
}
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Generated
-5
@@ -1,5 +0,0 @@
|
|||||||
This directory tree is generated automatically by godep.
|
|
||||||
|
|
||||||
Please do not edit.
|
|
||||||
|
|
||||||
See https://github.com/tools/godep for more information.
|
|
||||||
Generated
-2
@@ -1,2 +0,0 @@
|
|||||||
/pkg
|
|
||||||
/bin
|
|
||||||
-77
@@ -1,77 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEntryWithError(t *testing.T) {
|
|
||||||
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
ErrorKey = "error"
|
|
||||||
}()
|
|
||||||
|
|
||||||
err := fmt.Errorf("kaboom at layer %d", 4711)
|
|
||||||
|
|
||||||
assert.Equal(err, WithError(err).Data["error"])
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &bytes.Buffer{}
|
|
||||||
entry := NewEntry(logger)
|
|
||||||
|
|
||||||
assert.Equal(err, entry.WithError(err).Data["error"])
|
|
||||||
|
|
||||||
ErrorKey = "err"
|
|
||||||
|
|
||||||
assert.Equal(err, entry.WithError(err).Data["err"])
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEntryPanicln(t *testing.T) {
|
|
||||||
errBoom := fmt.Errorf("boom time")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
p := recover()
|
|
||||||
assert.NotNil(t, p)
|
|
||||||
|
|
||||||
switch pVal := p.(type) {
|
|
||||||
case *Entry:
|
|
||||||
assert.Equal(t, "kaboom", pVal.Message)
|
|
||||||
assert.Equal(t, errBoom, pVal.Data["err"])
|
|
||||||
default:
|
|
||||||
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &bytes.Buffer{}
|
|
||||||
entry := NewEntry(logger)
|
|
||||||
entry.WithField("err", errBoom).Panicln("kaboom")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEntryPanicf(t *testing.T) {
|
|
||||||
errBoom := fmt.Errorf("boom again")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
p := recover()
|
|
||||||
assert.NotNil(t, p)
|
|
||||||
|
|
||||||
switch pVal := p.(type) {
|
|
||||||
case *Entry:
|
|
||||||
assert.Equal(t, "kaboom true", pVal.Message)
|
|
||||||
assert.Equal(t, errBoom, pVal.Data["err"])
|
|
||||||
default:
|
|
||||||
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &bytes.Buffer{}
|
|
||||||
entry := NewEntry(logger)
|
|
||||||
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
|
|
||||||
}
|
|
||||||
-50
@@ -1,50 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logrus.New()
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.Formatter = new(logrus.JSONFormatter)
|
|
||||||
log.Formatter = new(logrus.TextFormatter) // default
|
|
||||||
log.Level = logrus.DebugLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
defer func() {
|
|
||||||
err := recover()
|
|
||||||
if err != nil {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"err": err,
|
|
||||||
"number": 100,
|
|
||||||
}).Fatal("The ice breaks!")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"number": 8,
|
|
||||||
}).Debug("Started observing beach")
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 122,
|
|
||||||
}).Warn("The group's number increased tremendously!")
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"temperature": -4,
|
|
||||||
}).Debug("Temperature changes")
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"animal": "orca",
|
|
||||||
"size": 9009,
|
|
||||||
}).Panic("It's over 9000!")
|
|
||||||
}
|
|
||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logrus.New()
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.Formatter = new(logrus.TextFormatter) // default
|
|
||||||
log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 122,
|
|
||||||
}).Warn("The group's number increased tremendously!")
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 100,
|
|
||||||
}).Fatal("The ice breaks!")
|
|
||||||
}
|
|
||||||
-98
@@ -1,98 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// smallFields is a small size data set for benchmarking
|
|
||||||
var smallFields = Fields{
|
|
||||||
"foo": "bar",
|
|
||||||
"baz": "qux",
|
|
||||||
"one": "two",
|
|
||||||
"three": "four",
|
|
||||||
}
|
|
||||||
|
|
||||||
// largeFields is a large size data set for benchmarking
|
|
||||||
var largeFields = Fields{
|
|
||||||
"foo": "bar",
|
|
||||||
"baz": "qux",
|
|
||||||
"one": "two",
|
|
||||||
"three": "four",
|
|
||||||
"five": "six",
|
|
||||||
"seven": "eight",
|
|
||||||
"nine": "ten",
|
|
||||||
"eleven": "twelve",
|
|
||||||
"thirteen": "fourteen",
|
|
||||||
"fifteen": "sixteen",
|
|
||||||
"seventeen": "eighteen",
|
|
||||||
"nineteen": "twenty",
|
|
||||||
"a": "b",
|
|
||||||
"c": "d",
|
|
||||||
"e": "f",
|
|
||||||
"g": "h",
|
|
||||||
"i": "j",
|
|
||||||
"k": "l",
|
|
||||||
"m": "n",
|
|
||||||
"o": "p",
|
|
||||||
"q": "r",
|
|
||||||
"s": "t",
|
|
||||||
"u": "v",
|
|
||||||
"w": "x",
|
|
||||||
"y": "z",
|
|
||||||
"this": "will",
|
|
||||||
"make": "thirty",
|
|
||||||
"entries": "yeah",
|
|
||||||
}
|
|
||||||
|
|
||||||
var errorFields = Fields{
|
|
||||||
"foo": fmt.Errorf("bar"),
|
|
||||||
"baz": fmt.Errorf("qux"),
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkErrorTextFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSmallTextFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLargeTextFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSmallColoredTextFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLargeColoredTextFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSmallJSONFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &JSONFormatter{}, smallFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLargeJSONFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &JSONFormatter{}, largeFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
|
|
||||||
entry := &Entry{
|
|
||||||
Time: time.Time{},
|
|
||||||
Level: InfoLevel,
|
|
||||||
Message: "message",
|
|
||||||
Data: fields,
|
|
||||||
}
|
|
||||||
var d []byte
|
|
||||||
var err error
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
d, err = formatter.Format(entry)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(d)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-63
@@ -1,63 +0,0 @@
|
|||||||
package logstash
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Formatter generates json in logstash format.
|
|
||||||
// Logstash site: http://logstash.net/
|
|
||||||
type LogstashFormatter struct {
|
|
||||||
Type string // if not empty use for logstash type field.
|
|
||||||
|
|
||||||
// TimestampFormat sets the format used for timestamps.
|
|
||||||
TimestampFormat string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
|
||||||
fields := make(logrus.Fields)
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
fields[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
fields["@version"] = 1
|
|
||||||
|
|
||||||
timeStampFormat := f.TimestampFormat
|
|
||||||
|
|
||||||
if timeStampFormat == "" {
|
|
||||||
timeStampFormat = logrus.DefaultTimestampFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
fields["@timestamp"] = entry.Time.Format(timeStampFormat)
|
|
||||||
|
|
||||||
// set message field
|
|
||||||
v, ok := entry.Data["message"]
|
|
||||||
if ok {
|
|
||||||
fields["fields.message"] = v
|
|
||||||
}
|
|
||||||
fields["message"] = entry.Message
|
|
||||||
|
|
||||||
// set level field
|
|
||||||
v, ok = entry.Data["level"]
|
|
||||||
if ok {
|
|
||||||
fields["fields.level"] = v
|
|
||||||
}
|
|
||||||
fields["level"] = entry.Level.String()
|
|
||||||
|
|
||||||
// set type field
|
|
||||||
if f.Type != "" {
|
|
||||||
v, ok = entry.Data["type"]
|
|
||||||
if ok {
|
|
||||||
fields["fields.type"] = v
|
|
||||||
}
|
|
||||||
fields["type"] = f.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
serialized, err := json.Marshal(fields)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
|
||||||
}
|
|
||||||
return append(serialized, '\n'), nil
|
|
||||||
}
|
|
||||||
Generated
Vendored
-52
@@ -1,52 +0,0 @@
|
|||||||
package logstash
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLogstashFormatter(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
lf := LogstashFormatter{Type: "abc"}
|
|
||||||
|
|
||||||
fields := logrus.Fields{
|
|
||||||
"message": "def",
|
|
||||||
"level": "ijk",
|
|
||||||
"type": "lmn",
|
|
||||||
"one": 1,
|
|
||||||
"pi": 3.14,
|
|
||||||
"bool": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := logrus.WithFields(fields)
|
|
||||||
entry.Message = "msg"
|
|
||||||
entry.Level = logrus.InfoLevel
|
|
||||||
|
|
||||||
b, _ := lf.Format(entry)
|
|
||||||
|
|
||||||
var data map[string]interface{}
|
|
||||||
dec := json.NewDecoder(bytes.NewReader(b))
|
|
||||||
dec.UseNumber()
|
|
||||||
dec.Decode(&data)
|
|
||||||
|
|
||||||
// base fields
|
|
||||||
assert.Equal(json.Number("1"), data["@version"])
|
|
||||||
assert.NotEmpty(data["@timestamp"])
|
|
||||||
assert.Equal("abc", data["type"])
|
|
||||||
assert.Equal("msg", data["message"])
|
|
||||||
assert.Equal("info", data["level"])
|
|
||||||
|
|
||||||
// substituted fields
|
|
||||||
assert.Equal("def", data["fields.message"])
|
|
||||||
assert.Equal("ijk", data["fields.level"])
|
|
||||||
assert.Equal("lmn", data["fields.type"])
|
|
||||||
|
|
||||||
// formats
|
|
||||||
assert.Equal(json.Number("1"), data["one"])
|
|
||||||
assert.Equal(json.Number("3.14"), data["pi"])
|
|
||||||
assert.Equal(true, data["bool"])
|
|
||||||
}
|
|
||||||
-122
@@ -1,122 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TestHook struct {
|
|
||||||
Fired bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *TestHook) Fire(entry *Entry) error {
|
|
||||||
hook.Fired = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *TestHook) Levels() []Level {
|
|
||||||
return []Level{
|
|
||||||
DebugLevel,
|
|
||||||
InfoLevel,
|
|
||||||
WarnLevel,
|
|
||||||
ErrorLevel,
|
|
||||||
FatalLevel,
|
|
||||||
PanicLevel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHookFires(t *testing.T) {
|
|
||||||
hook := new(TestHook)
|
|
||||||
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
assert.Equal(t, hook.Fired, false)
|
|
||||||
|
|
||||||
log.Print("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, hook.Fired, true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type ModifyHook struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *ModifyHook) Fire(entry *Entry) error {
|
|
||||||
entry.Data["wow"] = "whale"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *ModifyHook) Levels() []Level {
|
|
||||||
return []Level{
|
|
||||||
DebugLevel,
|
|
||||||
InfoLevel,
|
|
||||||
WarnLevel,
|
|
||||||
ErrorLevel,
|
|
||||||
FatalLevel,
|
|
||||||
PanicLevel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHookCanModifyEntry(t *testing.T) {
|
|
||||||
hook := new(ModifyHook)
|
|
||||||
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
log.WithField("wow", "elephant").Print("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["wow"], "whale")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanFireMultipleHooks(t *testing.T) {
|
|
||||||
hook1 := new(ModifyHook)
|
|
||||||
hook2 := new(TestHook)
|
|
||||||
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Hooks.Add(hook1)
|
|
||||||
log.Hooks.Add(hook2)
|
|
||||||
|
|
||||||
log.WithField("wow", "elephant").Print("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["wow"], "whale")
|
|
||||||
assert.Equal(t, hook2.Fired, true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type ErrorHook struct {
|
|
||||||
Fired bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *ErrorHook) Fire(entry *Entry) error {
|
|
||||||
hook.Fired = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *ErrorHook) Levels() []Level {
|
|
||||||
return []Level{
|
|
||||||
ErrorLevel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorHookShouldntFireOnInfo(t *testing.T) {
|
|
||||||
hook := new(ErrorHook)
|
|
||||||
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
log.Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, hook.Fired, false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorHookShouldFireOnError(t *testing.T) {
|
|
||||||
hook := new(ErrorHook)
|
|
||||||
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
log.Error("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, hook.Fired, true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
# Syslog Hooks for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"log/syslog"
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log := logrus.New()
|
|
||||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"log/syslog"
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log := logrus.New()
|
|
||||||
hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
-54
@@ -1,54 +0,0 @@
|
|||||||
// +build !windows,!nacl,!plan9
|
|
||||||
|
|
||||||
package logrus_syslog
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"log/syslog"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SyslogHook to send logs via syslog.
|
|
||||||
type SyslogHook struct {
|
|
||||||
Writer *syslog.Writer
|
|
||||||
SyslogNetwork string
|
|
||||||
SyslogRaddr string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a hook to be added to an instance of logger. This is called with
|
|
||||||
// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
|
|
||||||
// `if err == nil { log.Hooks.Add(hook) }`
|
|
||||||
func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
|
|
||||||
w, err := syslog.Dial(network, raddr, priority, tag)
|
|
||||||
return &SyslogHook{w, network, raddr}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
|
|
||||||
line, err := entry.String()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch entry.Level {
|
|
||||||
case logrus.PanicLevel:
|
|
||||||
return hook.Writer.Crit(line)
|
|
||||||
case logrus.FatalLevel:
|
|
||||||
return hook.Writer.Crit(line)
|
|
||||||
case logrus.ErrorLevel:
|
|
||||||
return hook.Writer.Err(line)
|
|
||||||
case logrus.WarnLevel:
|
|
||||||
return hook.Writer.Warning(line)
|
|
||||||
case logrus.InfoLevel:
|
|
||||||
return hook.Writer.Info(line)
|
|
||||||
case logrus.DebugLevel:
|
|
||||||
return hook.Writer.Debug(line)
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *SyslogHook) Levels() []logrus.Level {
|
|
||||||
return logrus.AllLevels
|
|
||||||
}
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
package logrus_syslog
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"log/syslog"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLocalhostAddAndPrint(t *testing.T) {
|
|
||||||
log := logrus.New()
|
|
||||||
hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unable to connect to local syslog.")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
|
|
||||||
for _, level := range hook.Levels() {
|
|
||||||
if len(log.Hooks[level]) != 1 {
|
|
||||||
t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Congratulations!")
|
|
||||||
}
|
|
||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// test.Hook is a hook designed for dealing with logs in test scenarios.
|
|
||||||
type Hook struct {
|
|
||||||
Entries []*logrus.Entry
|
|
||||||
}
|
|
||||||
|
|
||||||
// Installs a test hook for the global logger.
|
|
||||||
func NewGlobal() *Hook {
|
|
||||||
|
|
||||||
hook := new(Hook)
|
|
||||||
logrus.AddHook(hook)
|
|
||||||
|
|
||||||
return hook
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Installs a test hook for a given local logger.
|
|
||||||
func NewLocal(logger *logrus.Logger) *Hook {
|
|
||||||
|
|
||||||
hook := new(Hook)
|
|
||||||
logger.Hooks.Add(hook)
|
|
||||||
|
|
||||||
return hook
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a discarding logger and installs the test hook.
|
|
||||||
func NewNullLogger() (*logrus.Logger, *Hook) {
|
|
||||||
|
|
||||||
logger := logrus.New()
|
|
||||||
logger.Out = ioutil.Discard
|
|
||||||
|
|
||||||
return logger, NewLocal(logger)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Hook) Fire(e *logrus.Entry) error {
|
|
||||||
t.Entries = append(t.Entries, e)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Hook) Levels() []logrus.Level {
|
|
||||||
return logrus.AllLevels
|
|
||||||
}
|
|
||||||
|
|
||||||
// LastEntry returns the last entry that was logged or nil.
|
|
||||||
func (t *Hook) LastEntry() (l *logrus.Entry) {
|
|
||||||
|
|
||||||
if i := len(t.Entries) - 1; i < 0 {
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return t.Entries[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset removes all Entries from this test hook.
|
|
||||||
func (t *Hook) Reset() {
|
|
||||||
t.Entries = make([]*logrus.Entry, 0)
|
|
||||||
}
|
|
||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAllHooks(t *testing.T) {
|
|
||||||
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
logger, hook := NewNullLogger()
|
|
||||||
assert.Nil(hook.LastEntry())
|
|
||||||
assert.Equal(0, len(hook.Entries))
|
|
||||||
|
|
||||||
logger.Error("Hello error")
|
|
||||||
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
|
||||||
assert.Equal("Hello error", hook.LastEntry().Message)
|
|
||||||
assert.Equal(1, len(hook.Entries))
|
|
||||||
|
|
||||||
logger.Warn("Hello warning")
|
|
||||||
assert.Equal(logrus.WarnLevel, hook.LastEntry().Level)
|
|
||||||
assert.Equal("Hello warning", hook.LastEntry().Message)
|
|
||||||
assert.Equal(2, len(hook.Entries))
|
|
||||||
|
|
||||||
hook.Reset()
|
|
||||||
assert.Nil(hook.LastEntry())
|
|
||||||
assert.Equal(0, len(hook.Entries))
|
|
||||||
|
|
||||||
hook = NewGlobal()
|
|
||||||
|
|
||||||
logrus.Error("Hello error")
|
|
||||||
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
|
||||||
assert.Equal("Hello error", hook.LastEntry().Message)
|
|
||||||
assert.Equal(1, len(hook.Entries))
|
|
||||||
|
|
||||||
}
|
|
||||||
-120
@@ -1,120 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestErrorNotLost(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := make(map[string]interface{})
|
|
||||||
err = json.Unmarshal(b, &entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["error"] != "wild walrus" {
|
|
||||||
t.Fatal("Error field not set")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := make(map[string]interface{})
|
|
||||||
err = json.Unmarshal(b, &entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["omg"] != "wild walrus" {
|
|
||||||
t.Fatal("Error field not set")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFieldClashWithTime(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("time", "right now!"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := make(map[string]interface{})
|
|
||||||
err = json.Unmarshal(b, &entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["fields.time"] != "right now!" {
|
|
||||||
t.Fatal("fields.time not set to original time field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["time"] != "0001-01-01T00:00:00Z" {
|
|
||||||
t.Fatal("time field not set to current time, was: ", entry["time"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFieldClashWithMsg(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("msg", "something"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := make(map[string]interface{})
|
|
||||||
err = json.Unmarshal(b, &entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["fields.msg"] != "something" {
|
|
||||||
t.Fatal("fields.msg not set to original msg field")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFieldClashWithLevel(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("level", "something"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := make(map[string]interface{})
|
|
||||||
err = json.Unmarshal(b, &entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["fields.level"] != "something" {
|
|
||||||
t.Fatal("fields.level not set to original level field")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestJSONEntryEndsWithNewline(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("level", "something"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b[len(b)-1] != '\n' {
|
|
||||||
t.Fatal("Expected JSON log entry to end with a newline")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-361
@@ -1,361 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
var fields Fields
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &buffer
|
|
||||||
logger.Formatter = new(JSONFormatter)
|
|
||||||
|
|
||||||
log(logger)
|
|
||||||
|
|
||||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
assertions(fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &buffer
|
|
||||||
logger.Formatter = &TextFormatter{
|
|
||||||
DisableColors: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
log(logger)
|
|
||||||
|
|
||||||
fields := make(map[string]string)
|
|
||||||
for _, kv := range strings.Split(buffer.String(), " ") {
|
|
||||||
if !strings.Contains(kv, "=") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
kvArr := strings.Split(kv, "=")
|
|
||||||
key := strings.TrimSpace(kvArr[0])
|
|
||||||
val := kvArr[1]
|
|
||||||
if kvArr[1][0] == '"' {
|
|
||||||
var err error
|
|
||||||
val, err = strconv.Unquote(val)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
fields[key] = val
|
|
||||||
}
|
|
||||||
assertions(fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrint(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Print("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test")
|
|
||||||
assert.Equal(t, fields["level"], "info")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfo(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test")
|
|
||||||
assert.Equal(t, fields["level"], "info")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWarn(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Warn("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test")
|
|
||||||
assert.Equal(t, fields["level"], "warning")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Infoln("test", "test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test test")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Infoln("test", 10)
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test 10")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Infoln(10, 10)
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "10 10")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Infoln(10, 10)
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "10 10")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Info("test", 10)
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test10")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Info("test", "test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "testtest")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithFieldsShouldAllowAssignments(t *testing.T) {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
var fields Fields
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &buffer
|
|
||||||
logger.Formatter = new(JSONFormatter)
|
|
||||||
|
|
||||||
localLog := logger.WithFields(Fields{
|
|
||||||
"key1": "value1",
|
|
||||||
})
|
|
||||||
|
|
||||||
localLog.WithField("key2", "value2").Info("test")
|
|
||||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "value2", fields["key2"])
|
|
||||||
assert.Equal(t, "value1", fields["key1"])
|
|
||||||
|
|
||||||
buffer = bytes.Buffer{}
|
|
||||||
fields = Fields{}
|
|
||||||
localLog.Info("test")
|
|
||||||
err = json.Unmarshal(buffer.Bytes(), &fields)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
_, ok := fields["key2"]
|
|
||||||
assert.Equal(t, false, ok)
|
|
||||||
assert.Equal(t, "value1", fields["key1"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.WithField("msg", "hello").Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.WithField("msg", "hello").Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test")
|
|
||||||
assert.Equal(t, fields["fields.msg"], "hello")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.WithField("time", "hello").Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["fields.time"], "hello")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.WithField("level", 1).Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["level"], "info")
|
|
||||||
assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
|
|
||||||
LogAndAssertText(t, func(log *Logger) {
|
|
||||||
ll := log.WithField("herp", "derp")
|
|
||||||
ll.Info("hello")
|
|
||||||
ll.Info("bye")
|
|
||||||
}, func(fields map[string]string) {
|
|
||||||
for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
|
|
||||||
if _, ok := fields[fieldName]; ok {
|
|
||||||
t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
|
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
var fields Fields
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &buffer
|
|
||||||
logger.Formatter = new(JSONFormatter)
|
|
||||||
|
|
||||||
llog := logger.WithField("context", "eating raw fish")
|
|
||||||
|
|
||||||
llog.Info("looks delicious")
|
|
||||||
|
|
||||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
|
||||||
assert.NoError(t, err, "should have decoded first message")
|
|
||||||
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
|
|
||||||
assert.Equal(t, fields["msg"], "looks delicious")
|
|
||||||
assert.Equal(t, fields["context"], "eating raw fish")
|
|
||||||
|
|
||||||
buffer.Reset()
|
|
||||||
|
|
||||||
llog.Warn("omg it is!")
|
|
||||||
|
|
||||||
err = json.Unmarshal(buffer.Bytes(), &fields)
|
|
||||||
assert.NoError(t, err, "should have decoded second message")
|
|
||||||
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
|
|
||||||
assert.Equal(t, fields["msg"], "omg it is!")
|
|
||||||
assert.Equal(t, fields["context"], "eating raw fish")
|
|
||||||
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConvertLevelToString(t *testing.T) {
|
|
||||||
assert.Equal(t, "debug", DebugLevel.String())
|
|
||||||
assert.Equal(t, "info", InfoLevel.String())
|
|
||||||
assert.Equal(t, "warning", WarnLevel.String())
|
|
||||||
assert.Equal(t, "error", ErrorLevel.String())
|
|
||||||
assert.Equal(t, "fatal", FatalLevel.String())
|
|
||||||
assert.Equal(t, "panic", PanicLevel.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseLevel(t *testing.T) {
|
|
||||||
l, err := ParseLevel("panic")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, PanicLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("PANIC")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, PanicLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("fatal")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, FatalLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("FATAL")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, FatalLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("error")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, ErrorLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("ERROR")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, ErrorLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("warn")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, WarnLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("WARN")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, WarnLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("warning")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, WarnLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("WARNING")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, WarnLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("info")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, InfoLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("INFO")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, InfoLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("debug")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, DebugLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("DEBUG")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, DebugLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("invalid")
|
|
||||||
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetSetLevelRace(t *testing.T) {
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(i int) {
|
|
||||||
defer wg.Done()
|
|
||||||
if i%2 == 0 {
|
|
||||||
SetLevel(InfoLevel)
|
|
||||||
} else {
|
|
||||||
GetLevel()
|
|
||||||
}
|
|
||||||
}(i)
|
|
||||||
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLoggingRace(t *testing.T) {
|
|
||||||
logger := New()
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(100)
|
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
go func() {
|
|
||||||
logger.Info("info")
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile test
|
|
||||||
func TestLogrusInterface(t *testing.T) {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
fn := func(l FieldLogger) {
|
|
||||||
b := l.WithField("key", "value")
|
|
||||||
b.Debug("Test")
|
|
||||||
}
|
|
||||||
// test logger
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &buffer
|
|
||||||
fn(logger)
|
|
||||||
|
|
||||||
// test Entry
|
|
||||||
e := logger.WithField("another", "value")
|
|
||||||
fn(e)
|
|
||||||
}
|
|
||||||
-61
@@ -1,61 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestQuoting(t *testing.T) {
|
|
||||||
tf := &TextFormatter{DisableColors: true}
|
|
||||||
|
|
||||||
checkQuoting := func(q bool, value interface{}) {
|
|
||||||
b, _ := tf.Format(WithField("test", value))
|
|
||||||
idx := bytes.Index(b, ([]byte)("test="))
|
|
||||||
cont := bytes.Contains(b[idx+5:], []byte{'"'})
|
|
||||||
if cont != q {
|
|
||||||
if q {
|
|
||||||
t.Errorf("quoting expected for: %#v", value)
|
|
||||||
} else {
|
|
||||||
t.Errorf("quoting not expected for: %#v", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkQuoting(false, "abcd")
|
|
||||||
checkQuoting(false, "v1.0")
|
|
||||||
checkQuoting(false, "1234567890")
|
|
||||||
checkQuoting(true, "/foobar")
|
|
||||||
checkQuoting(true, "x y")
|
|
||||||
checkQuoting(true, "x,y")
|
|
||||||
checkQuoting(false, errors.New("invalid"))
|
|
||||||
checkQuoting(true, errors.New("invalid argument"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimestampFormat(t *testing.T) {
|
|
||||||
checkTimeStr := func(format string) {
|
|
||||||
customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}
|
|
||||||
customStr, _ := customFormatter.Format(WithField("test", "test"))
|
|
||||||
timeStart := bytes.Index(customStr, ([]byte)("time="))
|
|
||||||
timeEnd := bytes.Index(customStr, ([]byte)("level="))
|
|
||||||
timeStr := customStr[timeStart+5 : timeEnd-1]
|
|
||||||
if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' {
|
|
||||||
timeStr = timeStr[1 : len(timeStr)-1]
|
|
||||||
}
|
|
||||||
if format == "" {
|
|
||||||
format = time.RFC3339
|
|
||||||
}
|
|
||||||
_, e := time.Parse(format, (string)(timeStr))
|
|
||||||
if e != nil {
|
|
||||||
t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkTimeStr("2006-01-02T15:04:05.000000000Z07:00")
|
|
||||||
checkTimeStr("Mon Jan _2 15:04:05 2006")
|
|
||||||
checkTimeStr("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO add tests for sorting etc., this requires a parser for the text
|
|
||||||
// formatter output.
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
dist: trusty
|
|
||||||
sudo: required
|
|
||||||
|
|
||||||
language: go
|
|
||||||
|
|
||||||
branches:
|
|
||||||
except:
|
|
||||||
- gh-pages
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- env: CEPH_RELEASE=jewel
|
|
||||||
- env: CEPH_RELEASE=kraken
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- sudo apt-get update
|
|
||||||
- ci/before_install.sh
|
|
||||||
- bash ci/micro-osd.sh /tmp/micro-ceph
|
|
||||||
- export CEPH_CONF=/tmp/micro-ceph/ceph.conf
|
|
||||||
- ceph status
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go get -t -v ./...
|
|
||||||
- go list ./...
|
|
||||||
- go test -v $(go list ./... | grep -v cephfs)
|
|
||||||
- go fmt ./...
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
FROM golang:1.7.1
|
|
||||||
MAINTAINER Abhishek Lekshmanan "abhishek.lekshmanan@gmail.com"
|
|
||||||
|
|
||||||
ENV CEPH_VERSION jewel
|
|
||||||
|
|
||||||
RUN echo deb http://download.ceph.com/debian-$CEPH_VERSION/ jessie main | tee /etc/apt/sources.list.d/ceph-$CEPH_VERSION.list
|
|
||||||
|
|
||||||
# Running wget with no certificate checks, alternatively ssl-cert package should be installed
|
|
||||||
RUN wget --no-check-certificate -q -O- 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' | apt-key add - \
|
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y --no-install-recommends \
|
|
||||||
ceph \
|
|
||||||
ceph-mds \
|
|
||||||
librados-dev \
|
|
||||||
librbd-dev \
|
|
||||||
libcephfs-dev \
|
|
||||||
uuid-runtime \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
|
||||||
&& apt-get clean
|
|
||||||
|
|
||||||
VOLUME /go/src/github.com/ceph/go-ceph
|
|
||||||
|
|
||||||
COPY ./ci/entrypoint.sh /tmp/entrypoint.sh
|
|
||||||
|
|
||||||
ENTRYPOINT ["/tmp/entrypoint.sh", "/tmp/micro-ceph"]
|
|
||||||
|
|
||||||
-107
@@ -1,107 +0,0 @@
|
|||||||
# go-ceph - Go bindings for Ceph APIs
|
|
||||||
|
|
||||||
[](https://travis-ci.org/ceph/go-ceph) [](https://godoc.org/github.com/ceph/go-ceph) [](https://raw.githubusercontent.com/ceph/go-ceph/master/LICENSE)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
go get github.com/ceph/go-ceph
|
|
||||||
|
|
||||||
The native RADOS library and development headers are expected to be installed.
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
Detailed documentation is available at
|
|
||||||
<http://godoc.org/github.com/ceph/go-ceph>.
|
|
||||||
|
|
||||||
### Connecting to a cluster
|
|
||||||
|
|
||||||
Connect to a Ceph cluster using a configuration file located in the default
|
|
||||||
search paths.
|
|
||||||
|
|
||||||
```go
|
|
||||||
conn, _ := rados.NewConn()
|
|
||||||
conn.ReadDefaultConfigFile()
|
|
||||||
conn.Connect()
|
|
||||||
```
|
|
||||||
|
|
||||||
A connection can be shutdown by calling the `Shutdown` method on the
|
|
||||||
connection object (e.g. `conn.Shutdown()`). There are also other methods for
|
|
||||||
configuring the connection. Specific configuration options can be set:
|
|
||||||
|
|
||||||
```go
|
|
||||||
conn.SetConfigOption("log_file", "/dev/null")
|
|
||||||
```
|
|
||||||
|
|
||||||
and command line options can also be used using the `ParseCmdLineArgs` method.
|
|
||||||
|
|
||||||
```go
|
|
||||||
args := []string{ "--mon-host", "1.1.1.1" }
|
|
||||||
err := conn.ParseCmdLineArgs(args)
|
|
||||||
```
|
|
||||||
|
|
||||||
For other configuration options see the full documentation.
|
|
||||||
|
|
||||||
### Object I/O
|
|
||||||
|
|
||||||
Object in RADOS can be written to and read from with through an interface very
|
|
||||||
similar to a standard file I/O interface:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// open a pool handle
|
|
||||||
ioctx, err := conn.OpenIOContext("mypool")
|
|
||||||
|
|
||||||
// write some data
|
|
||||||
bytes_in := []byte("input data")
|
|
||||||
err = ioctx.Write("obj", bytes_in, 0)
|
|
||||||
|
|
||||||
// read the data back out
|
|
||||||
bytes_out := make([]byte, len(bytes_in))
|
|
||||||
n_out, err := ioctx.Read("obj", bytes_out, 0)
|
|
||||||
|
|
||||||
if bytes_in != bytes_out {
|
|
||||||
fmt.Println("Output is not input!")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pool maintenance
|
|
||||||
|
|
||||||
The list of pools in a cluster can be retreived using the `ListPools` method
|
|
||||||
on the connection object. On a new cluster the following code snippet:
|
|
||||||
|
|
||||||
```go
|
|
||||||
pools, _ := conn.ListPools()
|
|
||||||
fmt.Println(pools)
|
|
||||||
```
|
|
||||||
|
|
||||||
will produce the output `[data metadata rbd]`, along with any other pools that
|
|
||||||
might exist in your cluster. Pools can also be created and destroyed. The
|
|
||||||
following creates a new, empty pool with default settings.
|
|
||||||
|
|
||||||
```go
|
|
||||||
conn.MakePool("new_pool")
|
|
||||||
```
|
|
||||||
|
|
||||||
Deleting a pool is also easy. Call `DeletePool(name string)` on a connection object to
|
|
||||||
delete a pool with the given name. The following will delete the pool named
|
|
||||||
`new_pool` and remove all of the pool's data.
|
|
||||||
|
|
||||||
```go
|
|
||||||
conn.DeletePool("new_pool")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Contributions are welcome & greatly appreciated, every little bit helps. Make code changes via Github pull requests:
|
|
||||||
|
|
||||||
- Fork the repo and create a topic branch for every feature/fix. Avoid
|
|
||||||
making changes directly on master branch.
|
|
||||||
- All incoming features should be accompanied with tests.
|
|
||||||
- Make sure that you run `go fmt` before submitting a change
|
|
||||||
set. Alternatively the Makefile has a flag for this, so you can call
|
|
||||||
`make fmt` as well.
|
|
||||||
- The integration tests can be run in a docker container, for this run:
|
|
||||||
|
|
||||||
```
|
|
||||||
make test-docker
|
|
||||||
```
|
|
||||||
|
|
||||||
-89
@@ -1,89 +0,0 @@
|
|||||||
package cephfs
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo LDFLAGS: -lcephfs
|
|
||||||
#cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <cephfs/libcephfs.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import "fmt"
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
//
|
|
||||||
type CephError int
|
|
||||||
|
|
||||||
func (e CephError) Error() string {
|
|
||||||
return fmt.Sprintf("cephfs: ret=%d", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
type MountInfo struct {
|
|
||||||
mount *C.struct_ceph_mount_info
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateMount() (*MountInfo, error) {
|
|
||||||
mount := &MountInfo{}
|
|
||||||
ret := C.ceph_create(&mount.mount, nil)
|
|
||||||
if ret == 0 {
|
|
||||||
return mount, nil
|
|
||||||
} else {
|
|
||||||
return nil, CephError(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mount *MountInfo) ReadDefaultConfigFile() error {
|
|
||||||
ret := C.ceph_conf_read_file(mount.mount, nil)
|
|
||||||
if ret == 0 {
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return CephError(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mount *MountInfo) Mount() error {
|
|
||||||
ret := C.ceph_mount(mount.mount, nil)
|
|
||||||
if ret == 0 {
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return CephError(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mount *MountInfo) SyncFs() error {
|
|
||||||
ret := C.ceph_sync_fs(mount.mount)
|
|
||||||
if ret == 0 {
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return CephError(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mount *MountInfo) CurrentDir() string {
|
|
||||||
c_dir := C.ceph_getcwd(mount.mount)
|
|
||||||
return C.GoString(c_dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mount *MountInfo) ChangeDir(path string) error {
|
|
||||||
c_path := C.CString(path)
|
|
||||||
defer C.free(unsafe.Pointer(c_path))
|
|
||||||
|
|
||||||
ret := C.ceph_chdir(mount.mount, c_path)
|
|
||||||
if ret == 0 {
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return CephError(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mount *MountInfo) MakeDir(path string, mode uint32) error {
|
|
||||||
c_path := C.CString(path)
|
|
||||||
defer C.free(unsafe.Pointer(c_path))
|
|
||||||
|
|
||||||
ret := C.ceph_mkdir(mount.mount, c_path, C.mode_t(mode))
|
|
||||||
if ret == 0 {
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return CephError(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-66
@@ -1,66 +0,0 @@
|
|||||||
package cephfs_test
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
import "github.com/ceph/go-ceph/cephfs"
|
|
||||||
import "github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
func TestCreateMount(t *testing.T) {
|
|
||||||
mount, err := cephfs.CreateMount()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, mount)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMountRoot(t *testing.T) {
|
|
||||||
mount, err := cephfs.CreateMount()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, mount)
|
|
||||||
|
|
||||||
err = mount.ReadDefaultConfigFile()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = mount.Mount()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSyncFs(t *testing.T) {
|
|
||||||
mount, err := cephfs.CreateMount()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, mount)
|
|
||||||
|
|
||||||
err = mount.ReadDefaultConfigFile()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = mount.Mount()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = mount.SyncFs()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChangeDir(t *testing.T) {
|
|
||||||
mount, err := cephfs.CreateMount()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, mount)
|
|
||||||
|
|
||||||
err = mount.ReadDefaultConfigFile()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = mount.Mount()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
dir1 := mount.CurrentDir()
|
|
||||||
assert.NotNil(t, dir1)
|
|
||||||
|
|
||||||
err = mount.MakeDir("/asdf", 0755)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = mount.ChangeDir("/asdf")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
dir2 := mount.CurrentDir()
|
|
||||||
assert.NotNil(t, dir2)
|
|
||||||
|
|
||||||
assert.NotEqual(t, dir1, dir2)
|
|
||||||
assert.Equal(t, dir1, "/")
|
|
||||||
assert.Equal(t, dir2, "/asdf")
|
|
||||||
}
|
|
||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
set -x
|
|
||||||
|
|
||||||
sudo apt-get install -y python-virtualenv
|
|
||||||
|
|
||||||
# ceph-deploy and ceph
|
|
||||||
|
|
||||||
WORKDIR=$HOME/workdir
|
|
||||||
mkdir $WORKDIR
|
|
||||||
pushd $WORKDIR
|
|
||||||
|
|
||||||
ssh-keygen -f $HOME/.ssh/id_rsa -t rsa -N ''
|
|
||||||
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
|
|
||||||
chmod 600 ~/.ssh/authorized_keys
|
|
||||||
|
|
||||||
git clone git://github.com/ceph/ceph-deploy
|
|
||||||
pushd ceph-deploy
|
|
||||||
./bootstrap
|
|
||||||
./ceph-deploy install --release ${CEPH_RELEASE} `hostname`
|
|
||||||
./ceph-deploy pkg --install librados-dev `hostname`
|
|
||||||
./ceph-deploy pkg --install librbd-dev `hostname`
|
|
||||||
./ceph-deploy pkg --install libcephfs-dev `hostname`
|
|
||||||
popd # ceph-deploy
|
|
||||||
|
|
||||||
popd # workdir
|
|
||||||
-114
@@ -1,114 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Copyright (C) 2013,2014 Loic Dachary <loic@dachary.org>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
set -e
|
|
||||||
set -u
|
|
||||||
DIR=$1
|
|
||||||
|
|
||||||
#if ! dpkg -l ceph ; then
|
|
||||||
# wget -q -O- 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' | sudo apt-key add -
|
|
||||||
# echo deb http://ceph.com/debian-dumpling/ $(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/ceph.list
|
|
||||||
# sudo apt-get update
|
|
||||||
# sudo apt-get --yes install ceph ceph-common
|
|
||||||
#fi
|
|
||||||
|
|
||||||
# get rid of process and directories leftovers
|
|
||||||
pkill ceph-mon || true
|
|
||||||
pkill ceph-osd || true
|
|
||||||
rm -fr $DIR
|
|
||||||
|
|
||||||
# cluster wide parameters
|
|
||||||
mkdir -p ${DIR}/log
|
|
||||||
cat >> $DIR/ceph.conf <<EOF
|
|
||||||
[global]
|
|
||||||
fsid = $(uuidgen)
|
|
||||||
osd crush chooseleaf type = 0
|
|
||||||
run dir = ${DIR}/run
|
|
||||||
auth cluster required = none
|
|
||||||
auth service required = none
|
|
||||||
auth client required = none
|
|
||||||
osd pool default size = 1
|
|
||||||
EOF
|
|
||||||
export CEPH_ARGS="--conf ${DIR}/ceph.conf"
|
|
||||||
|
|
||||||
# single monitor
|
|
||||||
MON_DATA=${DIR}/mon
|
|
||||||
mkdir -p $MON_DATA
|
|
||||||
|
|
||||||
cat >> $DIR/ceph.conf <<EOF
|
|
||||||
[mon.0]
|
|
||||||
log file = ${DIR}/log/mon.log
|
|
||||||
chdir = ""
|
|
||||||
mon cluster log file = ${DIR}/log/mon-cluster.log
|
|
||||||
mon data = ${MON_DATA}
|
|
||||||
mon addr = 127.0.0.1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
ceph-mon --id 0 --mkfs --keyring /dev/null
|
|
||||||
touch ${MON_DATA}/keyring
|
|
||||||
ceph-mon --id 0
|
|
||||||
|
|
||||||
# single osd
|
|
||||||
OSD_DATA=${DIR}/osd
|
|
||||||
mkdir ${OSD_DATA}
|
|
||||||
|
|
||||||
cat >> $DIR/ceph.conf <<EOF
|
|
||||||
[osd.0]
|
|
||||||
log file = ${DIR}/log/osd.log
|
|
||||||
chdir = ""
|
|
||||||
osd data = ${OSD_DATA}
|
|
||||||
osd journal = ${OSD_DATA}.journal
|
|
||||||
osd journal size = 100
|
|
||||||
osd objectstore = memstore
|
|
||||||
EOF
|
|
||||||
|
|
||||||
OSD_ID=$(ceph osd create)
|
|
||||||
ceph osd crush add osd.${OSD_ID} 1 root=default host=localhost
|
|
||||||
ceph-osd --id ${OSD_ID} --mkjournal --mkfs
|
|
||||||
ceph-osd --id ${OSD_ID}
|
|
||||||
|
|
||||||
# single mds
|
|
||||||
MDS_DATA=${DIR}/mds.a
|
|
||||||
mkdir ${MDS_DATA}
|
|
||||||
|
|
||||||
cat >> $DIR/ceph.conf <<EOF
|
|
||||||
[mds.a]
|
|
||||||
mds data = ${MDS_DATA}
|
|
||||||
mds log max segments = 2
|
|
||||||
mds cache size = 10000
|
|
||||||
host = localhost
|
|
||||||
EOF
|
|
||||||
|
|
||||||
ceph-authtool --create-keyring --gen-key --name=mds.a ${MDS_DATA}/keyring
|
|
||||||
ceph -i ${MDS_DATA}/keyring auth add mds.a mon 'allow profile mds' osd 'allow *' mds 'allow'
|
|
||||||
ceph osd pool create cephfs_data 8
|
|
||||||
ceph osd pool create cephfs_metadata 8
|
|
||||||
ceph fs new cephfs cephfs_metadata cephfs_data
|
|
||||||
ceph-mds -i a
|
|
||||||
|
|
||||||
# check that it works
|
|
||||||
rados --pool rbd put group /etc/group
|
|
||||||
rados --pool rbd get group ${DIR}/group
|
|
||||||
diff /etc/group ${DIR}/group
|
|
||||||
ceph osd tree
|
|
||||||
|
|
||||||
export CEPH_CONF="${DIR}/ceph.conf"
|
|
||||||
|
|
||||||
go get github.com/stretchr/testify/assert
|
|
||||||
cd /go/src/github.com/ceph/go-ceph
|
|
||||||
|
|
||||||
exec go test -v ./...
|
|
||||||
-119
@@ -1,119 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2013,2014 Loic Dachary <loic@dachary.org>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
set -e
|
|
||||||
set -u
|
|
||||||
|
|
||||||
DIR=$1
|
|
||||||
|
|
||||||
#if ! dpkg -l ceph ; then
|
|
||||||
# wget -q -O- 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' | sudo apt-key add -
|
|
||||||
# echo deb http://ceph.com/debian-dumpling/ $(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/ceph.list
|
|
||||||
# sudo apt-get update
|
|
||||||
# sudo apt-get --yes install ceph ceph-common
|
|
||||||
#fi
|
|
||||||
|
|
||||||
# get rid of process and directories leftovers
|
|
||||||
pkill ceph-mon || true
|
|
||||||
pkill ceph-osd || true
|
|
||||||
rm -fr $DIR
|
|
||||||
|
|
||||||
# cluster wide parameters
|
|
||||||
mkdir -p ${DIR}/log
|
|
||||||
cat >> $DIR/ceph.conf <<EOF
|
|
||||||
[global]
|
|
||||||
fsid = $(uuidgen)
|
|
||||||
osd crush chooseleaf type = 0
|
|
||||||
run dir = ${DIR}/run
|
|
||||||
auth cluster required = none
|
|
||||||
auth service required = none
|
|
||||||
auth client required = none
|
|
||||||
osd pool default size = 1
|
|
||||||
mon allow pool delete = true
|
|
||||||
EOF
|
|
||||||
export CEPH_ARGS="--conf ${DIR}/ceph.conf"
|
|
||||||
|
|
||||||
# single monitor
|
|
||||||
MON_DATA=${DIR}/mon
|
|
||||||
mkdir -p $MON_DATA
|
|
||||||
|
|
||||||
cat >> $DIR/ceph.conf <<EOF
|
|
||||||
[mon.0]
|
|
||||||
log file = ${DIR}/log/mon.log
|
|
||||||
chdir = ""
|
|
||||||
mon cluster log file = ${DIR}/log/mon-cluster.log
|
|
||||||
mon data = ${MON_DATA}
|
|
||||||
mon addr = 127.0.0.1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
ceph-mon --id 0 --mkfs --keyring /dev/null
|
|
||||||
touch ${MON_DATA}/keyring
|
|
||||||
ceph-mon --id 0
|
|
||||||
|
|
||||||
# single osd
|
|
||||||
OSD_DATA=${DIR}/osd
|
|
||||||
mkdir ${OSD_DATA}
|
|
||||||
|
|
||||||
cat >> $DIR/ceph.conf <<EOF
|
|
||||||
[osd.0]
|
|
||||||
log file = ${DIR}/log/osd.log
|
|
||||||
chdir = ""
|
|
||||||
osd data = ${OSD_DATA}
|
|
||||||
osd journal = ${OSD_DATA}.journal
|
|
||||||
osd journal size = 100
|
|
||||||
osd objectstore = memstore
|
|
||||||
EOF
|
|
||||||
|
|
||||||
OSD_ID=$(ceph osd create)
|
|
||||||
ceph osd crush add osd.${OSD_ID} 1 root=default host=localhost
|
|
||||||
ceph-osd --id ${OSD_ID} --mkjournal --mkfs
|
|
||||||
ceph-osd --id ${OSD_ID}
|
|
||||||
|
|
||||||
# single mds
|
|
||||||
MDS_DATA=${DIR}/mds.a
|
|
||||||
mkdir ${MDS_DATA}
|
|
||||||
|
|
||||||
cat >> $DIR/ceph.conf <<EOF
|
|
||||||
[mds.a]
|
|
||||||
mds data = ${MDS_DATA}
|
|
||||||
mds log max segments = 2
|
|
||||||
mds cache size = 10000
|
|
||||||
host = localhost
|
|
||||||
EOF
|
|
||||||
|
|
||||||
ceph-authtool --create-keyring --gen-key --name=mds.a ${MDS_DATA}/keyring
|
|
||||||
ceph -i ${MDS_DATA}/keyring auth add mds.a mon 'allow profile mds' osd 'allow *' mds 'allow'
|
|
||||||
ceph osd pool create cephfs_data 8
|
|
||||||
ceph osd pool create cephfs_metadata 8
|
|
||||||
ceph fs new cephfs cephfs_metadata cephfs_data
|
|
||||||
ceph-mds -i a
|
|
||||||
|
|
||||||
export CEPH_CONF="${DIR}/ceph.conf"
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
if ceph status | tee /dev/tty | grep -q HEALTH_OK; then
|
|
||||||
if ! ceph status | grep -q creating &> /dev/null; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
# check that it works
|
|
||||||
rados --pool rbd put group /etc/group
|
|
||||||
rados --pool rbd get group ${DIR}/group
|
|
||||||
diff /etc/group ${DIR}/group
|
|
||||||
ceph osd tree
|
|
||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
/*
|
|
||||||
Set of wrappers around Ceph APIs.
|
|
||||||
*/
|
|
||||||
package rados
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "github.com/ceph/go-ceph/rados"
|
|
||||||
_ "github.com/ceph/go-ceph/rbd"
|
|
||||||
)
|
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
package rados
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestImports(t *testing.T) {
|
|
||||||
if assert.Equal(t, 1, 1) != true {
|
|
||||||
t.Error("Something is wrong.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1022
File diff suppressed because it is too large
Load Diff
-277
@@ -1,277 +0,0 @@
|
|||||||
package rbd_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/ceph/go-ceph/rados"
|
|
||||||
"github.com/ceph/go-ceph/rbd"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"os/exec"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetUUID() string {
|
|
||||||
out, _ := exec.Command("uuidgen").Output()
|
|
||||||
return string(out[:36])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVersion(t *testing.T) {
|
|
||||||
var major, minor, patch = rbd.Version()
|
|
||||||
assert.False(t, major < 0 || major > 1000, "invalid major")
|
|
||||||
assert.False(t, minor < 0 || minor > 1000, "invalid minor")
|
|
||||||
assert.False(t, patch < 0 || patch > 1000, "invalid patch")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetImageNames(t *testing.T) {
|
|
||||||
conn, _ := rados.NewConn()
|
|
||||||
conn.ReadDefaultConfigFile()
|
|
||||||
conn.Connect()
|
|
||||||
|
|
||||||
poolname := GetUUID()
|
|
||||||
err := conn.MakePool(poolname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
ioctx, err := conn.OpenIOContext(poolname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
createdList := []string{}
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
name := GetUUID()
|
|
||||||
_, err := rbd.Create(ioctx, name, 1<<22, 22)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
createdList = append(createdList, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
imageNames, err := rbd.GetImageNames(ioctx)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
sort.Strings(createdList)
|
|
||||||
sort.Strings(imageNames)
|
|
||||||
assert.Equal(t, createdList, imageNames)
|
|
||||||
|
|
||||||
for _, name := range createdList {
|
|
||||||
img := rbd.GetImage(ioctx, name)
|
|
||||||
err := img.Remove()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ioctx.Destroy()
|
|
||||||
conn.DeletePool(poolname)
|
|
||||||
conn.Shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIOReaderWriter(t *testing.T) {
|
|
||||||
conn, _ := rados.NewConn()
|
|
||||||
conn.ReadDefaultConfigFile()
|
|
||||||
conn.Connect()
|
|
||||||
|
|
||||||
poolname := GetUUID()
|
|
||||||
err := conn.MakePool(poolname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
ioctx, err := conn.OpenIOContext(poolname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
name := GetUUID()
|
|
||||||
img, err := rbd.Create(ioctx, name, 1<<22, 22)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = img.Open()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
stats, err := img.Stat()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
encoder := json.NewEncoder(img)
|
|
||||||
encoder.Encode(stats)
|
|
||||||
|
|
||||||
err = img.Flush()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = img.Seek(0, 0)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
var stats2 *rbd.ImageInfo
|
|
||||||
decoder := json.NewDecoder(img)
|
|
||||||
decoder.Decode(&stats2)
|
|
||||||
|
|
||||||
assert.Equal(t, &stats, &stats2)
|
|
||||||
|
|
||||||
_, err = img.Seek(0, 0)
|
|
||||||
bytes_in := []byte("input data")
|
|
||||||
_, err = img.Write(bytes_in)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = img.Seek(0, 0)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
bytes_out := make([]byte, len(bytes_in))
|
|
||||||
n_out, err := img.Read(bytes_out)
|
|
||||||
|
|
||||||
assert.Equal(t, n_out, len(bytes_in))
|
|
||||||
assert.Equal(t, bytes_in, bytes_out)
|
|
||||||
|
|
||||||
err = img.Close()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
img.Remove()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
ioctx.Destroy()
|
|
||||||
conn.DeletePool(poolname)
|
|
||||||
conn.Shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateSnapshot(t *testing.T) {
|
|
||||||
conn, _ := rados.NewConn()
|
|
||||||
conn.ReadDefaultConfigFile()
|
|
||||||
conn.Connect()
|
|
||||||
|
|
||||||
poolname := GetUUID()
|
|
||||||
err := conn.MakePool(poolname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
ioctx, err := conn.OpenIOContext(poolname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
name := GetUUID()
|
|
||||||
img, err := rbd.Create(ioctx, name, 1<<22, 22)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = img.Open()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
snapshot, err := img.CreateSnapshot("mysnap")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = img.Close()
|
|
||||||
err = img.Open("mysnap")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
snapshot.Remove()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = img.Close()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
img.Remove()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
ioctx.Destroy()
|
|
||||||
conn.DeletePool(poolname)
|
|
||||||
conn.Shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParentInfo(t *testing.T) {
|
|
||||||
conn, _ := rados.NewConn()
|
|
||||||
conn.ReadDefaultConfigFile()
|
|
||||||
conn.Connect()
|
|
||||||
|
|
||||||
poolname := GetUUID()
|
|
||||||
err := conn.MakePool(poolname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
ioctx, err := conn.OpenIOContext(poolname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
name := "parent"
|
|
||||||
img, err := rbd.Create(ioctx, name, 1<<22, 22, 1)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = img.Open()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
snapshot, err := img.CreateSnapshot("mysnap")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = snapshot.Protect()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// create an image context with the parent+snapshot
|
|
||||||
snapImg := rbd.GetImage(ioctx, "parent")
|
|
||||||
err = snapImg.Open("mysnap")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// ensure no children prior to clone
|
|
||||||
pools, images, err := snapImg.ListChildren()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, len(pools), 0, "pools equal")
|
|
||||||
assert.Equal(t, len(images), 0, "children length equal")
|
|
||||||
|
|
||||||
imgNew, err := img.Clone("mysnap", ioctx, "child", 1, 22)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = imgNew.Open()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
parentPool := make([]byte, 128)
|
|
||||||
parentName := make([]byte, 128)
|
|
||||||
parentSnapname := make([]byte, 128)
|
|
||||||
|
|
||||||
err = imgNew.GetParentInfo(parentPool, parentName, parentSnapname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
n := bytes.Index(parentName, []byte{0})
|
|
||||||
pName := string(parentName[:n])
|
|
||||||
|
|
||||||
n = bytes.Index(parentSnapname, []byte{0})
|
|
||||||
pSnapname := string(parentSnapname[:n])
|
|
||||||
assert.Equal(t, pName, "parent", "they should be equal")
|
|
||||||
assert.Equal(t, pSnapname, "mysnap", "they should be equal")
|
|
||||||
|
|
||||||
pools, images, err = snapImg.ListChildren()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, len(pools), 1, "pools equal")
|
|
||||||
assert.Equal(t, len(images), 1, "children length equal")
|
|
||||||
|
|
||||||
err = imgNew.Close()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = imgNew.Remove()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = snapshot.Unprotect()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = snapshot.Remove()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = img.Close()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = snapImg.Close()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = img.Remove()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
ioctx.Destroy()
|
|
||||||
conn.DeletePool(poolname)
|
|
||||||
conn.Shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotFound(t *testing.T) {
|
|
||||||
conn, _ := rados.NewConn()
|
|
||||||
conn.ReadDefaultConfigFile()
|
|
||||||
conn.Connect()
|
|
||||||
|
|
||||||
poolname := GetUUID()
|
|
||||||
err := conn.MakePool(poolname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
ioctx, err := conn.OpenIOContext(poolname)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
name := GetUUID()
|
|
||||||
|
|
||||||
img := rbd.GetImage(ioctx, name)
|
|
||||||
err = img.Open()
|
|
||||||
assert.Equal(t, err, rbd.RbdErrorNotFound)
|
|
||||||
|
|
||||||
img.Remove()
|
|
||||||
assert.Equal(t, err, rbd.RbdErrorNotFound)
|
|
||||||
|
|
||||||
ioctx.Destroy()
|
|
||||||
conn.DeletePool(poolname)
|
|
||||||
conn.Shutdown()
|
|
||||||
}
|
|
||||||
-82
@@ -1,82 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package activation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// correctStringWritten fails the text if the correct string wasn't written
|
|
||||||
// to the other side of the pipe.
|
|
||||||
func correctStringWritten(t *testing.T, r *os.File, expected string) bool {
|
|
||||||
bytes := make([]byte, len(expected))
|
|
||||||
io.ReadAtLeast(r, bytes, len(expected))
|
|
||||||
|
|
||||||
if string(bytes) != expected {
|
|
||||||
t.Fatalf("Unexpected string %s", string(bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestActivation forks out a copy of activation.go example and reads back two
|
|
||||||
// strings from the pipes that are passed in.
|
|
||||||
func TestActivation(t *testing.T) {
|
|
||||||
cmd := exec.Command("go", "run", "../examples/activation/activation.go")
|
|
||||||
|
|
||||||
r1, w1, _ := os.Pipe()
|
|
||||||
r2, w2, _ := os.Pipe()
|
|
||||||
cmd.ExtraFiles = []*os.File{
|
|
||||||
w1,
|
|
||||||
w2,
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
|
|
||||||
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
correctStringWritten(t, r1, "Hello world")
|
|
||||||
correctStringWritten(t, r2, "Goodbye world")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivationNoFix(t *testing.T) {
|
|
||||||
cmd := exec.Command("go", "run", "../examples/activation/activation.go")
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2")
|
|
||||||
|
|
||||||
out, _ := cmd.CombinedOutput()
|
|
||||||
if bytes.Contains(out, []byte("No files")) == false {
|
|
||||||
t.Fatalf("Child didn't error out as expected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivationNoFiles(t *testing.T) {
|
|
||||||
cmd := exec.Command("go", "run", "../examples/activation/activation.go")
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=0", "FIX_LISTEN_PID=1")
|
|
||||||
|
|
||||||
out, _ := cmd.CombinedOutput()
|
|
||||||
if bytes.Contains(out, []byte("No files")) == false {
|
|
||||||
t.Fatalf("Child didn't error out as expected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-86
@@ -1,86 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package activation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// correctStringWritten fails the text if the correct string wasn't written
|
|
||||||
// to the other side of the pipe.
|
|
||||||
func correctStringWrittenNet(t *testing.T, r net.Conn, expected string) bool {
|
|
||||||
bytes := make([]byte, len(expected))
|
|
||||||
io.ReadAtLeast(r, bytes, len(expected))
|
|
||||||
|
|
||||||
if string(bytes) != expected {
|
|
||||||
t.Fatalf("Unexpected string %s", string(bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestActivation forks out a copy of activation.go example and reads back two
|
|
||||||
// strings from the pipes that are passed in.
|
|
||||||
func TestListeners(t *testing.T) {
|
|
||||||
cmd := exec.Command("go", "run", "../examples/activation/listen.go")
|
|
||||||
|
|
||||||
l1, err := net.Listen("tcp", ":9999")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
l2, err := net.Listen("tcp", ":1234")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
t1 := l1.(*net.TCPListener)
|
|
||||||
t2 := l2.(*net.TCPListener)
|
|
||||||
|
|
||||||
f1, _ := t1.File()
|
|
||||||
f2, _ := t2.File()
|
|
||||||
|
|
||||||
cmd.ExtraFiles = []*os.File{
|
|
||||||
f1,
|
|
||||||
f2,
|
|
||||||
}
|
|
||||||
|
|
||||||
r1, err := net.Dial("tcp", "127.0.0.1:9999")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
r1.Write([]byte("Hi"))
|
|
||||||
|
|
||||||
r2, err := net.Dial("tcp", "127.0.0.1:1234")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
r2.Write([]byte("Hi"))
|
|
||||||
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
|
|
||||||
|
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
println(string(out))
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
correctStringWrittenNet(t, r1, "Hello world")
|
|
||||||
correctStringWrittenNet(t, r2, "Goodbye world")
|
|
||||||
}
|
|
||||||
Generated
Vendored
-68
@@ -1,68 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package activation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestActivation forks out a copy of activation.go example and reads back two
|
|
||||||
// strings from the pipes that are passed in.
|
|
||||||
func TestPacketConns(t *testing.T) {
|
|
||||||
cmd := exec.Command("go", "run", "../examples/activation/udpconn.go")
|
|
||||||
|
|
||||||
u1, err := net.ListenUDP("udp", &net.UDPAddr{Port: 9999})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
u2, err := net.ListenUDP("udp", &net.UDPAddr{Port: 1234})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
f1, _ := u1.File()
|
|
||||||
f2, _ := u2.File()
|
|
||||||
|
|
||||||
cmd.ExtraFiles = []*os.File{
|
|
||||||
f1,
|
|
||||||
f2,
|
|
||||||
}
|
|
||||||
|
|
||||||
r1, err := net.Dial("udp", "127.0.0.1:9999")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
r1.Write([]byte("Hi"))
|
|
||||||
|
|
||||||
r2, err := net.Dial("udp", "127.0.0.1:1234")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
r2.Write([]byte("Hi"))
|
|
||||||
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
|
|
||||||
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Cmd output '%s', err: '%s'\n", out, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
correctStringWrittenNet(t, r1, "Hello world")
|
|
||||||
correctStringWrittenNet(t, r2, "Goodbye world")
|
|
||||||
}
|
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
Docker
|
|
||||||
Copyright 2012-2016 Docker, Inc.
|
|
||||||
|
|
||||||
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
|
||||||
|
|
||||||
This product contains software (https://github.com/kr/pty) developed
|
|
||||||
by Keith Rarick, licensed under the MIT License.
|
|
||||||
|
|
||||||
The following is courtesy of our legal counsel:
|
|
||||||
|
|
||||||
|
|
||||||
Use and transfer of Docker may be subject to certain restrictions by the
|
|
||||||
United States and other governments.
|
|
||||||
It is your responsibility to ensure that your use and/or transfer does not
|
|
||||||
violate applicable laws.
|
|
||||||
|
|
||||||
For more information, please see https://www.bis.doc.gov
|
|
||||||
|
|
||||||
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.
|
|
||||||
Generated
Vendored
-39
@@ -1,39 +0,0 @@
|
|||||||
package sockets
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestInmemSocket(t *testing.T) {
|
|
||||||
l := NewInmemSocket("test", 0)
|
|
||||||
defer l.Close()
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
conn, err := l.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
conn.Write([]byte("hello"))
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := l.Dial("test", "test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 5)
|
|
||||||
_, err = conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(buf) != "hello" {
|
|
||||||
t.Fatalf("expected `hello`, got %s", string(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Close()
|
|
||||||
conn, err = l.Dial("test", "test")
|
|
||||||
if err != errClosed {
|
|
||||||
t.Fatalf("expected `errClosed` error, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-161
@@ -1,161 +0,0 @@
|
|||||||
// Copyright 2012 The Gorilla 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 context
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type keyType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
key1 keyType = iota
|
|
||||||
key2
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestContext(t *testing.T) {
|
|
||||||
assertEqual := func(val interface{}, exp interface{}) {
|
|
||||||
if val != exp {
|
|
||||||
t.Errorf("Expected %v, got %v.", exp, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
|
|
||||||
emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
|
|
||||||
|
|
||||||
// Get()
|
|
||||||
assertEqual(Get(r, key1), nil)
|
|
||||||
|
|
||||||
// Set()
|
|
||||||
Set(r, key1, "1")
|
|
||||||
assertEqual(Get(r, key1), "1")
|
|
||||||
assertEqual(len(data[r]), 1)
|
|
||||||
|
|
||||||
Set(r, key2, "2")
|
|
||||||
assertEqual(Get(r, key2), "2")
|
|
||||||
assertEqual(len(data[r]), 2)
|
|
||||||
|
|
||||||
//GetOk
|
|
||||||
value, ok := GetOk(r, key1)
|
|
||||||
assertEqual(value, "1")
|
|
||||||
assertEqual(ok, true)
|
|
||||||
|
|
||||||
value, ok = GetOk(r, "not exists")
|
|
||||||
assertEqual(value, nil)
|
|
||||||
assertEqual(ok, false)
|
|
||||||
|
|
||||||
Set(r, "nil value", nil)
|
|
||||||
value, ok = GetOk(r, "nil value")
|
|
||||||
assertEqual(value, nil)
|
|
||||||
assertEqual(ok, true)
|
|
||||||
|
|
||||||
// GetAll()
|
|
||||||
values := GetAll(r)
|
|
||||||
assertEqual(len(values), 3)
|
|
||||||
|
|
||||||
// GetAll() for empty request
|
|
||||||
values = GetAll(emptyR)
|
|
||||||
if values != nil {
|
|
||||||
t.Error("GetAll didn't return nil value for invalid request")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllOk()
|
|
||||||
values, ok = GetAllOk(r)
|
|
||||||
assertEqual(len(values), 3)
|
|
||||||
assertEqual(ok, true)
|
|
||||||
|
|
||||||
// GetAllOk() for empty request
|
|
||||||
values, ok = GetAllOk(emptyR)
|
|
||||||
assertEqual(value, nil)
|
|
||||||
assertEqual(ok, false)
|
|
||||||
|
|
||||||
// Delete()
|
|
||||||
Delete(r, key1)
|
|
||||||
assertEqual(Get(r, key1), nil)
|
|
||||||
assertEqual(len(data[r]), 2)
|
|
||||||
|
|
||||||
Delete(r, key2)
|
|
||||||
assertEqual(Get(r, key2), nil)
|
|
||||||
assertEqual(len(data[r]), 1)
|
|
||||||
|
|
||||||
// Clear()
|
|
||||||
Clear(r)
|
|
||||||
assertEqual(len(data), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) {
|
|
||||||
<-wait
|
|
||||||
for i := 0; i < iterations; i++ {
|
|
||||||
Get(r, key)
|
|
||||||
}
|
|
||||||
done <- struct{}{}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) {
|
|
||||||
<-wait
|
|
||||||
for i := 0; i < iterations; i++ {
|
|
||||||
Set(r, key, value)
|
|
||||||
}
|
|
||||||
done <- struct{}{}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) {
|
|
||||||
|
|
||||||
b.StopTimer()
|
|
||||||
r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
|
|
||||||
done := make(chan struct{})
|
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
wait := make(chan struct{})
|
|
||||||
|
|
||||||
for i := 0; i < numReaders; i++ {
|
|
||||||
go parallelReader(r, "test", iterations, wait, done)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < numWriters; i++ {
|
|
||||||
go parallelWriter(r, "test", "123", iterations, wait, done)
|
|
||||||
}
|
|
||||||
|
|
||||||
close(wait)
|
|
||||||
|
|
||||||
for i := 0; i < numReaders+numWriters; i++ {
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMutexSameReadWrite1(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 1, 1, 32)
|
|
||||||
}
|
|
||||||
func BenchmarkMutexSameReadWrite2(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 2, 2, 32)
|
|
||||||
}
|
|
||||||
func BenchmarkMutexSameReadWrite4(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 4, 4, 32)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex1(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 2, 8, 32)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex2(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 16, 4, 64)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex3(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 1, 2, 128)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex4(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 128, 32, 256)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex5(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 1024, 2048, 64)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex6(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 2048, 1024, 512)
|
|
||||||
}
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
// Copyright 2012 The Gorilla 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 mux
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkMux(b *testing.B) {
|
|
||||||
router := new(Router)
|
|
||||||
handler := func(w http.ResponseWriter, r *http.Request) {}
|
|
||||||
router.HandleFunc("/v1/{v1}", handler)
|
|
||||||
|
|
||||||
request, _ := http.NewRequest("GET", "/v1/anything", nil)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
router.ServeHTTP(nil, request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1003
File diff suppressed because it is too large
Load Diff
-714
@@ -1,714 +0,0 @@
|
|||||||
// Old tests ported to Go1. This is a mess. Want to drop it one day.
|
|
||||||
|
|
||||||
// Copyright 2011 Gorilla 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 mux
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// ResponseRecorder
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Copyright 2009 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.
|
|
||||||
|
|
||||||
// ResponseRecorder is an implementation of http.ResponseWriter that
|
|
||||||
// records its mutations for later inspection in tests.
|
|
||||||
type ResponseRecorder struct {
|
|
||||||
Code int // the HTTP response code from WriteHeader
|
|
||||||
HeaderMap http.Header // the HTTP response headers
|
|
||||||
Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
|
|
||||||
Flushed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRecorder returns an initialized ResponseRecorder.
|
|
||||||
func NewRecorder() *ResponseRecorder {
|
|
||||||
return &ResponseRecorder{
|
|
||||||
HeaderMap: make(http.Header),
|
|
||||||
Body: new(bytes.Buffer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
|
|
||||||
// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
|
|
||||||
const DefaultRemoteAddr = "1.2.3.4"
|
|
||||||
|
|
||||||
// Header returns the response headers.
|
|
||||||
func (rw *ResponseRecorder) Header() http.Header {
|
|
||||||
return rw.HeaderMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write always succeeds and writes to rw.Body, if not nil.
|
|
||||||
func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
|
|
||||||
if rw.Body != nil {
|
|
||||||
rw.Body.Write(buf)
|
|
||||||
}
|
|
||||||
if rw.Code == 0 {
|
|
||||||
rw.Code = http.StatusOK
|
|
||||||
}
|
|
||||||
return len(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteHeader sets rw.Code.
|
|
||||||
func (rw *ResponseRecorder) WriteHeader(code int) {
|
|
||||||
rw.Code = code
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush sets rw.Flushed to true.
|
|
||||||
func (rw *ResponseRecorder) Flush() {
|
|
||||||
rw.Flushed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func TestRouteMatchers(t *testing.T) {
|
|
||||||
var scheme, host, path, query, method string
|
|
||||||
var headers map[string]string
|
|
||||||
var resultVars map[bool]map[string]string
|
|
||||||
|
|
||||||
router := NewRouter()
|
|
||||||
router.NewRoute().Host("{var1}.google.com").
|
|
||||||
Path("/{var2:[a-z]+}/{var3:[0-9]+}").
|
|
||||||
Queries("foo", "bar").
|
|
||||||
Methods("GET").
|
|
||||||
Schemes("https").
|
|
||||||
Headers("x-requested-with", "XMLHttpRequest")
|
|
||||||
router.NewRoute().Host("www.{var4}.com").
|
|
||||||
PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}").
|
|
||||||
Queries("baz", "ding").
|
|
||||||
Methods("POST").
|
|
||||||
Schemes("http").
|
|
||||||
Headers("Content-Type", "application/json")
|
|
||||||
|
|
||||||
reset := func() {
|
|
||||||
// Everything match.
|
|
||||||
scheme = "https"
|
|
||||||
host = "www.google.com"
|
|
||||||
path = "/product/42"
|
|
||||||
query = "?foo=bar"
|
|
||||||
method = "GET"
|
|
||||||
headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
|
|
||||||
resultVars = map[bool]map[string]string{
|
|
||||||
true: {"var1": "www", "var2": "product", "var3": "42"},
|
|
||||||
false: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reset2 := func() {
|
|
||||||
// Everything match.
|
|
||||||
scheme = "http"
|
|
||||||
host = "www.google.com"
|
|
||||||
path = "/foo/product/42/path/that/is/ignored"
|
|
||||||
query = "?baz=ding"
|
|
||||||
method = "POST"
|
|
||||||
headers = map[string]string{"Content-Type": "application/json"}
|
|
||||||
resultVars = map[bool]map[string]string{
|
|
||||||
true: {"var4": "google", "var5": "product", "var6": "42"},
|
|
||||||
false: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match := func(shouldMatch bool) {
|
|
||||||
url := scheme + "://" + host + path + query
|
|
||||||
request, _ := http.NewRequest(method, url, nil)
|
|
||||||
for key, value := range headers {
|
|
||||||
request.Header.Add(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
matched := router.Match(request, &routeMatch)
|
|
||||||
if matched != shouldMatch {
|
|
||||||
// Need better messages. :)
|
|
||||||
if matched {
|
|
||||||
t.Errorf("Should match.")
|
|
||||||
} else {
|
|
||||||
t.Errorf("Should not match.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if matched {
|
|
||||||
currentRoute := routeMatch.Route
|
|
||||||
if currentRoute == nil {
|
|
||||||
t.Errorf("Expected a current route.")
|
|
||||||
}
|
|
||||||
vars := routeMatch.Vars
|
|
||||||
expectedVars := resultVars[shouldMatch]
|
|
||||||
if len(vars) != len(expectedVars) {
|
|
||||||
t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
|
|
||||||
}
|
|
||||||
for name, value := range vars {
|
|
||||||
if expectedVars[name] != value {
|
|
||||||
t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1st route --------------------------------------------------------------
|
|
||||||
|
|
||||||
// Everything match.
|
|
||||||
reset()
|
|
||||||
match(true)
|
|
||||||
|
|
||||||
// Scheme doesn't match.
|
|
||||||
reset()
|
|
||||||
scheme = "http"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Host doesn't match.
|
|
||||||
reset()
|
|
||||||
host = "www.mygoogle.com"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Path doesn't match.
|
|
||||||
reset()
|
|
||||||
path = "/product/notdigits"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Query doesn't match.
|
|
||||||
reset()
|
|
||||||
query = "?foo=baz"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Method doesn't match.
|
|
||||||
reset()
|
|
||||||
method = "POST"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Header doesn't match.
|
|
||||||
reset()
|
|
||||||
headers = map[string]string{}
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Everything match, again.
|
|
||||||
reset()
|
|
||||||
match(true)
|
|
||||||
|
|
||||||
// 2nd route --------------------------------------------------------------
|
|
||||||
|
|
||||||
// Everything match.
|
|
||||||
reset2()
|
|
||||||
match(true)
|
|
||||||
|
|
||||||
// Scheme doesn't match.
|
|
||||||
reset2()
|
|
||||||
scheme = "https"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Host doesn't match.
|
|
||||||
reset2()
|
|
||||||
host = "sub.google.com"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Path doesn't match.
|
|
||||||
reset2()
|
|
||||||
path = "/bar/product/42"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Query doesn't match.
|
|
||||||
reset2()
|
|
||||||
query = "?foo=baz"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Method doesn't match.
|
|
||||||
reset2()
|
|
||||||
method = "GET"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Header doesn't match.
|
|
||||||
reset2()
|
|
||||||
headers = map[string]string{}
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Everything match, again.
|
|
||||||
reset2()
|
|
||||||
match(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
type headerMatcherTest struct {
|
|
||||||
matcher headerMatcher
|
|
||||||
headers map[string]string
|
|
||||||
result bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var headerMatcherTests = []headerMatcherTest{
|
|
||||||
{
|
|
||||||
matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
|
|
||||||
headers: map[string]string{"X-Requested-With": "XMLHttpRequest"},
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: headerMatcher(map[string]string{"x-requested-with": ""}),
|
|
||||||
headers: map[string]string{"X-Requested-With": "anything"},
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
|
|
||||||
headers: map[string]string{},
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type hostMatcherTest struct {
|
|
||||||
matcher *Route
|
|
||||||
url string
|
|
||||||
vars map[string]string
|
|
||||||
result bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var hostMatcherTests = []hostMatcherTest{
|
|
||||||
{
|
|
||||||
matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
|
|
||||||
url: "http://abc.def.ghi/",
|
|
||||||
vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
|
|
||||||
url: "http://a.b.c/",
|
|
||||||
vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type methodMatcherTest struct {
|
|
||||||
matcher methodMatcher
|
|
||||||
method string
|
|
||||||
result bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodMatcherTests = []methodMatcherTest{
|
|
||||||
{
|
|
||||||
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
|
|
||||||
method: "GET",
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
|
|
||||||
method: "POST",
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
|
|
||||||
method: "PUT",
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
|
|
||||||
method: "DELETE",
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type pathMatcherTest struct {
|
|
||||||
matcher *Route
|
|
||||||
url string
|
|
||||||
vars map[string]string
|
|
||||||
result bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var pathMatcherTests = []pathMatcherTest{
|
|
||||||
{
|
|
||||||
matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
|
|
||||||
url: "http://localhost:8080/123/456/789",
|
|
||||||
vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
|
|
||||||
url: "http://localhost:8080/1/2/3",
|
|
||||||
vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type schemeMatcherTest struct {
|
|
||||||
matcher schemeMatcher
|
|
||||||
url string
|
|
||||||
result bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var schemeMatcherTests = []schemeMatcherTest{
|
|
||||||
{
|
|
||||||
matcher: schemeMatcher([]string{"http", "https"}),
|
|
||||||
url: "http://localhost:8080/",
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: schemeMatcher([]string{"http", "https"}),
|
|
||||||
url: "https://localhost:8080/",
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: schemeMatcher([]string{"https"}),
|
|
||||||
url: "http://localhost:8080/",
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: schemeMatcher([]string{"http"}),
|
|
||||||
url: "https://localhost:8080/",
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type urlBuildingTest struct {
|
|
||||||
route *Route
|
|
||||||
vars []string
|
|
||||||
url string
|
|
||||||
}
|
|
||||||
|
|
||||||
var urlBuildingTests = []urlBuildingTest{
|
|
||||||
{
|
|
||||||
route: new(Route).Host("foo.domain.com"),
|
|
||||||
vars: []string{},
|
|
||||||
url: "http://foo.domain.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: new(Route).Host("{subdomain}.domain.com"),
|
|
||||||
vars: []string{"subdomain", "bar"},
|
|
||||||
url: "http://bar.domain.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: new(Route).Host("foo.domain.com").Path("/articles"),
|
|
||||||
vars: []string{},
|
|
||||||
url: "http://foo.domain.com/articles",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: new(Route).Path("/articles"),
|
|
||||||
vars: []string{},
|
|
||||||
url: "/articles",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
|
|
||||||
vars: []string{"category", "technology", "id", "42"},
|
|
||||||
url: "/articles/technology/42",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
|
|
||||||
vars: []string{"subdomain", "foo", "category", "technology", "id", "42"},
|
|
||||||
url: "http://foo.domain.com/articles/technology/42",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHeaderMatcher(t *testing.T) {
|
|
||||||
for _, v := range headerMatcherTests {
|
|
||||||
request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
|
|
||||||
for key, value := range v.headers {
|
|
||||||
request.Header.Add(key, value)
|
|
||||||
}
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
result := v.matcher.Match(request, &routeMatch)
|
|
||||||
if result != v.result {
|
|
||||||
if v.result {
|
|
||||||
t.Errorf("%#v: should match %v.", v.matcher, request.Header)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHostMatcher(t *testing.T) {
|
|
||||||
for _, v := range hostMatcherTests {
|
|
||||||
request, _ := http.NewRequest("GET", v.url, nil)
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
result := v.matcher.Match(request, &routeMatch)
|
|
||||||
vars := routeMatch.Vars
|
|
||||||
if result != v.result {
|
|
||||||
if v.result {
|
|
||||||
t.Errorf("%#v: should match %v.", v.matcher, v.url)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%#v: should not match %v.", v.matcher, v.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if result {
|
|
||||||
if len(vars) != len(v.vars) {
|
|
||||||
t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
|
|
||||||
}
|
|
||||||
for name, value := range vars {
|
|
||||||
if v.vars[name] != value {
|
|
||||||
t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(vars) != 0 {
|
|
||||||
t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMethodMatcher(t *testing.T) {
|
|
||||||
for _, v := range methodMatcherTests {
|
|
||||||
request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
result := v.matcher.Match(request, &routeMatch)
|
|
||||||
if result != v.result {
|
|
||||||
if v.result {
|
|
||||||
t.Errorf("%#v: should match %v.", v.matcher, v.method)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%#v: should not match %v.", v.matcher, v.method)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPathMatcher(t *testing.T) {
|
|
||||||
for _, v := range pathMatcherTests {
|
|
||||||
request, _ := http.NewRequest("GET", v.url, nil)
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
result := v.matcher.Match(request, &routeMatch)
|
|
||||||
vars := routeMatch.Vars
|
|
||||||
if result != v.result {
|
|
||||||
if v.result {
|
|
||||||
t.Errorf("%#v: should match %v.", v.matcher, v.url)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%#v: should not match %v.", v.matcher, v.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if result {
|
|
||||||
if len(vars) != len(v.vars) {
|
|
||||||
t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
|
|
||||||
}
|
|
||||||
for name, value := range vars {
|
|
||||||
if v.vars[name] != value {
|
|
||||||
t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(vars) != 0 {
|
|
||||||
t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSchemeMatcher(t *testing.T) {
|
|
||||||
for _, v := range schemeMatcherTests {
|
|
||||||
request, _ := http.NewRequest("GET", v.url, nil)
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
result := v.matcher.Match(request, &routeMatch)
|
|
||||||
if result != v.result {
|
|
||||||
if v.result {
|
|
||||||
t.Errorf("%#v: should match %v.", v.matcher, v.url)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%#v: should not match %v.", v.matcher, v.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUrlBuilding(t *testing.T) {
|
|
||||||
|
|
||||||
for _, v := range urlBuildingTests {
|
|
||||||
u, _ := v.route.URL(v.vars...)
|
|
||||||
url := u.String()
|
|
||||||
if url != v.url {
|
|
||||||
t.Errorf("expected %v, got %v", v.url, url)
|
|
||||||
/*
|
|
||||||
reversePath := ""
|
|
||||||
reverseHost := ""
|
|
||||||
if v.route.pathTemplate != nil {
|
|
||||||
reversePath = v.route.pathTemplate.Reverse
|
|
||||||
}
|
|
||||||
if v.route.hostTemplate != nil {
|
|
||||||
reverseHost = v.route.hostTemplate.Reverse
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost)
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
}
|
|
||||||
|
|
||||||
router := NewRouter()
|
|
||||||
router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
|
|
||||||
|
|
||||||
url, _ := router.Get("article").URL("category", "technology", "id", "42")
|
|
||||||
expected := "/articles/technology/42"
|
|
||||||
if url.String() != expected {
|
|
||||||
t.Errorf("Expected %v, got %v", expected, url.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMatchedRouteName(t *testing.T) {
|
|
||||||
routeName := "stock"
|
|
||||||
router := NewRouter()
|
|
||||||
route := router.NewRoute().Path("/products/").Name(routeName)
|
|
||||||
|
|
||||||
url := "http://www.domain.com/products/"
|
|
||||||
request, _ := http.NewRequest("GET", url, nil)
|
|
||||||
var rv RouteMatch
|
|
||||||
ok := router.Match(request, &rv)
|
|
||||||
|
|
||||||
if !ok || rv.Route != route {
|
|
||||||
t.Errorf("Expected same route, got %+v.", rv.Route)
|
|
||||||
}
|
|
||||||
|
|
||||||
retName := rv.Route.GetName()
|
|
||||||
if retName != routeName {
|
|
||||||
t.Errorf("Expected %q, got %q.", routeName, retName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubRouting(t *testing.T) {
|
|
||||||
// Example from docs.
|
|
||||||
router := NewRouter()
|
|
||||||
subrouter := router.NewRoute().Host("www.domain.com").Subrouter()
|
|
||||||
route := subrouter.NewRoute().Path("/products/").Name("products")
|
|
||||||
|
|
||||||
url := "http://www.domain.com/products/"
|
|
||||||
request, _ := http.NewRequest("GET", url, nil)
|
|
||||||
var rv RouteMatch
|
|
||||||
ok := router.Match(request, &rv)
|
|
||||||
|
|
||||||
if !ok || rv.Route != route {
|
|
||||||
t.Errorf("Expected same route, got %+v.", rv.Route)
|
|
||||||
}
|
|
||||||
|
|
||||||
u, _ := router.Get("products").URL()
|
|
||||||
builtUrl := u.String()
|
|
||||||
// Yay, subroute aware of the domain when building!
|
|
||||||
if builtUrl != url {
|
|
||||||
t.Errorf("Expected %q, got %q.", url, builtUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVariableNames(t *testing.T) {
|
|
||||||
route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
|
|
||||||
if route.err == nil {
|
|
||||||
t.Errorf("Expected error for duplicated variable names")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRedirectSlash(t *testing.T) {
|
|
||||||
var route *Route
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
r := NewRouter()
|
|
||||||
|
|
||||||
r.StrictSlash(false)
|
|
||||||
route = r.NewRoute()
|
|
||||||
if route.strictSlash != false {
|
|
||||||
t.Errorf("Expected false redirectSlash.")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.StrictSlash(true)
|
|
||||||
route = r.NewRoute()
|
|
||||||
if route.strictSlash != true {
|
|
||||||
t.Errorf("Expected true redirectSlash.")
|
|
||||||
}
|
|
||||||
|
|
||||||
route = new(Route)
|
|
||||||
route.strictSlash = true
|
|
||||||
route.Path("/{arg1}/{arg2:[0-9]+}/")
|
|
||||||
request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
|
|
||||||
routeMatch = RouteMatch{}
|
|
||||||
_ = route.Match(request, &routeMatch)
|
|
||||||
vars := routeMatch.Vars
|
|
||||||
if vars["arg1"] != "foo" {
|
|
||||||
t.Errorf("Expected foo.")
|
|
||||||
}
|
|
||||||
if vars["arg2"] != "123" {
|
|
||||||
t.Errorf("Expected 123.")
|
|
||||||
}
|
|
||||||
rsp := NewRecorder()
|
|
||||||
routeMatch.Handler.ServeHTTP(rsp, request)
|
|
||||||
if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
|
|
||||||
t.Errorf("Expected redirect header.")
|
|
||||||
}
|
|
||||||
|
|
||||||
route = new(Route)
|
|
||||||
route.strictSlash = true
|
|
||||||
route.Path("/{arg1}/{arg2:[0-9]+}")
|
|
||||||
request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
|
|
||||||
routeMatch = RouteMatch{}
|
|
||||||
_ = route.Match(request, &routeMatch)
|
|
||||||
vars = routeMatch.Vars
|
|
||||||
if vars["arg1"] != "foo" {
|
|
||||||
t.Errorf("Expected foo.")
|
|
||||||
}
|
|
||||||
if vars["arg2"] != "123" {
|
|
||||||
t.Errorf("Expected 123.")
|
|
||||||
}
|
|
||||||
rsp = NewRecorder()
|
|
||||||
routeMatch.Handler.ServeHTTP(rsp, request)
|
|
||||||
if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
|
|
||||||
t.Errorf("Expected redirect header.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test for the new regexp library, still not available in stable Go.
|
|
||||||
func TestNewRegexp(t *testing.T) {
|
|
||||||
var p *routeRegexp
|
|
||||||
var matches []string
|
|
||||||
|
|
||||||
tests := map[string]map[string][]string{
|
|
||||||
"/{foo:a{2}}": {
|
|
||||||
"/a": nil,
|
|
||||||
"/aa": {"aa"},
|
|
||||||
"/aaa": nil,
|
|
||||||
"/aaaa": nil,
|
|
||||||
},
|
|
||||||
"/{foo:a{2,}}": {
|
|
||||||
"/a": nil,
|
|
||||||
"/aa": {"aa"},
|
|
||||||
"/aaa": {"aaa"},
|
|
||||||
"/aaaa": {"aaaa"},
|
|
||||||
},
|
|
||||||
"/{foo:a{2,3}}": {
|
|
||||||
"/a": nil,
|
|
||||||
"/aa": {"aa"},
|
|
||||||
"/aaa": {"aaa"},
|
|
||||||
"/aaaa": nil,
|
|
||||||
},
|
|
||||||
"/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
|
|
||||||
"/a": nil,
|
|
||||||
"/ab": nil,
|
|
||||||
"/abc": nil,
|
|
||||||
"/abcd": nil,
|
|
||||||
"/abc/ab": {"abc", "ab"},
|
|
||||||
"/abc/abc": nil,
|
|
||||||
"/abcd/ab": nil,
|
|
||||||
},
|
|
||||||
`/{foo:\w{3,}}/{bar:\d{2,}}`: {
|
|
||||||
"/a": nil,
|
|
||||||
"/ab": nil,
|
|
||||||
"/abc": nil,
|
|
||||||
"/abc/1": nil,
|
|
||||||
"/abc/12": {"abc", "12"},
|
|
||||||
"/abcd/12": {"abcd", "12"},
|
|
||||||
"/abcd/123": {"abcd", "123"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for pattern, paths := range tests {
|
|
||||||
p, _ = newRouteRegexp(pattern, false, false, false, false)
|
|
||||||
for path, result := range paths {
|
|
||||||
matches = p.regexp.FindStringSubmatch(path)
|
|
||||||
if result == nil {
|
|
||||||
if matches != nil {
|
|
||||||
t.Errorf("%v should not match %v.", pattern, path)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(matches) != len(result)+1 {
|
|
||||||
t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
|
|
||||||
} else {
|
|
||||||
for k, v := range result {
|
|
||||||
if matches[k+1] != v {
|
|
||||||
t.Errorf("Expected %v, got %v.", v, matches[k+1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-123
@@ -1,123 +0,0 @@
|
|||||||
// Copyright (C) 2013-2015 by Maxim Bublis <b@codemonkey.ru>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkFromBytes(b *testing.B) {
|
|
||||||
bytes := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
FromBytes(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkFromString(b *testing.B) {
|
|
||||||
s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
FromString(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkFromStringUrn(b *testing.B) {
|
|
||||||
s := "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
FromString(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkFromStringWithBrackets(b *testing.B) {
|
|
||||||
s := "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}"
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
FromString(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNewV1(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NewV1()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNewV2(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NewV2(DomainPerson)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNewV3(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NewV3(NamespaceDNS, "www.example.com")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNewV4(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NewV4()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNewV5(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NewV5(NamespaceDNS, "www.example.com")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMarshalBinary(b *testing.B) {
|
|
||||||
u := NewV4()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
u.MarshalBinary()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMarshalText(b *testing.B) {
|
|
||||||
u := NewV4()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
u.MarshalText()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkUnmarshalBinary(b *testing.B) {
|
|
||||||
bytes := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
u := UUID{}
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
u.UnmarshalBinary(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkUnmarshalText(b *testing.B) {
|
|
||||||
bytes := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
|
||||||
u := UUID{}
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
u.UnmarshalText(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sink string
|
|
||||||
|
|
||||||
func BenchmarkMarshalToString(b *testing.B) {
|
|
||||||
u := NewV4()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
sink = u.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-481
@@ -1,481 +0,0 @@
|
|||||||
// Copyright (C) 2013-2015 by Maxim Bublis <b@codemonkey.ru>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// Package uuid provides implementation of Universally Unique Identifier (UUID).
|
|
||||||
// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and
|
|
||||||
// version 2 (as specified in DCE 1.1).
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1"
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UUID layout variants.
|
|
||||||
const (
|
|
||||||
VariantNCS = iota
|
|
||||||
VariantRFC4122
|
|
||||||
VariantMicrosoft
|
|
||||||
VariantFuture
|
|
||||||
)
|
|
||||||
|
|
||||||
// UUID DCE domains.
|
|
||||||
const (
|
|
||||||
DomainPerson = iota
|
|
||||||
DomainGroup
|
|
||||||
DomainOrg
|
|
||||||
)
|
|
||||||
|
|
||||||
// Difference in 100-nanosecond intervals between
|
|
||||||
// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
|
|
||||||
const epochStart = 122192928000000000
|
|
||||||
|
|
||||||
// Used in string method conversion
|
|
||||||
const dash byte = '-'
|
|
||||||
|
|
||||||
// UUID v1/v2 storage.
|
|
||||||
var (
|
|
||||||
storageMutex sync.Mutex
|
|
||||||
storageOnce sync.Once
|
|
||||||
epochFunc = unixTimeFunc
|
|
||||||
clockSequence uint16
|
|
||||||
lastTime uint64
|
|
||||||
hardwareAddr [6]byte
|
|
||||||
posixUID = uint32(os.Getuid())
|
|
||||||
posixGID = uint32(os.Getgid())
|
|
||||||
)
|
|
||||||
|
|
||||||
// String parse helpers.
|
|
||||||
var (
|
|
||||||
urnPrefix = []byte("urn:uuid:")
|
|
||||||
byteGroups = []int{8, 4, 4, 4, 12}
|
|
||||||
)
|
|
||||||
|
|
||||||
func initClockSequence() {
|
|
||||||
buf := make([]byte, 2)
|
|
||||||
safeRandom(buf)
|
|
||||||
clockSequence = binary.BigEndian.Uint16(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initHardwareAddr() {
|
|
||||||
interfaces, err := net.Interfaces()
|
|
||||||
if err == nil {
|
|
||||||
for _, iface := range interfaces {
|
|
||||||
if len(iface.HardwareAddr) >= 6 {
|
|
||||||
copy(hardwareAddr[:], iface.HardwareAddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize hardwareAddr randomly in case
|
|
||||||
// of real network interfaces absence
|
|
||||||
safeRandom(hardwareAddr[:])
|
|
||||||
|
|
||||||
// Set multicast bit as recommended in RFC 4122
|
|
||||||
hardwareAddr[0] |= 0x01
|
|
||||||
}
|
|
||||||
|
|
||||||
func initStorage() {
|
|
||||||
initClockSequence()
|
|
||||||
initHardwareAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func safeRandom(dest []byte) {
|
|
||||||
if _, err := rand.Read(dest); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns difference in 100-nanosecond intervals between
|
|
||||||
// UUID epoch (October 15, 1582) and current time.
|
|
||||||
// This is default epoch calculation function.
|
|
||||||
func unixTimeFunc() uint64 {
|
|
||||||
return epochStart + uint64(time.Now().UnixNano()/100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UUID representation compliant with specification
|
|
||||||
// described in RFC 4122.
|
|
||||||
type UUID [16]byte
|
|
||||||
|
|
||||||
// NullUUID can be used with the standard sql package to represent a
|
|
||||||
// UUID value that can be NULL in the database
|
|
||||||
type NullUUID struct {
|
|
||||||
UUID UUID
|
|
||||||
Valid bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// The nil UUID is special form of UUID that is specified to have all
|
|
||||||
// 128 bits set to zero.
|
|
||||||
var Nil = UUID{}
|
|
||||||
|
|
||||||
// Predefined namespace UUIDs.
|
|
||||||
var (
|
|
||||||
NamespaceDNS, _ = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
|
||||||
NamespaceURL, _ = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
|
|
||||||
NamespaceOID, _ = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
|
|
||||||
NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
|
||||||
)
|
|
||||||
|
|
||||||
// And returns result of binary AND of two UUIDs.
|
|
||||||
func And(u1 UUID, u2 UUID) UUID {
|
|
||||||
u := UUID{}
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
u[i] = u1[i] & u2[i]
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// Or returns result of binary OR of two UUIDs.
|
|
||||||
func Or(u1 UUID, u2 UUID) UUID {
|
|
||||||
u := UUID{}
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
u[i] = u1[i] | u2[i]
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal returns true if u1 and u2 equals, otherwise returns false.
|
|
||||||
func Equal(u1 UUID, u2 UUID) bool {
|
|
||||||
return bytes.Equal(u1[:], u2[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version returns algorithm version used to generate UUID.
|
|
||||||
func (u UUID) Version() uint {
|
|
||||||
return uint(u[6] >> 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variant returns UUID layout variant.
|
|
||||||
func (u UUID) Variant() uint {
|
|
||||||
switch {
|
|
||||||
case (u[8] & 0x80) == 0x00:
|
|
||||||
return VariantNCS
|
|
||||||
case (u[8]&0xc0)|0x80 == 0x80:
|
|
||||||
return VariantRFC4122
|
|
||||||
case (u[8]&0xe0)|0xc0 == 0xc0:
|
|
||||||
return VariantMicrosoft
|
|
||||||
}
|
|
||||||
return VariantFuture
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns bytes slice representation of UUID.
|
|
||||||
func (u UUID) Bytes() []byte {
|
|
||||||
return u[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns canonical string representation of UUID:
|
|
||||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
|
|
||||||
func (u UUID) String() string {
|
|
||||||
buf := make([]byte, 36)
|
|
||||||
|
|
||||||
hex.Encode(buf[0:8], u[0:4])
|
|
||||||
buf[8] = dash
|
|
||||||
hex.Encode(buf[9:13], u[4:6])
|
|
||||||
buf[13] = dash
|
|
||||||
hex.Encode(buf[14:18], u[6:8])
|
|
||||||
buf[18] = dash
|
|
||||||
hex.Encode(buf[19:23], u[8:10])
|
|
||||||
buf[23] = dash
|
|
||||||
hex.Encode(buf[24:], u[10:])
|
|
||||||
|
|
||||||
return string(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetVersion sets version bits.
|
|
||||||
func (u *UUID) SetVersion(v byte) {
|
|
||||||
u[6] = (u[6] & 0x0f) | (v << 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetVariant sets variant bits as described in RFC 4122.
|
|
||||||
func (u *UUID) SetVariant() {
|
|
||||||
u[8] = (u[8] & 0xbf) | 0x80
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalText implements the encoding.TextMarshaler interface.
|
|
||||||
// The encoding is the same as returned by String.
|
|
||||||
func (u UUID) MarshalText() (text []byte, err error) {
|
|
||||||
text = []byte(u.String())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
|
||||||
// Following formats are supported:
|
|
||||||
// "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
||||||
// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
|
|
||||||
// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
||||||
func (u *UUID) UnmarshalText(text []byte) (err error) {
|
|
||||||
if len(text) < 32 {
|
|
||||||
err = fmt.Errorf("uuid: UUID string too short: %s", text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t := text[:]
|
|
||||||
braced := false
|
|
||||||
|
|
||||||
if bytes.Equal(t[:9], urnPrefix) {
|
|
||||||
t = t[9:]
|
|
||||||
} else if t[0] == '{' {
|
|
||||||
braced = true
|
|
||||||
t = t[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
b := u[:]
|
|
||||||
|
|
||||||
for i, byteGroup := range byteGroups {
|
|
||||||
if i > 0 {
|
|
||||||
if t[0] != '-' {
|
|
||||||
err = fmt.Errorf("uuid: invalid string format")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t = t[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(t) < byteGroup {
|
|
||||||
err = fmt.Errorf("uuid: UUID string too short: %s", text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == 4 && len(t) > byteGroup &&
|
|
||||||
((braced && t[byteGroup] != '}') || len(t[byteGroup:]) > 1 || !braced) {
|
|
||||||
err = fmt.Errorf("uuid: UUID string too long: %s", text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = hex.Decode(b[:byteGroup/2], t[:byteGroup])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t = t[byteGroup:]
|
|
||||||
b = b[byteGroup/2:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
|
||||||
func (u UUID) MarshalBinary() (data []byte, err error) {
|
|
||||||
data = u.Bytes()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
|
|
||||||
// It will return error if the slice isn't 16 bytes long.
|
|
||||||
func (u *UUID) UnmarshalBinary(data []byte) (err error) {
|
|
||||||
if len(data) != 16 {
|
|
||||||
err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
copy(u[:], data)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the driver.Valuer interface.
|
|
||||||
func (u UUID) Value() (driver.Value, error) {
|
|
||||||
return u.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan implements the sql.Scanner interface.
|
|
||||||
// A 16-byte slice is handled by UnmarshalBinary, while
|
|
||||||
// a longer byte slice or a string is handled by UnmarshalText.
|
|
||||||
func (u *UUID) Scan(src interface{}) error {
|
|
||||||
switch src := src.(type) {
|
|
||||||
case []byte:
|
|
||||||
if len(src) == 16 {
|
|
||||||
return u.UnmarshalBinary(src)
|
|
||||||
}
|
|
||||||
return u.UnmarshalText(src)
|
|
||||||
|
|
||||||
case string:
|
|
||||||
return u.UnmarshalText([]byte(src))
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("uuid: cannot convert %T to UUID", src)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the driver.Valuer interface.
|
|
||||||
func (u NullUUID) Value() (driver.Value, error) {
|
|
||||||
if !u.Valid {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
// Delegate to UUID Value function
|
|
||||||
return u.UUID.Value()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan implements the sql.Scanner interface.
|
|
||||||
func (u *NullUUID) Scan(src interface{}) error {
|
|
||||||
if src == nil {
|
|
||||||
u.UUID, u.Valid = Nil, false
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate to UUID Scan function
|
|
||||||
u.Valid = true
|
|
||||||
return u.UUID.Scan(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromBytes returns UUID converted from raw byte slice input.
|
|
||||||
// It will return error if the slice isn't 16 bytes long.
|
|
||||||
func FromBytes(input []byte) (u UUID, err error) {
|
|
||||||
err = u.UnmarshalBinary(input)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromBytesOrNil returns UUID converted from raw byte slice input.
|
|
||||||
// Same behavior as FromBytes, but returns a Nil UUID on error.
|
|
||||||
func FromBytesOrNil(input []byte) UUID {
|
|
||||||
uuid, err := FromBytes(input)
|
|
||||||
if err != nil {
|
|
||||||
return Nil
|
|
||||||
}
|
|
||||||
return uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromString returns UUID parsed from string input.
|
|
||||||
// Input is expected in a form accepted by UnmarshalText.
|
|
||||||
func FromString(input string) (u UUID, err error) {
|
|
||||||
err = u.UnmarshalText([]byte(input))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromStringOrNil returns UUID parsed from string input.
|
|
||||||
// Same behavior as FromString, but returns a Nil UUID on error.
|
|
||||||
func FromStringOrNil(input string) UUID {
|
|
||||||
uuid, err := FromString(input)
|
|
||||||
if err != nil {
|
|
||||||
return Nil
|
|
||||||
}
|
|
||||||
return uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns UUID v1/v2 storage state.
|
|
||||||
// Returns epoch timestamp, clock sequence, and hardware address.
|
|
||||||
func getStorage() (uint64, uint16, []byte) {
|
|
||||||
storageOnce.Do(initStorage)
|
|
||||||
|
|
||||||
storageMutex.Lock()
|
|
||||||
defer storageMutex.Unlock()
|
|
||||||
|
|
||||||
timeNow := epochFunc()
|
|
||||||
// Clock changed backwards since last UUID generation.
|
|
||||||
// Should increase clock sequence.
|
|
||||||
if timeNow <= lastTime {
|
|
||||||
clockSequence++
|
|
||||||
}
|
|
||||||
lastTime = timeNow
|
|
||||||
|
|
||||||
return timeNow, clockSequence, hardwareAddr[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV1 returns UUID based on current timestamp and MAC address.
|
|
||||||
func NewV1() UUID {
|
|
||||||
u := UUID{}
|
|
||||||
|
|
||||||
timeNow, clockSeq, hardwareAddr := getStorage()
|
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
|
|
||||||
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
|
|
||||||
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
|
|
||||||
binary.BigEndian.PutUint16(u[8:], clockSeq)
|
|
||||||
|
|
||||||
copy(u[10:], hardwareAddr)
|
|
||||||
|
|
||||||
u.SetVersion(1)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV2 returns DCE Security UUID based on POSIX UID/GID.
|
|
||||||
func NewV2(domain byte) UUID {
|
|
||||||
u := UUID{}
|
|
||||||
|
|
||||||
timeNow, clockSeq, hardwareAddr := getStorage()
|
|
||||||
|
|
||||||
switch domain {
|
|
||||||
case DomainPerson:
|
|
||||||
binary.BigEndian.PutUint32(u[0:], posixUID)
|
|
||||||
case DomainGroup:
|
|
||||||
binary.BigEndian.PutUint32(u[0:], posixGID)
|
|
||||||
}
|
|
||||||
|
|
||||||
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
|
|
||||||
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
|
|
||||||
binary.BigEndian.PutUint16(u[8:], clockSeq)
|
|
||||||
u[9] = domain
|
|
||||||
|
|
||||||
copy(u[10:], hardwareAddr)
|
|
||||||
|
|
||||||
u.SetVersion(2)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
|
|
||||||
func NewV3(ns UUID, name string) UUID {
|
|
||||||
u := newFromHash(md5.New(), ns, name)
|
|
||||||
u.SetVersion(3)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV4 returns random generated UUID.
|
|
||||||
func NewV4() UUID {
|
|
||||||
u := UUID{}
|
|
||||||
safeRandom(u[:])
|
|
||||||
u.SetVersion(4)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
|
|
||||||
func NewV5(ns UUID, name string) UUID {
|
|
||||||
u := newFromHash(sha1.New(), ns, name)
|
|
||||||
u.SetVersion(5)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns UUID based on hashing of namespace UUID and name.
|
|
||||||
func newFromHash(h hash.Hash, ns UUID, name string) UUID {
|
|
||||||
u := UUID{}
|
|
||||||
h.Write(ns[:])
|
|
||||||
h.Write([]byte(name))
|
|
||||||
copy(u[:], h.Sum(nil))
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
-633
@@ -1,633 +0,0 @@
|
|||||||
// Copyright (C) 2013, 2015 by Maxim Bublis <b@codemonkey.ru>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBytes(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
|
|
||||||
bytes1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
|
|
||||||
if !bytes.Equal(u.Bytes(), bytes1) {
|
|
||||||
t.Errorf("Incorrect bytes representation for UUID: %s", u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestString(t *testing.T) {
|
|
||||||
if NamespaceDNS.String() != "6ba7b810-9dad-11d1-80b4-00c04fd430c8" {
|
|
||||||
t.Errorf("Incorrect string representation for UUID: %s", NamespaceDNS.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqual(t *testing.T) {
|
|
||||||
if !Equal(NamespaceDNS, NamespaceDNS) {
|
|
||||||
t.Errorf("Incorrect comparison of %s and %s", NamespaceDNS, NamespaceDNS)
|
|
||||||
}
|
|
||||||
|
|
||||||
if Equal(NamespaceDNS, NamespaceURL) {
|
|
||||||
t.Errorf("Incorrect comparison of %s and %s", NamespaceDNS, NamespaceURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOr(t *testing.T) {
|
|
||||||
u1 := UUID{0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff}
|
|
||||||
u2 := UUID{0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00}
|
|
||||||
|
|
||||||
u := UUID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
|
||||||
|
|
||||||
if !Equal(u, Or(u1, u2)) {
|
|
||||||
t.Errorf("Incorrect bitwise OR result %s", Or(u1, u2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAnd(t *testing.T) {
|
|
||||||
u1 := UUID{0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff}
|
|
||||||
u2 := UUID{0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00}
|
|
||||||
|
|
||||||
u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
|
|
||||||
if !Equal(u, And(u1, u2)) {
|
|
||||||
t.Errorf("Incorrect bitwise AND result %s", And(u1, u2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVersion(t *testing.T) {
|
|
||||||
u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
|
|
||||||
if u.Version() != 1 {
|
|
||||||
t.Errorf("Incorrect version for UUID: %d", u.Version())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetVersion(t *testing.T) {
|
|
||||||
u := UUID{}
|
|
||||||
u.SetVersion(4)
|
|
||||||
|
|
||||||
if u.Version() != 4 {
|
|
||||||
t.Errorf("Incorrect version for UUID after u.setVersion(4): %d", u.Version())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVariant(t *testing.T) {
|
|
||||||
u1 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
|
|
||||||
if u1.Variant() != VariantNCS {
|
|
||||||
t.Errorf("Incorrect variant for UUID variant %d: %d", VariantNCS, u1.Variant())
|
|
||||||
}
|
|
||||||
|
|
||||||
u2 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
|
|
||||||
if u2.Variant() != VariantRFC4122 {
|
|
||||||
t.Errorf("Incorrect variant for UUID variant %d: %d", VariantRFC4122, u2.Variant())
|
|
||||||
}
|
|
||||||
|
|
||||||
u3 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
|
|
||||||
if u3.Variant() != VariantMicrosoft {
|
|
||||||
t.Errorf("Incorrect variant for UUID variant %d: %d", VariantMicrosoft, u3.Variant())
|
|
||||||
}
|
|
||||||
|
|
||||||
u4 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
|
|
||||||
if u4.Variant() != VariantFuture {
|
|
||||||
t.Errorf("Incorrect variant for UUID variant %d: %d", VariantFuture, u4.Variant())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetVariant(t *testing.T) {
|
|
||||||
u := new(UUID)
|
|
||||||
u.SetVariant()
|
|
||||||
|
|
||||||
if u.Variant() != VariantRFC4122 {
|
|
||||||
t.Errorf("Incorrect variant for UUID after u.setVariant(): %d", u.Variant())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromBytes(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
|
|
||||||
u1, err := FromBytes(b1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error parsing UUID from bytes: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u, u1) {
|
|
||||||
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
|
||||||
}
|
|
||||||
|
|
||||||
b2 := []byte{}
|
|
||||||
|
|
||||||
_, err = FromBytes(b2)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error parsing from empty byte slice, got %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalBinary(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
|
|
||||||
b2, err := u.MarshalBinary()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error marshaling UUID: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(b1, b2) {
|
|
||||||
t.Errorf("Marshaled UUID should be %s, got %s", b1, b2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalBinary(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
|
|
||||||
u1 := UUID{}
|
|
||||||
err := u1.UnmarshalBinary(b1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error unmarshaling UUID: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u, u1) {
|
|
||||||
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
|
||||||
}
|
|
||||||
|
|
||||||
b2 := []byte{}
|
|
||||||
u2 := UUID{}
|
|
||||||
|
|
||||||
err = u2.UnmarshalBinary(b2)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error unmarshalling from empty byte slice, got %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromString(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
|
|
||||||
s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
||||||
s2 := "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}"
|
|
||||||
s3 := "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
||||||
|
|
||||||
_, err := FromString("")
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error trying to parse empty string, got %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
u1, err := FromString(s1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error parsing UUID from string: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u, u1) {
|
|
||||||
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
|
||||||
}
|
|
||||||
|
|
||||||
u2, err := FromString(s2)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error parsing UUID from string: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u, u2) {
|
|
||||||
t.Errorf("UUIDs should be equal: %s and %s", u, u2)
|
|
||||||
}
|
|
||||||
|
|
||||||
u3, err := FromString(s3)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error parsing UUID from string: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u, u3) {
|
|
||||||
t.Errorf("UUIDs should be equal: %s and %s", u, u3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromStringShort(t *testing.T) {
|
|
||||||
// Invalid 35-character UUID string
|
|
||||||
s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c"
|
|
||||||
|
|
||||||
for i := len(s1); i >= 0; i-- {
|
|
||||||
_, err := FromString(s1[:i])
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error trying to parse too short string, got %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromStringLong(t *testing.T) {
|
|
||||||
// Invalid 37+ character UUID string
|
|
||||||
s := []string{
|
|
||||||
"6ba7b810-9dad-11d1-80b4-00c04fd430c8=",
|
|
||||||
"6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
|
|
||||||
"{6ba7b810-9dad-11d1-80b4-00c04fd430c8}f",
|
|
||||||
"6ba7b810-9dad-11d1-80b4-00c04fd430c800c04fd430c8",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, str := range s {
|
|
||||||
_, err := FromString(str)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error trying to parse too long string, passed %s", str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromStringInvalid(t *testing.T) {
|
|
||||||
// Invalid UUID string formats
|
|
||||||
s := []string{
|
|
||||||
"6ba7b8109dad11d180b400c04fd430c8",
|
|
||||||
"6ba7b8109dad11d180b400c04fd430c86ba7b8109dad11d180b400c04fd430c8",
|
|
||||||
"urn:uuid:{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
|
|
||||||
"6ba7b8109-dad-11d1-80b4-00c04fd430c8",
|
|
||||||
"6ba7b810-9dad1-1d1-80b4-00c04fd430c8",
|
|
||||||
"6ba7b810-9dad-11d18-0b4-00c04fd430c8",
|
|
||||||
"6ba7b810-9dad-11d1-80b40-0c04fd430c8",
|
|
||||||
"6ba7b810+9dad+11d1+80b4+00c04fd430c8",
|
|
||||||
"6ba7b810-9dad11d180b400c04fd430c8",
|
|
||||||
"6ba7b8109dad-11d180b400c04fd430c8",
|
|
||||||
"6ba7b8109dad11d1-80b400c04fd430c8",
|
|
||||||
"6ba7b8109dad11d180b4-00c04fd430c8",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, str := range s {
|
|
||||||
_, err := FromString(str)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error trying to parse invalid string, passed %s", str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromStringOrNil(t *testing.T) {
|
|
||||||
u := FromStringOrNil("")
|
|
||||||
if u != Nil {
|
|
||||||
t.Errorf("Should return Nil UUID on parse failure, got %s", u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromBytesOrNil(t *testing.T) {
|
|
||||||
b := []byte{}
|
|
||||||
u := FromBytesOrNil(b)
|
|
||||||
if u != Nil {
|
|
||||||
t.Errorf("Should return Nil UUID on parse failure, got %s", u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalText(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
|
||||||
|
|
||||||
b2, err := u.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error marshaling UUID: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(b1, b2) {
|
|
||||||
t.Errorf("Marshaled UUID should be %s, got %s", b1, b2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalText(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
|
||||||
|
|
||||||
u1 := UUID{}
|
|
||||||
err := u1.UnmarshalText(b1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error unmarshaling UUID: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u, u1) {
|
|
||||||
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
|
||||||
}
|
|
||||||
|
|
||||||
b2 := []byte("")
|
|
||||||
u2 := UUID{}
|
|
||||||
|
|
||||||
err = u2.UnmarshalText(b2)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error trying to unmarshal from empty string")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValue(t *testing.T) {
|
|
||||||
u, err := FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error parsing UUID from string: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
val, err := u.Value()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error getting UUID value: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if val != u.String() {
|
|
||||||
t.Errorf("Wrong value returned, should be equal: %s and %s", val, u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueNil(t *testing.T) {
|
|
||||||
u := UUID{}
|
|
||||||
|
|
||||||
val, err := u.Value()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error getting UUID value: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if val != Nil.String() {
|
|
||||||
t.Errorf("Wrong value returned, should be equal to UUID.Nil: %s", val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNullUUIDValueNil(t *testing.T) {
|
|
||||||
u := NullUUID{}
|
|
||||||
|
|
||||||
val, err := u.Value()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error getting UUID value: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if val != nil {
|
|
||||||
t.Errorf("Wrong value returned, should be nil: %s", val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestScanBinary(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
|
|
||||||
u1 := UUID{}
|
|
||||||
err := u1.Scan(b1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error unmarshaling UUID: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u, u1) {
|
|
||||||
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
|
||||||
}
|
|
||||||
|
|
||||||
b2 := []byte{}
|
|
||||||
u2 := UUID{}
|
|
||||||
|
|
||||||
err = u2.Scan(b2)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error unmarshalling from empty byte slice, got %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestScanString(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
||||||
|
|
||||||
u1 := UUID{}
|
|
||||||
err := u1.Scan(s1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error unmarshaling UUID: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u, u1) {
|
|
||||||
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
|
||||||
}
|
|
||||||
|
|
||||||
s2 := ""
|
|
||||||
u2 := UUID{}
|
|
||||||
|
|
||||||
err = u2.Scan(s2)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error trying to unmarshal from empty string")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestScanText(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
|
||||||
|
|
||||||
u1 := UUID{}
|
|
||||||
err := u1.Scan(b1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error unmarshaling UUID: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u, u1) {
|
|
||||||
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
|
||||||
}
|
|
||||||
|
|
||||||
b2 := []byte("")
|
|
||||||
u2 := UUID{}
|
|
||||||
|
|
||||||
err = u2.Scan(b2)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error trying to unmarshal from empty string")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestScanUnsupported(t *testing.T) {
|
|
||||||
u := UUID{}
|
|
||||||
|
|
||||||
err := u.Scan(true)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should return error trying to unmarshal from bool")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestScanNil(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
|
|
||||||
err := u.Scan(nil)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Error UUID shouldn't allow unmarshalling from nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNullUUIDScanValid(t *testing.T) {
|
|
||||||
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
|
||||||
s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
||||||
|
|
||||||
u1 := NullUUID{}
|
|
||||||
err := u1.Scan(s1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error unmarshaling NullUUID: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !u1.Valid {
|
|
||||||
t.Errorf("NullUUID should be valid")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u, u1.UUID) {
|
|
||||||
t.Errorf("UUIDs should be equal: %s and %s", u, u1.UUID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNullUUIDScanNil(t *testing.T) {
|
|
||||||
u := NullUUID{UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}, true}
|
|
||||||
|
|
||||||
err := u.Scan(nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error unmarshaling NullUUID: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Valid {
|
|
||||||
t.Errorf("NullUUID should not be valid")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Equal(u.UUID, Nil) {
|
|
||||||
t.Errorf("NullUUID value should be equal to Nil: %v", u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewV1(t *testing.T) {
|
|
||||||
u := NewV1()
|
|
||||||
|
|
||||||
if u.Version() != 1 {
|
|
||||||
t.Errorf("UUIDv1 generated with incorrect version: %d", u.Version())
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Variant() != VariantRFC4122 {
|
|
||||||
t.Errorf("UUIDv1 generated with incorrect variant: %d", u.Variant())
|
|
||||||
}
|
|
||||||
|
|
||||||
u1 := NewV1()
|
|
||||||
u2 := NewV1()
|
|
||||||
|
|
||||||
if Equal(u1, u2) {
|
|
||||||
t.Errorf("UUIDv1 generated two equal UUIDs: %s and %s", u1, u2)
|
|
||||||
}
|
|
||||||
|
|
||||||
oldFunc := epochFunc
|
|
||||||
epochFunc = func() uint64 { return 0 }
|
|
||||||
|
|
||||||
u3 := NewV1()
|
|
||||||
u4 := NewV1()
|
|
||||||
|
|
||||||
if Equal(u3, u4) {
|
|
||||||
t.Errorf("UUIDv1 generated two equal UUIDs: %s and %s", u3, u4)
|
|
||||||
}
|
|
||||||
|
|
||||||
epochFunc = oldFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewV2(t *testing.T) {
|
|
||||||
u1 := NewV2(DomainPerson)
|
|
||||||
|
|
||||||
if u1.Version() != 2 {
|
|
||||||
t.Errorf("UUIDv2 generated with incorrect version: %d", u1.Version())
|
|
||||||
}
|
|
||||||
|
|
||||||
if u1.Variant() != VariantRFC4122 {
|
|
||||||
t.Errorf("UUIDv2 generated with incorrect variant: %d", u1.Variant())
|
|
||||||
}
|
|
||||||
|
|
||||||
u2 := NewV2(DomainGroup)
|
|
||||||
|
|
||||||
if u2.Version() != 2 {
|
|
||||||
t.Errorf("UUIDv2 generated with incorrect version: %d", u2.Version())
|
|
||||||
}
|
|
||||||
|
|
||||||
if u2.Variant() != VariantRFC4122 {
|
|
||||||
t.Errorf("UUIDv2 generated with incorrect variant: %d", u2.Variant())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewV3(t *testing.T) {
|
|
||||||
u := NewV3(NamespaceDNS, "www.example.com")
|
|
||||||
|
|
||||||
if u.Version() != 3 {
|
|
||||||
t.Errorf("UUIDv3 generated with incorrect version: %d", u.Version())
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Variant() != VariantRFC4122 {
|
|
||||||
t.Errorf("UUIDv3 generated with incorrect variant: %d", u.Variant())
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.String() != "5df41881-3aed-3515-88a7-2f4a814cf09e" {
|
|
||||||
t.Errorf("UUIDv3 generated incorrectly: %s", u.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
u = NewV3(NamespaceDNS, "python.org")
|
|
||||||
|
|
||||||
if u.String() != "6fa459ea-ee8a-3ca4-894e-db77e160355e" {
|
|
||||||
t.Errorf("UUIDv3 generated incorrectly: %s", u.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
u1 := NewV3(NamespaceDNS, "golang.org")
|
|
||||||
u2 := NewV3(NamespaceDNS, "golang.org")
|
|
||||||
if !Equal(u1, u2) {
|
|
||||||
t.Errorf("UUIDv3 generated different UUIDs for same namespace and name: %s and %s", u1, u2)
|
|
||||||
}
|
|
||||||
|
|
||||||
u3 := NewV3(NamespaceDNS, "example.com")
|
|
||||||
if Equal(u1, u3) {
|
|
||||||
t.Errorf("UUIDv3 generated same UUIDs for different names in same namespace: %s and %s", u1, u2)
|
|
||||||
}
|
|
||||||
|
|
||||||
u4 := NewV3(NamespaceURL, "golang.org")
|
|
||||||
if Equal(u1, u4) {
|
|
||||||
t.Errorf("UUIDv3 generated same UUIDs for sane names in different namespaces: %s and %s", u1, u4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewV4(t *testing.T) {
|
|
||||||
u := NewV4()
|
|
||||||
|
|
||||||
if u.Version() != 4 {
|
|
||||||
t.Errorf("UUIDv4 generated with incorrect version: %d", u.Version())
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Variant() != VariantRFC4122 {
|
|
||||||
t.Errorf("UUIDv4 generated with incorrect variant: %d", u.Variant())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewV5(t *testing.T) {
|
|
||||||
u := NewV5(NamespaceDNS, "www.example.com")
|
|
||||||
|
|
||||||
if u.Version() != 5 {
|
|
||||||
t.Errorf("UUIDv5 generated with incorrect version: %d", u.Version())
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Variant() != VariantRFC4122 {
|
|
||||||
t.Errorf("UUIDv5 generated with incorrect variant: %d", u.Variant())
|
|
||||||
}
|
|
||||||
|
|
||||||
u = NewV5(NamespaceDNS, "python.org")
|
|
||||||
|
|
||||||
if u.String() != "886313e1-3b8a-5372-9b90-0c9aee199e5d" {
|
|
||||||
t.Errorf("UUIDv5 generated incorrectly: %s", u.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
u1 := NewV5(NamespaceDNS, "golang.org")
|
|
||||||
u2 := NewV5(NamespaceDNS, "golang.org")
|
|
||||||
if !Equal(u1, u2) {
|
|
||||||
t.Errorf("UUIDv5 generated different UUIDs for same namespace and name: %s and %s", u1, u2)
|
|
||||||
}
|
|
||||||
|
|
||||||
u3 := NewV5(NamespaceDNS, "example.com")
|
|
||||||
if Equal(u1, u3) {
|
|
||||||
t.Errorf("UUIDv5 generated same UUIDs for different names in same namespace: %s and %s", u1, u2)
|
|
||||||
}
|
|
||||||
|
|
||||||
u4 := NewV5(NamespaceURL, "golang.org")
|
|
||||||
if Equal(u1, u4) {
|
|
||||||
t.Errorf("UUIDv3 generated same UUIDs for sane names in different namespaces: %s and %s", u1, u4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-140
@@ -1,140 +0,0 @@
|
|||||||
package cobra
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = fmt.Println
|
|
||||||
var _ = os.Stderr
|
|
||||||
|
|
||||||
func checkOmit(t *testing.T, found, unexpected string) {
|
|
||||||
if strings.Contains(found, unexpected) {
|
|
||||||
t.Errorf("Unexpected response.\nGot: %q\nBut should not have!\n", unexpected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func check(t *testing.T, found, expected string) {
|
|
||||||
if !strings.Contains(found, expected) {
|
|
||||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runShellCheck(s string) error {
|
|
||||||
excluded := []string{
|
|
||||||
"SC2034", // PREFIX appears unused. Verify it or export it.
|
|
||||||
}
|
|
||||||
cmd := exec.Command("shellcheck", "-s", "bash", "-", "-e", strings.Join(excluded, ","))
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
|
|
||||||
stdin, err := cmd.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
defer stdin.Close()
|
|
||||||
stdin.Write([]byte(s))
|
|
||||||
}()
|
|
||||||
|
|
||||||
return cmd.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
// World worst custom function, just keep telling you to enter hello!
|
|
||||||
const (
|
|
||||||
bashCompletionFunc = `__custom_func() {
|
|
||||||
COMPREPLY=( "hello" )
|
|
||||||
}
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBashCompletions(t *testing.T) {
|
|
||||||
c := initializeWithRootCmd()
|
|
||||||
cmdEcho.AddCommand(cmdTimes)
|
|
||||||
c.AddCommand(cmdEcho, cmdPrint, cmdDeprecated, cmdColon)
|
|
||||||
|
|
||||||
// custom completion function
|
|
||||||
c.BashCompletionFunction = bashCompletionFunc
|
|
||||||
|
|
||||||
// required flag
|
|
||||||
c.MarkFlagRequired("introot")
|
|
||||||
|
|
||||||
// valid nouns
|
|
||||||
validArgs := []string{"pod", "node", "service", "replicationcontroller"}
|
|
||||||
c.ValidArgs = validArgs
|
|
||||||
|
|
||||||
// noun aliases
|
|
||||||
argAliases := []string{"pods", "nodes", "services", "replicationcontrollers", "po", "no", "svc", "rc"}
|
|
||||||
c.ArgAliases = argAliases
|
|
||||||
|
|
||||||
// filename
|
|
||||||
var flagval string
|
|
||||||
c.Flags().StringVar(&flagval, "filename", "", "Enter a filename")
|
|
||||||
c.MarkFlagFilename("filename", "json", "yaml", "yml")
|
|
||||||
|
|
||||||
// persistent filename
|
|
||||||
var flagvalPersistent string
|
|
||||||
c.PersistentFlags().StringVar(&flagvalPersistent, "persistent-filename", "", "Enter a filename")
|
|
||||||
c.MarkPersistentFlagFilename("persistent-filename")
|
|
||||||
c.MarkPersistentFlagRequired("persistent-filename")
|
|
||||||
|
|
||||||
// filename extensions
|
|
||||||
var flagvalExt string
|
|
||||||
c.Flags().StringVar(&flagvalExt, "filename-ext", "", "Enter a filename (extension limited)")
|
|
||||||
c.MarkFlagFilename("filename-ext")
|
|
||||||
|
|
||||||
// filename extensions
|
|
||||||
var flagvalCustom string
|
|
||||||
c.Flags().StringVar(&flagvalCustom, "custom", "", "Enter a filename (extension limited)")
|
|
||||||
c.MarkFlagCustom("custom", "__complete_custom")
|
|
||||||
|
|
||||||
// subdirectories in a given directory
|
|
||||||
var flagvalTheme string
|
|
||||||
c.Flags().StringVar(&flagvalTheme, "theme", "", "theme to use (located in /themes/THEMENAME/)")
|
|
||||||
c.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"})
|
|
||||||
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
c.GenBashCompletion(out)
|
|
||||||
str := out.String()
|
|
||||||
|
|
||||||
check(t, str, "_cobra-test")
|
|
||||||
check(t, str, "_cobra-test_echo")
|
|
||||||
check(t, str, "_cobra-test_echo_times")
|
|
||||||
check(t, str, "_cobra-test_print")
|
|
||||||
check(t, str, "_cobra-test_cmd__colon")
|
|
||||||
|
|
||||||
// check for required flags
|
|
||||||
check(t, str, `must_have_one_flag+=("--introot=")`)
|
|
||||||
check(t, str, `must_have_one_flag+=("--persistent-filename=")`)
|
|
||||||
// check for custom completion function
|
|
||||||
check(t, str, `COMPREPLY=( "hello" )`)
|
|
||||||
// check for required nouns
|
|
||||||
check(t, str, `must_have_one_noun+=("pod")`)
|
|
||||||
// check for noun aliases
|
|
||||||
check(t, str, `noun_aliases+=("pods")`)
|
|
||||||
check(t, str, `noun_aliases+=("rc")`)
|
|
||||||
checkOmit(t, str, `must_have_one_noun+=("pods")`)
|
|
||||||
// check for filename extension flags
|
|
||||||
check(t, str, `flags_completion+=("_filedir")`)
|
|
||||||
// check for filename extension flags
|
|
||||||
check(t, str, `flags_completion+=("__handle_filename_extension_flag json|yaml|yml")`)
|
|
||||||
// check for custom flags
|
|
||||||
check(t, str, `flags_completion+=("__complete_custom")`)
|
|
||||||
// check for subdirs_in_dir flags
|
|
||||||
check(t, str, `flags_completion+=("__handle_subdirs_in_dir_flag themes")`)
|
|
||||||
|
|
||||||
checkOmit(t, str, cmdDeprecated.Name())
|
|
||||||
|
|
||||||
// if available, run shellcheck against the script
|
|
||||||
if err := exec.Command("which", "shellcheck").Run(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err := runShellCheck(str)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("shellcheck failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-128
@@ -1,128 +0,0 @@
|
|||||||
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RootCmd.AddCommand(addCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
var pName string
|
|
||||||
|
|
||||||
// initialize Command
|
|
||||||
var addCmd = &cobra.Command{
|
|
||||||
Use: "add [command name]",
|
|
||||||
Aliases: []string{"command"},
|
|
||||||
Short: "Add a command to a Cobra Application",
|
|
||||||
Long: `Add (cobra add) will create a new command, with a license and
|
|
||||||
the appropriate structure for a Cobra-based CLI application,
|
|
||||||
and register it to its parent (default RootCmd).
|
|
||||||
|
|
||||||
If you want your command to be public, pass in the command name
|
|
||||||
with an initial uppercase letter.
|
|
||||||
|
|
||||||
Example: cobra add server -> resulting in a new cmd/server.go
|
|
||||||
`,
|
|
||||||
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
er("add needs a name for the command")
|
|
||||||
}
|
|
||||||
guessProjectPath()
|
|
||||||
createCmdFile(args[0])
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
addCmd.Flags().StringVarP(&pName, "parent", "p", "RootCmd", "name of parent command for this command")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parentName() string {
|
|
||||||
if !strings.HasSuffix(strings.ToLower(pName), "cmd") {
|
|
||||||
return pName + "Cmd"
|
|
||||||
}
|
|
||||||
|
|
||||||
return pName
|
|
||||||
}
|
|
||||||
|
|
||||||
func createCmdFile(cmdName string) {
|
|
||||||
lic := getLicense()
|
|
||||||
|
|
||||||
template := `{{ comment .copyright }}
|
|
||||||
{{ comment .license }}
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// {{.cmdName}}Cmd represents the {{.cmdName}} command
|
|
||||||
var {{ .cmdName }}Cmd = &cobra.Command{
|
|
||||||
Use: "{{ .cmdName }}",
|
|
||||||
Short: "A brief description of your command",
|
|
||||||
Long: ` + "`" + `A longer description that spans multiple lines and likely contains examples
|
|
||||||
and usage of using your command. For example:
|
|
||||||
|
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
|
||||||
This application is a tool to generate the needed files
|
|
||||||
to quickly create a Cobra application.` + "`" + `,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
// TODO: Work your own magic here
|
|
||||||
fmt.Println("{{ .cmdName }} called")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
{{ .parentName }}.AddCommand({{ .cmdName }}Cmd)
|
|
||||||
|
|
||||||
// Here you will define your flags and configuration settings.
|
|
||||||
|
|
||||||
// Cobra supports Persistent Flags which will work for this command
|
|
||||||
// and all subcommands, e.g.:
|
|
||||||
// {{.cmdName}}Cmd.PersistentFlags().String("foo", "", "A help for foo")
|
|
||||||
|
|
||||||
// Cobra supports local flags which will only run when this command
|
|
||||||
// is called directly, e.g.:
|
|
||||||
// {{.cmdName}}Cmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
|
||||||
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
var data map[string]interface{}
|
|
||||||
data = make(map[string]interface{})
|
|
||||||
|
|
||||||
data["copyright"] = copyrightLine()
|
|
||||||
data["license"] = lic.Header
|
|
||||||
data["appName"] = projectName()
|
|
||||||
data["viper"] = viper.GetBool("useViper")
|
|
||||||
data["parentName"] = parentName()
|
|
||||||
data["cmdName"] = cmdName
|
|
||||||
|
|
||||||
err := writeTemplateToFile(filepath.Join(ProjectPath(), guessCmdDir()), cmdName+".go", template, data)
|
|
||||||
if err != nil {
|
|
||||||
er(err)
|
|
||||||
}
|
|
||||||
fmt.Println(cmdName, "created at", filepath.Join(ProjectPath(), guessCmdDir(), cmdName+".go"))
|
|
||||||
}
|
|
||||||
-356
@@ -1,356 +0,0 @@
|
|||||||
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
// var BaseDir = ""
|
|
||||||
// var AppName = ""
|
|
||||||
// var CommandDir = ""
|
|
||||||
|
|
||||||
var funcMap template.FuncMap
|
|
||||||
var projectPath = ""
|
|
||||||
var inputPath = ""
|
|
||||||
var projectBase = ""
|
|
||||||
|
|
||||||
// for testing only
|
|
||||||
var testWd = ""
|
|
||||||
|
|
||||||
var cmdDirs = []string{"cmd", "cmds", "command", "commands"}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
funcMap = template.FuncMap{
|
|
||||||
"comment": commentifyString,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func er(msg interface{}) {
|
|
||||||
fmt.Println("Error:", msg)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a file or directory exists.
|
|
||||||
func exists(path string) (bool, error) {
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProjectPath() string {
|
|
||||||
if projectPath == "" {
|
|
||||||
guessProjectPath()
|
|
||||||
}
|
|
||||||
|
|
||||||
return projectPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrapper of the os package so we can test better
|
|
||||||
func getWd() (string, error) {
|
|
||||||
if testWd == "" {
|
|
||||||
return os.Getwd()
|
|
||||||
}
|
|
||||||
return testWd, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func guessCmdDir() string {
|
|
||||||
guessProjectPath()
|
|
||||||
if b, _ := isEmpty(projectPath); b {
|
|
||||||
return "cmd"
|
|
||||||
}
|
|
||||||
|
|
||||||
files, _ := filepath.Glob(projectPath + string(os.PathSeparator) + "c*")
|
|
||||||
for _, f := range files {
|
|
||||||
for _, c := range cmdDirs {
|
|
||||||
if f == c {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "cmd"
|
|
||||||
}
|
|
||||||
|
|
||||||
func guessImportPath() string {
|
|
||||||
guessProjectPath()
|
|
||||||
|
|
||||||
if !strings.HasPrefix(projectPath, getSrcPath()) {
|
|
||||||
er("Cobra only supports project within $GOPATH")
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.ToSlash(filepath.Clean(strings.TrimPrefix(projectPath, getSrcPath())))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSrcPath() string {
|
|
||||||
return filepath.Join(os.Getenv("GOPATH"), "src") + string(os.PathSeparator)
|
|
||||||
}
|
|
||||||
|
|
||||||
func projectName() string {
|
|
||||||
return filepath.Base(ProjectPath())
|
|
||||||
}
|
|
||||||
|
|
||||||
func guessProjectPath() {
|
|
||||||
// if no path is provided... assume CWD.
|
|
||||||
if inputPath == "" {
|
|
||||||
x, err := getWd()
|
|
||||||
if err != nil {
|
|
||||||
er(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// inspect CWD
|
|
||||||
base := filepath.Base(x)
|
|
||||||
|
|
||||||
// if we are in the cmd directory.. back up
|
|
||||||
for _, c := range cmdDirs {
|
|
||||||
if base == c {
|
|
||||||
projectPath = filepath.Dir(x)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if projectPath == "" {
|
|
||||||
projectPath = filepath.Clean(x)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
srcPath := getSrcPath()
|
|
||||||
// if provided, inspect for logical locations
|
|
||||||
if strings.ContainsRune(inputPath, os.PathSeparator) {
|
|
||||||
if filepath.IsAbs(inputPath) || filepath.HasPrefix(inputPath, string(os.PathSeparator)) {
|
|
||||||
// if Absolute, use it
|
|
||||||
projectPath = filepath.Clean(inputPath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// If not absolute but contains slashes,
|
|
||||||
// assuming it means create it from $GOPATH
|
|
||||||
count := strings.Count(inputPath, string(os.PathSeparator))
|
|
||||||
|
|
||||||
switch count {
|
|
||||||
// If only one directory deep, assume "github.com"
|
|
||||||
case 1:
|
|
||||||
projectPath = filepath.Join(srcPath, "github.com", inputPath)
|
|
||||||
return
|
|
||||||
case 2:
|
|
||||||
projectPath = filepath.Join(srcPath, inputPath)
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
er("Unknown directory")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// hardest case.. just a word.
|
|
||||||
if projectBase == "" {
|
|
||||||
x, err := getWd()
|
|
||||||
if err == nil {
|
|
||||||
projectPath = filepath.Join(x, inputPath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
er(err)
|
|
||||||
} else {
|
|
||||||
projectPath = filepath.Join(srcPath, projectBase, inputPath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isEmpty checks if a given path is empty.
|
|
||||||
func isEmpty(path string) (bool, error) {
|
|
||||||
if b, _ := exists(path); !b {
|
|
||||||
return false, fmt.Errorf("%q path does not exist", path)
|
|
||||||
}
|
|
||||||
fi, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if fi.IsDir() {
|
|
||||||
f, err := os.Open(path)
|
|
||||||
// FIX: Resource leak - f.close() should be called here by defer or is missed
|
|
||||||
// if the err != nil branch is taken.
|
|
||||||
defer f.Close()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
list, _ := f.Readdir(-1)
|
|
||||||
// f.Close() - see bug fix above
|
|
||||||
return len(list) == 0, nil
|
|
||||||
}
|
|
||||||
return fi.Size() == 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// isDir checks if a given path is a directory.
|
|
||||||
func isDir(path string) (bool, error) {
|
|
||||||
fi, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return fi.IsDir(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// dirExists checks if a path exists and is a directory.
|
|
||||||
func dirExists(path string) (bool, error) {
|
|
||||||
fi, err := os.Stat(path)
|
|
||||||
if err == nil && fi.IsDir() {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeTemplateToFile(path string, file string, template string, data interface{}) error {
|
|
||||||
filename := filepath.Join(path, file)
|
|
||||||
|
|
||||||
r, err := templateToReader(template, data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = safeWriteToDisk(filename, r)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeStringToFile(path, file, text string) error {
|
|
||||||
filename := filepath.Join(path, file)
|
|
||||||
|
|
||||||
r := strings.NewReader(text)
|
|
||||||
err := safeWriteToDisk(filename, r)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func templateToReader(tpl string, data interface{}) (io.Reader, error) {
|
|
||||||
tmpl := template.New("")
|
|
||||||
tmpl.Funcs(funcMap)
|
|
||||||
tmpl, err := tmpl.Parse(tpl)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
err = tmpl.Execute(buf, data)
|
|
||||||
|
|
||||||
return buf, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same as WriteToDisk but checks to see if file/directory already exists.
|
|
||||||
func safeWriteToDisk(inpath string, r io.Reader) (err error) {
|
|
||||||
dir, _ := filepath.Split(inpath)
|
|
||||||
ospath := filepath.FromSlash(dir)
|
|
||||||
|
|
||||||
if ospath != "" {
|
|
||||||
err = os.MkdirAll(ospath, 0777) // rwx, rw, r
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ex, err := exists(inpath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ex {
|
|
||||||
return fmt.Errorf("%v already exists", inpath)
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := os.Create(inpath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(file, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLicense() License {
|
|
||||||
l := whichLicense()
|
|
||||||
if l != "" {
|
|
||||||
if x, ok := Licenses[l]; ok {
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Licenses["apache"]
|
|
||||||
}
|
|
||||||
|
|
||||||
func whichLicense() string {
|
|
||||||
// if explicitly flagged, use that
|
|
||||||
if userLicense != "" {
|
|
||||||
return matchLicense(userLicense)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if already present in the project, use that
|
|
||||||
// TODO: Inspect project for existing license
|
|
||||||
|
|
||||||
// default to viper's setting
|
|
||||||
|
|
||||||
if viper.IsSet("license.header") || viper.IsSet("license.text") {
|
|
||||||
if custom, ok := Licenses["custom"]; ok {
|
|
||||||
custom.Header = viper.GetString("license.header")
|
|
||||||
custom.Text = viper.GetString("license.text")
|
|
||||||
Licenses["custom"] = custom
|
|
||||||
return "custom"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchLicense(viper.GetString("license"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyrightLine() string {
|
|
||||||
author := viper.GetString("author")
|
|
||||||
year := time.Now().Format("2006")
|
|
||||||
|
|
||||||
return "Copyright © " + year + " " + author
|
|
||||||
}
|
|
||||||
|
|
||||||
func commentifyString(in string) string {
|
|
||||||
var newlines []string
|
|
||||||
lines := strings.Split(in, "\n")
|
|
||||||
for _, x := range lines {
|
|
||||||
if !strings.HasPrefix(x, "//") {
|
|
||||||
if x != "" {
|
|
||||||
newlines = append(newlines, "// "+x)
|
|
||||||
} else {
|
|
||||||
newlines = append(newlines, "//")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newlines = append(newlines, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(newlines, "\n")
|
|
||||||
}
|
|
||||||
-40
@@ -1,40 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = fmt.Println
|
|
||||||
var _ = os.Stderr
|
|
||||||
|
|
||||||
func checkGuess(t *testing.T, wd, input, expected string) {
|
|
||||||
testWd = wd
|
|
||||||
inputPath = input
|
|
||||||
guessProjectPath()
|
|
||||||
|
|
||||||
if projectPath != expected {
|
|
||||||
t.Errorf("Unexpected Project Path. \n Got: %q\nExpected: %q\n", projectPath, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
func reset() {
|
|
||||||
testWd = ""
|
|
||||||
inputPath = ""
|
|
||||||
projectPath = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProjectPath(t *testing.T) {
|
|
||||||
checkGuess(t, "", filepath.Join("github.com", "spf13", "hugo"), filepath.Join(getSrcPath(), "github.com", "spf13", "hugo"))
|
|
||||||
checkGuess(t, "", filepath.Join("spf13", "hugo"), filepath.Join(getSrcPath(), "github.com", "spf13", "hugo"))
|
|
||||||
checkGuess(t, "", filepath.Join("/", "bar", "foo"), filepath.Join("/", "bar", "foo"))
|
|
||||||
checkGuess(t, "/bar/foo", "baz", filepath.Join("/", "bar", "foo", "baz"))
|
|
||||||
checkGuess(t, "/bar/foo/cmd", "", filepath.Join("/", "bar", "foo"))
|
|
||||||
checkGuess(t, "/bar/foo/command", "", filepath.Join("/", "bar", "foo"))
|
|
||||||
checkGuess(t, "/bar/foo/commands", "", filepath.Join("/", "bar", "foo"))
|
|
||||||
checkGuess(t, "github.com/spf13/hugo/../hugo", "", filepath.Join("github.com", "spf13", "hugo"))
|
|
||||||
}
|
|
||||||
-245
@@ -1,245 +0,0 @@
|
|||||||
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RootCmd.AddCommand(initCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize Command
|
|
||||||
var initCmd = &cobra.Command{
|
|
||||||
Use: "init [name]",
|
|
||||||
Aliases: []string{"initialize", "initialise", "create"},
|
|
||||||
Short: "Initialize a Cobra Application",
|
|
||||||
Long: `Initialize (cobra init) will create a new application, with a license
|
|
||||||
and the appropriate structure for a Cobra-based CLI application.
|
|
||||||
|
|
||||||
* If a name is provided, it will be created in the current directory;
|
|
||||||
* If no name is provided, the current directory will be assumed;
|
|
||||||
* If a relative path is provided, it will be created inside $GOPATH
|
|
||||||
(e.g. github.com/spf13/hugo);
|
|
||||||
* If an absolute path is provided, it will be created;
|
|
||||||
* If the directory already exists but is empty, it will be used.
|
|
||||||
|
|
||||||
Init will not use an existing directory with contents.`,
|
|
||||||
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
switch len(args) {
|
|
||||||
case 0:
|
|
||||||
inputPath = ""
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
inputPath = args[0]
|
|
||||||
|
|
||||||
default:
|
|
||||||
er("init doesn't support more than 1 parameter")
|
|
||||||
}
|
|
||||||
guessProjectPath()
|
|
||||||
initializePath(projectPath)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func initializePath(path string) {
|
|
||||||
b, err := exists(path)
|
|
||||||
if err != nil {
|
|
||||||
er(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !b { // If path doesn't yet exist, create it
|
|
||||||
err := os.MkdirAll(path, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
er(err)
|
|
||||||
}
|
|
||||||
} else { // If path exists and is not empty don't use it
|
|
||||||
empty, err := exists(path)
|
|
||||||
if err != nil {
|
|
||||||
er(err)
|
|
||||||
}
|
|
||||||
if !empty {
|
|
||||||
er("Cobra will not create a new project in a non empty directory")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We have a directory and it's empty.. Time to initialize it.
|
|
||||||
|
|
||||||
createLicenseFile()
|
|
||||||
createMainFile()
|
|
||||||
createRootCmdFile()
|
|
||||||
}
|
|
||||||
|
|
||||||
func createLicenseFile() {
|
|
||||||
lic := getLicense()
|
|
||||||
|
|
||||||
// Don't bother writing a LICENSE file if there is no text.
|
|
||||||
if lic.Text != "" {
|
|
||||||
data := make(map[string]interface{})
|
|
||||||
|
|
||||||
// Try to remove the email address, if any
|
|
||||||
data["copyright"] = strings.Split(copyrightLine(), " <")[0]
|
|
||||||
|
|
||||||
data["appName"] = projectName()
|
|
||||||
|
|
||||||
// Generate license template from text and data.
|
|
||||||
r, _ := templateToReader(lic.Text, data)
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
buf.ReadFrom(r)
|
|
||||||
|
|
||||||
err := writeTemplateToFile(ProjectPath(), "LICENSE", buf.String(), data)
|
|
||||||
_ = err
|
|
||||||
// if err != nil {
|
|
||||||
// er(err)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createMainFile() {
|
|
||||||
lic := getLicense()
|
|
||||||
|
|
||||||
template := `{{ comment .copyright }}
|
|
||||||
{{if .license}}{{ comment .license }}
|
|
||||||
{{end}}
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "{{ .importpath }}"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cmd.Execute()
|
|
||||||
}
|
|
||||||
`
|
|
||||||
data := make(map[string]interface{})
|
|
||||||
|
|
||||||
data["copyright"] = copyrightLine()
|
|
||||||
data["appName"] = projectName()
|
|
||||||
|
|
||||||
// Generate license template from header and data.
|
|
||||||
r, _ := templateToReader(lic.Header, data)
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
buf.ReadFrom(r)
|
|
||||||
data["license"] = buf.String()
|
|
||||||
|
|
||||||
data["importpath"] = guessImportPath() + "/" + guessCmdDir()
|
|
||||||
|
|
||||||
err := writeTemplateToFile(ProjectPath(), "main.go", template, data)
|
|
||||||
_ = err
|
|
||||||
// if err != nil {
|
|
||||||
// er(err)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
func createRootCmdFile() {
|
|
||||||
lic := getLicense()
|
|
||||||
|
|
||||||
template := `{{ comment .copyright }}
|
|
||||||
{{if .license}}{{ comment .license }}
|
|
||||||
{{end}}
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
{{ if .viper }} "github.com/spf13/viper"
|
|
||||||
{{ end }})
|
|
||||||
{{if .viper}}
|
|
||||||
var cfgFile string
|
|
||||||
{{ end }}
|
|
||||||
// RootCmd represents the base command when called without any subcommands
|
|
||||||
var RootCmd = &cobra.Command{
|
|
||||||
Use: "{{ .appName }}",
|
|
||||||
Short: "A brief description of your application",
|
|
||||||
Long: ` + "`" + `A longer description that spans multiple lines and likely contains
|
|
||||||
examples and usage of using your application. For example:
|
|
||||||
|
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
|
||||||
This application is a tool to generate the needed files
|
|
||||||
to quickly create a Cobra application.` + "`" + `,
|
|
||||||
// Uncomment the following line if your bare application
|
|
||||||
// has an action associated with it:
|
|
||||||
// Run: func(cmd *cobra.Command, args []string) { },
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute adds all child commands to the root command sets flags appropriately.
|
|
||||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
|
||||||
func Execute() {
|
|
||||||
if err := RootCmd.Execute(); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
{{ if .viper }} cobra.OnInitialize(initConfig)
|
|
||||||
|
|
||||||
{{ end }} // Here you will define your flags and configuration settings.
|
|
||||||
// Cobra supports Persistent Flags, which, if defined here,
|
|
||||||
// will be global for your application.
|
|
||||||
{{ if .viper }}
|
|
||||||
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.{{ .appName }}.yaml)")
|
|
||||||
{{ else }}
|
|
||||||
// RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.{{ .appName }}.yaml)")
|
|
||||||
{{ end }} // Cobra also supports local flags, which will only run
|
|
||||||
// when this action is called directly.
|
|
||||||
RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
|
||||||
}
|
|
||||||
{{ if .viper }}
|
|
||||||
// initConfig reads in config file and ENV variables if set.
|
|
||||||
func initConfig() {
|
|
||||||
if cfgFile != "" { // enable ability to specify config file via flag
|
|
||||||
viper.SetConfigFile(cfgFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
viper.SetConfigName(".{{ .appName }}") // name of config file (without extension)
|
|
||||||
viper.AddConfigPath("$HOME") // adding home directory as first search path
|
|
||||||
viper.AutomaticEnv() // read in environment variables that match
|
|
||||||
|
|
||||||
// If a config file is found, read it in.
|
|
||||||
if err := viper.ReadInConfig(); err == nil {
|
|
||||||
fmt.Println("Using config file:", viper.ConfigFileUsed())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{{ end }}`
|
|
||||||
|
|
||||||
data := make(map[string]interface{})
|
|
||||||
|
|
||||||
data["copyright"] = copyrightLine()
|
|
||||||
data["appName"] = projectName()
|
|
||||||
|
|
||||||
// Generate license template from header and data.
|
|
||||||
r, _ := templateToReader(lic.Header, data)
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
buf.ReadFrom(r)
|
|
||||||
data["license"] = buf.String()
|
|
||||||
|
|
||||||
data["viper"] = viper.GetBool("useViper")
|
|
||||||
|
|
||||||
err := writeTemplateToFile(ProjectPath()+string(os.PathSeparator)+guessCmdDir(), "root.go", template, data)
|
|
||||||
if err != nil {
|
|
||||||
er(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Your Cobra application is ready at")
|
|
||||||
fmt.Println(ProjectPath())
|
|
||||||
fmt.Println("Give it a try by going there and running `go run main.go`")
|
|
||||||
fmt.Println("Add commands to it by running `cobra add [cmdname]`")
|
|
||||||
}
|
|
||||||
-1384
File diff suppressed because it is too large
Load Diff
-72
@@ -1,72 +0,0 @@
|
|||||||
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cfgFile string
|
|
||||||
var userLicense string
|
|
||||||
|
|
||||||
// RootCmd represents the base command when called without any subcommands
|
|
||||||
var RootCmd = &cobra.Command{
|
|
||||||
Use: "cobra",
|
|
||||||
Short: "A generator for Cobra based Applications",
|
|
||||||
Long: `Cobra is a CLI library for Go that empowers applications.
|
|
||||||
This application is a tool to generate the needed files
|
|
||||||
to quickly create a Cobra application.`,
|
|
||||||
}
|
|
||||||
|
|
||||||
//Execute adds all child commands to the root command sets flags appropriately.
|
|
||||||
func Execute() {
|
|
||||||
if err := RootCmd.Execute(); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
cobra.OnInitialize(initConfig)
|
|
||||||
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
|
|
||||||
RootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "", "base project directory, e.g. github.com/spf13/")
|
|
||||||
RootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "Author name for copyright attribution")
|
|
||||||
RootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `license` in config)")
|
|
||||||
RootCmd.PersistentFlags().Bool("viper", true, "Use Viper for configuration")
|
|
||||||
viper.BindPFlag("author", RootCmd.PersistentFlags().Lookup("author"))
|
|
||||||
viper.BindPFlag("projectbase", RootCmd.PersistentFlags().Lookup("projectbase"))
|
|
||||||
viper.BindPFlag("useViper", RootCmd.PersistentFlags().Lookup("viper"))
|
|
||||||
viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
|
|
||||||
viper.SetDefault("license", "apache")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read in config file and ENV variables if set.
|
|
||||||
func initConfig() {
|
|
||||||
if cfgFile != "" { // enable ability to specify config file via flag
|
|
||||||
viper.SetConfigFile(cfgFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
viper.SetConfigName(".cobra") // name of config file (without extension)
|
|
||||||
viper.AddConfigPath("$HOME") // adding home directory as first search path
|
|
||||||
viper.AutomaticEnv() // read in environment variables that match
|
|
||||||
|
|
||||||
// If a config file is found, read it in.
|
|
||||||
if err := viper.ReadInConfig(); err == nil {
|
|
||||||
fmt.Println("Using config file:", viper.ConfigFileUsed())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "github.com/spf13/cobra/cobra/cmd"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cmd.Execute()
|
|
||||||
}
|
|
||||||
-1188
File diff suppressed because it is too large
Load Diff
-135
@@ -1,135 +0,0 @@
|
|||||||
package cobra
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// test to ensure hidden commands run as intended
|
|
||||||
func TestHiddenCommandExecutes(t *testing.T) {
|
|
||||||
|
|
||||||
// ensure that outs does not already equal what the command will be setting it
|
|
||||||
// to, if it did this test would not actually be testing anything...
|
|
||||||
if outs == "hidden" {
|
|
||||||
t.Errorf("outs should NOT EQUAL hidden")
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdHidden.Execute()
|
|
||||||
|
|
||||||
// upon running the command, the value of outs should now be 'hidden'
|
|
||||||
if outs != "hidden" {
|
|
||||||
t.Errorf("Hidden command failed to run!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test to ensure hidden commands do not show up in usage/help text
|
|
||||||
func TestHiddenCommandIsHidden(t *testing.T) {
|
|
||||||
if cmdHidden.IsAvailableCommand() {
|
|
||||||
t.Errorf("Hidden command found!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStripFlags(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
input []string
|
|
||||||
output []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
[]string{"foo", "bar"},
|
|
||||||
[]string{"foo", "bar"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"foo", "--bar", "-b"},
|
|
||||||
[]string{"foo"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"-b", "foo", "--bar", "bar"},
|
|
||||||
[]string{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"-i10", "echo"},
|
|
||||||
[]string{"echo"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"-i=10", "echo"},
|
|
||||||
[]string{"echo"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"--int=100", "echo"},
|
|
||||||
[]string{"echo"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"-ib", "echo", "-bfoo", "baz"},
|
|
||||||
[]string{"echo", "baz"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"-i=baz", "bar", "-i", "foo", "blah"},
|
|
||||||
[]string{"bar", "blah"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"--int=baz", "-bbar", "-i", "foo", "blah"},
|
|
||||||
[]string{"blah"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"--cat", "bar", "-i", "foo", "blah"},
|
|
||||||
[]string{"bar", "blah"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"-c", "bar", "-i", "foo", "blah"},
|
|
||||||
[]string{"bar", "blah"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"--persist", "bar"},
|
|
||||||
[]string{"bar"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]string{"-p", "bar"},
|
|
||||||
[]string{"bar"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdPrint := &Command{
|
|
||||||
Use: "print [string to print]",
|
|
||||||
Short: "Print anything to the screen",
|
|
||||||
Long: `an utterly useless command for testing.`,
|
|
||||||
Run: func(cmd *Command, args []string) {
|
|
||||||
tp = args
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var flagi int
|
|
||||||
var flagstr string
|
|
||||||
var flagbool bool
|
|
||||||
cmdPrint.PersistentFlags().BoolVarP(&flagbool, "persist", "p", false, "help for persistent one")
|
|
||||||
cmdPrint.Flags().IntVarP(&flagi, "int", "i", 345, "help message for flag int")
|
|
||||||
cmdPrint.Flags().StringVarP(&flagstr, "bar", "b", "bar", "help message for flag string")
|
|
||||||
cmdPrint.Flags().BoolVarP(&flagbool, "cat", "c", false, "help message for flag bool")
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
output := stripFlags(test.input, cmdPrint)
|
|
||||||
if !reflect.DeepEqual(test.output, output) {
|
|
||||||
t.Errorf("expected: %v, got: %v", test.output, output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_DisableFlagParsing(t *testing.T) {
|
|
||||||
as := []string{"-v", "-race", "-file", "foo.go"}
|
|
||||||
targs := []string{}
|
|
||||||
cmdPrint := &Command{
|
|
||||||
DisableFlagParsing: true,
|
|
||||||
Run: func(cmd *Command, args []string) {
|
|
||||||
targs = args
|
|
||||||
},
|
|
||||||
}
|
|
||||||
osargs := []string{"cmd"}
|
|
||||||
os.Args = append(osargs, as...)
|
|
||||||
err := cmdPrint.Execute()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(as, targs) {
|
|
||||||
t.Errorf("expected: %v, got: %v", as, targs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-145
@@ -1,145 +0,0 @@
|
|||||||
package doc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
var flagb1, flagb2, flagb3, flagbr, flagbp bool
|
|
||||||
var flags1, flags2a, flags2b, flags3 string
|
|
||||||
var flagi1, flagi2, flagi3, flagir int
|
|
||||||
|
|
||||||
const strtwoParentHelp = "help message for parent flag strtwo"
|
|
||||||
const strtwoChildHelp = "help message for child flag strtwo"
|
|
||||||
|
|
||||||
var cmdEcho = &cobra.Command{
|
|
||||||
Use: "echo [string to echo]",
|
|
||||||
Aliases: []string{"say"},
|
|
||||||
Short: "Echo anything to the screen",
|
|
||||||
Long: `an utterly useless command for testing.`,
|
|
||||||
Example: "Just run cobra-test echo",
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdEchoSub = &cobra.Command{
|
|
||||||
Use: "echosub [string to print]",
|
|
||||||
Short: "second sub command for echo",
|
|
||||||
Long: `an absolutely utterly useless command for testing gendocs!.`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdDeprecated = &cobra.Command{
|
|
||||||
Use: "deprecated [can't do anything here]",
|
|
||||||
Short: "A command which is deprecated",
|
|
||||||
Long: `an absolutely utterly useless command for testing deprecation!.`,
|
|
||||||
Deprecated: "Please use echo instead",
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdTimes = &cobra.Command{
|
|
||||||
Use: "times [# times] [string to echo]",
|
|
||||||
SuggestFor: []string{"counts"},
|
|
||||||
Short: "Echo anything to the screen more times",
|
|
||||||
Long: `a slightly useless command for testing.`,
|
|
||||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdPrint = &cobra.Command{
|
|
||||||
Use: "print [string to print]",
|
|
||||||
Short: "Print anything to the screen",
|
|
||||||
Long: `an absolutely utterly useless command for testing.`,
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdRootNoRun = &cobra.Command{
|
|
||||||
Use: "cobra-test",
|
|
||||||
Short: "The root can run its own function",
|
|
||||||
Long: "The root description for help",
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdRootSameName = &cobra.Command{
|
|
||||||
Use: "print",
|
|
||||||
Short: "Root with the same name as a subcommand",
|
|
||||||
Long: "The root description for help",
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdRootWithRun = &cobra.Command{
|
|
||||||
Use: "cobra-test",
|
|
||||||
Short: "The root can run its own function",
|
|
||||||
Long: "The root description for help",
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdSubNoRun = &cobra.Command{
|
|
||||||
Use: "subnorun",
|
|
||||||
Short: "A subcommand without a Run function",
|
|
||||||
Long: "A long output about a subcommand without a Run function",
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdVersion1 = &cobra.Command{
|
|
||||||
Use: "version",
|
|
||||||
Short: "Print the version number",
|
|
||||||
Long: `First version of the version command`,
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdVersion2 = &cobra.Command{
|
|
||||||
Use: "version",
|
|
||||||
Short: "Print the version number",
|
|
||||||
Long: `Second version of the version command`,
|
|
||||||
}
|
|
||||||
|
|
||||||
func flagInit() {
|
|
||||||
cmdEcho.ResetFlags()
|
|
||||||
cmdPrint.ResetFlags()
|
|
||||||
cmdTimes.ResetFlags()
|
|
||||||
cmdRootNoRun.ResetFlags()
|
|
||||||
cmdRootSameName.ResetFlags()
|
|
||||||
cmdRootWithRun.ResetFlags()
|
|
||||||
cmdSubNoRun.ResetFlags()
|
|
||||||
cmdRootNoRun.PersistentFlags().StringVarP(&flags2a, "strtwo", "t", "two", strtwoParentHelp)
|
|
||||||
cmdEcho.Flags().IntVarP(&flagi1, "intone", "i", 123, "help message for flag intone")
|
|
||||||
cmdTimes.Flags().IntVarP(&flagi2, "inttwo", "j", 234, "help message for flag inttwo")
|
|
||||||
cmdPrint.Flags().IntVarP(&flagi3, "intthree", "i", 345, "help message for flag intthree")
|
|
||||||
cmdEcho.PersistentFlags().StringVarP(&flags1, "strone", "s", "one", "help message for flag strone")
|
|
||||||
cmdEcho.PersistentFlags().BoolVarP(&flagbp, "persistentbool", "p", false, "help message for flag persistentbool")
|
|
||||||
cmdTimes.PersistentFlags().StringVarP(&flags2b, "strtwo", "t", "2", strtwoChildHelp)
|
|
||||||
cmdPrint.PersistentFlags().StringVarP(&flags3, "strthree", "s", "three", "help message for flag strthree")
|
|
||||||
cmdEcho.Flags().BoolVarP(&flagb1, "boolone", "b", true, "help message for flag boolone")
|
|
||||||
cmdTimes.Flags().BoolVarP(&flagb2, "booltwo", "c", false, "help message for flag booltwo")
|
|
||||||
cmdPrint.Flags().BoolVarP(&flagb3, "boolthree", "b", true, "help message for flag boolthree")
|
|
||||||
cmdVersion1.ResetFlags()
|
|
||||||
cmdVersion2.ResetFlags()
|
|
||||||
}
|
|
||||||
|
|
||||||
func initializeWithRootCmd() *cobra.Command {
|
|
||||||
cmdRootWithRun.ResetCommands()
|
|
||||||
flagInit()
|
|
||||||
cmdRootWithRun.Flags().BoolVarP(&flagbr, "boolroot", "b", false, "help message for flag boolroot")
|
|
||||||
cmdRootWithRun.Flags().IntVarP(&flagir, "introot", "i", 321, "help message for flag introot")
|
|
||||||
return cmdRootWithRun
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkStringContains(t *testing.T, found, expected string) {
|
|
||||||
if !strings.Contains(found, expected) {
|
|
||||||
logErr(t, found, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkStringOmits(t *testing.T, found, expected string) {
|
|
||||||
if strings.Contains(found, expected) {
|
|
||||||
logErr(t, found, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func logErr(t *testing.T, found, expected string) {
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
|
|
||||||
_, _, line, ok := runtime.Caller(2)
|
|
||||||
if ok {
|
|
||||||
fmt.Fprintf(out, "Line: %d ", line)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(out, "Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
|
||||||
t.Errorf(out.String())
|
|
||||||
}
|
|
||||||
-218
@@ -1,218 +0,0 @@
|
|||||||
// Copyright 2015 Red Hat Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package doc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
mangen "github.com/cpuguy83/go-md2man/md2man"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GenManTree will generate a man page for this command and all descendants
|
|
||||||
// in the directory given. The header may be nil. This function may not work
|
|
||||||
// correctly if your command names have - in them. If you have `cmd` with two
|
|
||||||
// subcmds, `sub` and `sub-third`. And `sub` has a subcommand called `third`
|
|
||||||
// it is undefined which help output will be in the file `cmd-sub-third.1`.
|
|
||||||
func GenManTree(cmd *cobra.Command, header *GenManHeader, dir string) error {
|
|
||||||
if header == nil {
|
|
||||||
header = &GenManHeader{}
|
|
||||||
}
|
|
||||||
for _, c := range cmd.Commands() {
|
|
||||||
if !c.IsAvailableCommand() || c.IsHelpCommand() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := GenManTree(c, header, dir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
needToResetTitle := header.Title == ""
|
|
||||||
|
|
||||||
basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".1"
|
|
||||||
filename := filepath.Join(dir, basename)
|
|
||||||
f, err := os.Create(filename)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
if err := GenMan(cmd, header, f); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if needToResetTitle {
|
|
||||||
header.Title = ""
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenManHeader is a lot like the .TH header at the start of man pages. These
|
|
||||||
// include the title, section, date, source, and manual. We will use the
|
|
||||||
// current time if Date if unset and will use "Auto generated by spf13/cobra"
|
|
||||||
// if the Source is unset.
|
|
||||||
type GenManHeader struct {
|
|
||||||
Title string
|
|
||||||
Section string
|
|
||||||
Date *time.Time
|
|
||||||
date string
|
|
||||||
Source string
|
|
||||||
Manual string
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenMan will generate a man page for the given command and write it to
|
|
||||||
// w. The header argument may be nil, however obviously w may not.
|
|
||||||
func GenMan(cmd *cobra.Command, header *GenManHeader, w io.Writer) error {
|
|
||||||
if header == nil {
|
|
||||||
header = &GenManHeader{}
|
|
||||||
}
|
|
||||||
b := genMan(cmd, header)
|
|
||||||
final := mangen.Render(b)
|
|
||||||
_, err := w.Write(final)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func fillHeader(header *GenManHeader, name string) {
|
|
||||||
if header.Title == "" {
|
|
||||||
header.Title = strings.ToUpper(strings.Replace(name, " ", "\\-", -1))
|
|
||||||
}
|
|
||||||
if header.Section == "" {
|
|
||||||
header.Section = "1"
|
|
||||||
}
|
|
||||||
if header.Date == nil {
|
|
||||||
now := time.Now()
|
|
||||||
header.Date = &now
|
|
||||||
}
|
|
||||||
header.date = (*header.Date).Format("Jan 2006")
|
|
||||||
if header.Source == "" {
|
|
||||||
header.Source = "Auto generated by spf13/cobra"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func manPreamble(out io.Writer, header *GenManHeader, name, short, long string) {
|
|
||||||
dashName := strings.Replace(name, " ", "-", -1)
|
|
||||||
fmt.Fprintf(out, `%% %s(%s)%s
|
|
||||||
%% %s
|
|
||||||
%% %s
|
|
||||||
# NAME
|
|
||||||
`, header.Title, header.Section, header.date, header.Source, header.Manual)
|
|
||||||
fmt.Fprintf(out, "%s \\- %s\n\n", dashName, short)
|
|
||||||
fmt.Fprintf(out, "# SYNOPSIS\n")
|
|
||||||
fmt.Fprintf(out, "**%s** [OPTIONS]\n\n", name)
|
|
||||||
fmt.Fprintf(out, "# DESCRIPTION\n")
|
|
||||||
fmt.Fprintf(out, "%s\n\n", long)
|
|
||||||
}
|
|
||||||
|
|
||||||
func manPrintFlags(out io.Writer, flags *pflag.FlagSet) {
|
|
||||||
flags.VisitAll(func(flag *pflag.Flag) {
|
|
||||||
if len(flag.Deprecated) > 0 || flag.Hidden {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
format := ""
|
|
||||||
if len(flag.Shorthand) > 0 {
|
|
||||||
format = "**-%s**, **--%s**"
|
|
||||||
} else {
|
|
||||||
format = "%s**--%s**"
|
|
||||||
}
|
|
||||||
if len(flag.NoOptDefVal) > 0 {
|
|
||||||
format = format + "["
|
|
||||||
}
|
|
||||||
if flag.Value.Type() == "string" {
|
|
||||||
// put quotes on the value
|
|
||||||
format = format + "=%q"
|
|
||||||
} else {
|
|
||||||
format = format + "=%s"
|
|
||||||
}
|
|
||||||
if len(flag.NoOptDefVal) > 0 {
|
|
||||||
format = format + "]"
|
|
||||||
}
|
|
||||||
format = format + "\n\t%s\n\n"
|
|
||||||
fmt.Fprintf(out, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func manPrintOptions(out io.Writer, command *cobra.Command) {
|
|
||||||
flags := command.NonInheritedFlags()
|
|
||||||
if flags.HasFlags() {
|
|
||||||
fmt.Fprintf(out, "# OPTIONS\n")
|
|
||||||
manPrintFlags(out, flags)
|
|
||||||
fmt.Fprintf(out, "\n")
|
|
||||||
}
|
|
||||||
flags = command.InheritedFlags()
|
|
||||||
if flags.HasFlags() {
|
|
||||||
fmt.Fprintf(out, "# OPTIONS INHERITED FROM PARENT COMMANDS\n")
|
|
||||||
manPrintFlags(out, flags)
|
|
||||||
fmt.Fprintf(out, "\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func genMan(cmd *cobra.Command, header *GenManHeader) []byte {
|
|
||||||
// something like `rootcmd subcmd1 subcmd2`
|
|
||||||
commandName := cmd.CommandPath()
|
|
||||||
// something like `rootcmd-subcmd1-subcmd2`
|
|
||||||
dashCommandName := strings.Replace(commandName, " ", "-", -1)
|
|
||||||
|
|
||||||
fillHeader(header, commandName)
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
|
|
||||||
short := cmd.Short
|
|
||||||
long := cmd.Long
|
|
||||||
if len(long) == 0 {
|
|
||||||
long = short
|
|
||||||
}
|
|
||||||
|
|
||||||
manPreamble(buf, header, commandName, short, long)
|
|
||||||
manPrintOptions(buf, cmd)
|
|
||||||
if len(cmd.Example) > 0 {
|
|
||||||
fmt.Fprintf(buf, "# EXAMPLE\n")
|
|
||||||
fmt.Fprintf(buf, "```\n%s\n```\n", cmd.Example)
|
|
||||||
}
|
|
||||||
if hasSeeAlso(cmd) {
|
|
||||||
fmt.Fprintf(buf, "# SEE ALSO\n")
|
|
||||||
seealsos := make([]string, 0)
|
|
||||||
if cmd.HasParent() {
|
|
||||||
parentPath := cmd.Parent().CommandPath()
|
|
||||||
dashParentPath := strings.Replace(parentPath, " ", "-", -1)
|
|
||||||
seealso := fmt.Sprintf("**%s(%s)**", dashParentPath, header.Section)
|
|
||||||
seealsos = append(seealsos, seealso)
|
|
||||||
cmd.VisitParents(func(c *cobra.Command) {
|
|
||||||
if c.DisableAutoGenTag {
|
|
||||||
cmd.DisableAutoGenTag = c.DisableAutoGenTag
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
children := cmd.Commands()
|
|
||||||
sort.Sort(byName(children))
|
|
||||||
for _, c := range children {
|
|
||||||
if !c.IsAvailableCommand() || c.IsHelpCommand() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seealso := fmt.Sprintf("**%s-%s(%s)**", dashCommandName, c.Name(), header.Section)
|
|
||||||
seealsos = append(seealsos, seealso)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(buf, "%s\n", strings.Join(seealsos, ", "))
|
|
||||||
}
|
|
||||||
if !cmd.DisableAutoGenTag {
|
|
||||||
fmt.Fprintf(buf, "# HISTORY\n%s Auto generated by spf13/cobra\n", header.Date.Format("2-Jan-2006"))
|
|
||||||
}
|
|
||||||
return buf.Bytes()
|
|
||||||
}
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
# Generating Man Pages For Your Own cobra.Command
|
|
||||||
|
|
||||||
Generating man pages from a cobra command is incredibly easy. An example is as follows:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/cobra/doc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "test",
|
|
||||||
Short: "my test program",
|
|
||||||
}
|
|
||||||
header := &cobra.GenManHeader{
|
|
||||||
Title: "MINE",
|
|
||||||
Section: "3",
|
|
||||||
}
|
|
||||||
doc.GenManTree(cmd, header, "/tmp")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
That will get you a man page `/tmp/test.1`
|
|
||||||
-161
@@ -1,161 +0,0 @@
|
|||||||
package doc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = fmt.Println
|
|
||||||
var _ = os.Stderr
|
|
||||||
|
|
||||||
func translate(in string) string {
|
|
||||||
return strings.Replace(in, "-", "\\-", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenManDoc(t *testing.T) {
|
|
||||||
c := initializeWithRootCmd()
|
|
||||||
// Need two commands to run the command alphabetical sort
|
|
||||||
cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated)
|
|
||||||
c.AddCommand(cmdPrint, cmdEcho)
|
|
||||||
cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp)
|
|
||||||
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
|
|
||||||
header := &GenManHeader{
|
|
||||||
Title: "Project",
|
|
||||||
Section: "2",
|
|
||||||
}
|
|
||||||
// We generate on a subcommand so we have both subcommands and parents
|
|
||||||
if err := GenMan(cmdEcho, header, out); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
found := out.String()
|
|
||||||
|
|
||||||
// Make sure parent has - in CommandPath() in SEE ALSO:
|
|
||||||
parentPath := cmdEcho.Parent().CommandPath()
|
|
||||||
dashParentPath := strings.Replace(parentPath, " ", "-", -1)
|
|
||||||
expected := translate(dashParentPath)
|
|
||||||
expected = expected + "(" + header.Section + ")"
|
|
||||||
checkStringContains(t, found, expected)
|
|
||||||
|
|
||||||
// Our description
|
|
||||||
expected = translate(cmdEcho.Name())
|
|
||||||
checkStringContains(t, found, expected)
|
|
||||||
|
|
||||||
// Better have our example
|
|
||||||
expected = translate(cmdEcho.Name())
|
|
||||||
checkStringContains(t, found, expected)
|
|
||||||
|
|
||||||
// A local flag
|
|
||||||
expected = "boolone"
|
|
||||||
checkStringContains(t, found, expected)
|
|
||||||
|
|
||||||
// persistent flag on parent
|
|
||||||
expected = "rootflag"
|
|
||||||
checkStringContains(t, found, expected)
|
|
||||||
|
|
||||||
// We better output info about our parent
|
|
||||||
expected = translate(cmdRootWithRun.Name())
|
|
||||||
checkStringContains(t, found, expected)
|
|
||||||
|
|
||||||
// And about subcommands
|
|
||||||
expected = translate(cmdEchoSub.Name())
|
|
||||||
checkStringContains(t, found, expected)
|
|
||||||
|
|
||||||
unexpected := translate(cmdDeprecated.Name())
|
|
||||||
checkStringOmits(t, found, unexpected)
|
|
||||||
|
|
||||||
// auto generated
|
|
||||||
expected = translate("Auto generated")
|
|
||||||
checkStringContains(t, found, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenManNoGenTag(t *testing.T) {
|
|
||||||
c := initializeWithRootCmd()
|
|
||||||
// Need two commands to run the command alphabetical sort
|
|
||||||
cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated)
|
|
||||||
c.AddCommand(cmdPrint, cmdEcho)
|
|
||||||
cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp)
|
|
||||||
cmdEcho.DisableAutoGenTag = true
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
|
|
||||||
header := &GenManHeader{
|
|
||||||
Title: "Project",
|
|
||||||
Section: "2",
|
|
||||||
}
|
|
||||||
// We generate on a subcommand so we have both subcommands and parents
|
|
||||||
if err := GenMan(cmdEcho, header, out); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
found := out.String()
|
|
||||||
|
|
||||||
unexpected := translate("#HISTORY")
|
|
||||||
checkStringOmits(t, found, unexpected)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenManSeeAlso(t *testing.T) {
|
|
||||||
noop := func(cmd *cobra.Command, args []string) {}
|
|
||||||
|
|
||||||
top := &cobra.Command{Use: "top", Run: noop}
|
|
||||||
aaa := &cobra.Command{Use: "aaa", Run: noop, Hidden: true} // #229
|
|
||||||
bbb := &cobra.Command{Use: "bbb", Run: noop}
|
|
||||||
ccc := &cobra.Command{Use: "ccc", Run: noop}
|
|
||||||
top.AddCommand(aaa, bbb, ccc)
|
|
||||||
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
header := &GenManHeader{}
|
|
||||||
if err := GenMan(top, header, out); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(out)
|
|
||||||
|
|
||||||
if err := AssertLineFound(scanner, ".SH SEE ALSO"); err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("Couldn't find SEE ALSO section header: %s", err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := AssertNextLineEquals(scanner, ".PP"); err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("First line after SEE ALSO wasn't break-indent: %s", err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := AssertNextLineEquals(scanner, `\fBtop\-bbb(1)\fP, \fBtop\-ccc(1)\fP`); err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("Second line after SEE ALSO wasn't correct: %s", err.Error()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func AssertLineFound(scanner *bufio.Scanner, expectedLine string) error {
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
if line == expectedLine {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return fmt.Errorf("AssertLineFound: scan failed: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("AssertLineFound: hit EOF before finding %#v", expectedLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func AssertNextLineEquals(scanner *bufio.Scanner, expectedLine string) error {
|
|
||||||
if scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
if line == expectedLine {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("AssertNextLineEquals: got %#v, not %#v", line, expectedLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return fmt.Errorf("AssertNextLineEquals: scan failed: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("AssertNextLineEquals: hit EOF before finding %#v", expectedLine)
|
|
||||||
}
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
package doc_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/cobra/doc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleCommand_GenManTree() {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "test",
|
|
||||||
Short: "my test program",
|
|
||||||
}
|
|
||||||
header := &doc.GenManHeader{
|
|
||||||
Title: "MINE",
|
|
||||||
Section: "3",
|
|
||||||
}
|
|
||||||
doc.GenManTree(cmd, header, "/tmp")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleCommand_GenMan() {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "test",
|
|
||||||
Short: "my test program",
|
|
||||||
}
|
|
||||||
header := &doc.GenManHeader{
|
|
||||||
Title: "MINE",
|
|
||||||
Section: "3",
|
|
||||||
}
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
doc.GenMan(cmd, header, out)
|
|
||||||
fmt.Print(out.String())
|
|
||||||
}
|
|
||||||
-175
@@ -1,175 +0,0 @@
|
|||||||
//Copyright 2015 Red Hat Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package doc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
func printOptions(w io.Writer, cmd *cobra.Command, name string) error {
|
|
||||||
flags := cmd.NonInheritedFlags()
|
|
||||||
flags.SetOutput(w)
|
|
||||||
if flags.HasFlags() {
|
|
||||||
if _, err := fmt.Fprintf(w, "### Options\n\n```\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
flags.PrintDefaults()
|
|
||||||
if _, err := fmt.Fprintf(w, "```\n\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parentFlags := cmd.InheritedFlags()
|
|
||||||
parentFlags.SetOutput(w)
|
|
||||||
if parentFlags.HasFlags() {
|
|
||||||
if _, err := fmt.Fprintf(w, "### Options inherited from parent commands\n\n```\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
parentFlags.PrintDefaults()
|
|
||||||
if _, err := fmt.Fprintf(w, "```\n\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenMarkdown(cmd *cobra.Command, w io.Writer) error {
|
|
||||||
return GenMarkdownCustom(cmd, w, func(s string) string { return s })
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error {
|
|
||||||
name := cmd.CommandPath()
|
|
||||||
|
|
||||||
short := cmd.Short
|
|
||||||
long := cmd.Long
|
|
||||||
if len(long) == 0 {
|
|
||||||
long = short
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := fmt.Fprintf(w, "## %s\n\n", name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprintf(w, "%s\n\n", short); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprintf(w, "### Synopsis\n\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprintf(w, "\n%s\n\n", long); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmd.Runnable() {
|
|
||||||
if _, err := fmt.Fprintf(w, "```\n%s\n```\n\n", cmd.UseLine()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cmd.Example) > 0 {
|
|
||||||
if _, err := fmt.Fprintf(w, "### Examples\n\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprintf(w, "```\n%s\n```\n\n", cmd.Example); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := printOptions(w, cmd, name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if hasSeeAlso(cmd) {
|
|
||||||
if _, err := fmt.Fprintf(w, "### SEE ALSO\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if cmd.HasParent() {
|
|
||||||
parent := cmd.Parent()
|
|
||||||
pname := parent.CommandPath()
|
|
||||||
link := pname + ".md"
|
|
||||||
link = strings.Replace(link, " ", "_", -1)
|
|
||||||
if _, err := fmt.Fprintf(w, "* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cmd.VisitParents(func(c *cobra.Command) {
|
|
||||||
if c.DisableAutoGenTag {
|
|
||||||
cmd.DisableAutoGenTag = c.DisableAutoGenTag
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
children := cmd.Commands()
|
|
||||||
sort.Sort(byName(children))
|
|
||||||
|
|
||||||
for _, child := range children {
|
|
||||||
if !child.IsAvailableCommand() || child.IsHelpCommand() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cname := name + " " + child.Name()
|
|
||||||
link := cname + ".md"
|
|
||||||
link = strings.Replace(link, " ", "_", -1)
|
|
||||||
if _, err := fmt.Fprintf(w, "* [%s](%s)\t - %s\n", cname, linkHandler(link), child.Short); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprintf(w, "\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !cmd.DisableAutoGenTag {
|
|
||||||
if _, err := fmt.Fprintf(w, "###### Auto generated by spf13/cobra on %s\n", time.Now().Format("2-Jan-2006")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenMarkdownTree(cmd *cobra.Command, dir string) error {
|
|
||||||
identity := func(s string) string { return s }
|
|
||||||
emptyStr := func(s string) string { return "" }
|
|
||||||
return GenMarkdownTreeCustom(cmd, dir, emptyStr, identity)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error {
|
|
||||||
for _, c := range cmd.Commands() {
|
|
||||||
if !c.IsAvailableCommand() || c.IsHelpCommand() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := GenMarkdownTreeCustom(c, dir, filePrepender, linkHandler); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".md"
|
|
||||||
filename := filepath.Join(dir, basename)
|
|
||||||
f, err := os.Create(filename)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
if _, err := io.WriteString(f, filePrepender(filename)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := GenMarkdownCustom(cmd, f, linkHandler); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
-104
@@ -1,104 +0,0 @@
|
|||||||
# Generating Markdown Docs For Your Own cobra.Command
|
|
||||||
|
|
||||||
Generating man pages from a cobra command is incredibly easy. An example is as follows:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/cobra/doc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "test",
|
|
||||||
Short: "my test program",
|
|
||||||
}
|
|
||||||
doc.GenMarkdownTree(cmd, "/tmp")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
That will get you a Markdown document `/tmp/test.md`
|
|
||||||
|
|
||||||
## Generate markdown docs for the entire command tree
|
|
||||||
|
|
||||||
This program can actually generate docs for the kubectl command in the kubernetes project
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd"
|
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra/doc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cmd := kubectlcmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
|
|
||||||
doc.GenMarkdownTree(cmd, "./")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This will generate a whole series of files, one for each command in the tree, in the directory specified (in this case "./")
|
|
||||||
|
|
||||||
## Generate markdown docs for a single command
|
|
||||||
|
|
||||||
You may wish to have more control over the output, or only generate for a single command, instead of the entire command tree. If this is the case you may prefer to `GenMarkdown` instead of `GenMarkdownTree`
|
|
||||||
|
|
||||||
```go
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
doc.GenMarkdown(cmd, out)
|
|
||||||
```
|
|
||||||
|
|
||||||
This will write the markdown doc for ONLY "cmd" into the out, buffer.
|
|
||||||
|
|
||||||
## Customize the output
|
|
||||||
|
|
||||||
Both `GenMarkdown` and `GenMarkdownTree` have alternate versions with callbacks to get some control of the output:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func GenMarkdownTreeCustom(cmd *Command, dir string, filePrepender, linkHandler func(string) string) error {
|
|
||||||
//...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string) string) error {
|
|
||||||
//...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `filePrepender` will prepend the return value given the full filepath to the rendered Markdown file. A common use case is to add front matter to use the generated documentation with [Hugo](http://gohugo.io/):
|
|
||||||
|
|
||||||
```go
|
|
||||||
const fmTemplate = `---
|
|
||||||
date: %s
|
|
||||||
title: "%s"
|
|
||||||
slug: %s
|
|
||||||
url: %s
|
|
||||||
---
|
|
||||||
`
|
|
||||||
|
|
||||||
filePrepender := func(filename string) string {
|
|
||||||
now := time.Now().Format(time.RFC3339)
|
|
||||||
name := filepath.Base(filename)
|
|
||||||
base := strings.TrimSuffix(name, path.Ext(name))
|
|
||||||
url := "/commands/" + strings.ToLower(base) + "/"
|
|
||||||
return fmt.Sprintf(fmTemplate, now, strings.Replace(base, "_", " ", -1), base, url)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `linkHandler` can be used to customize the rendered internal links to the commands, given a filename:
|
|
||||||
|
|
||||||
```go
|
|
||||||
linkHandler := func(name string) string {
|
|
||||||
base := strings.TrimSuffix(name, path.Ext(name))
|
|
||||||
return "/commands/" + strings.ToLower(base) + "/"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
-88
@@ -1,88 +0,0 @@
|
|||||||
package doc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = fmt.Println
|
|
||||||
var _ = os.Stderr
|
|
||||||
|
|
||||||
func TestGenMdDoc(t *testing.T) {
|
|
||||||
c := initializeWithRootCmd()
|
|
||||||
// Need two commands to run the command alphabetical sort
|
|
||||||
cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated)
|
|
||||||
c.AddCommand(cmdPrint, cmdEcho)
|
|
||||||
cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp)
|
|
||||||
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
|
|
||||||
// We generate on s subcommand so we have both subcommands and parents
|
|
||||||
if err := GenMarkdown(cmdEcho, out); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
found := out.String()
|
|
||||||
|
|
||||||
// Our description
|
|
||||||
expected := cmdEcho.Long
|
|
||||||
if !strings.Contains(found, expected) {
|
|
||||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Better have our example
|
|
||||||
expected = cmdEcho.Example
|
|
||||||
if !strings.Contains(found, expected) {
|
|
||||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A local flag
|
|
||||||
expected = "boolone"
|
|
||||||
if !strings.Contains(found, expected) {
|
|
||||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
|
||||||
}
|
|
||||||
|
|
||||||
// persistent flag on parent
|
|
||||||
expected = "rootflag"
|
|
||||||
if !strings.Contains(found, expected) {
|
|
||||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We better output info about our parent
|
|
||||||
expected = cmdRootWithRun.Short
|
|
||||||
if !strings.Contains(found, expected) {
|
|
||||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
|
||||||
}
|
|
||||||
|
|
||||||
// And about subcommands
|
|
||||||
expected = cmdEchoSub.Short
|
|
||||||
if !strings.Contains(found, expected) {
|
|
||||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
|
||||||
}
|
|
||||||
|
|
||||||
unexpected := cmdDeprecated.Short
|
|
||||||
if strings.Contains(found, unexpected) {
|
|
||||||
t.Errorf("Unexpected response.\nFound: %v\nBut should not have!!\n", unexpected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenMdNoTag(t *testing.T) {
|
|
||||||
c := initializeWithRootCmd()
|
|
||||||
// Need two commands to run the command alphabetical sort
|
|
||||||
cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated)
|
|
||||||
c.AddCommand(cmdPrint, cmdEcho)
|
|
||||||
c.DisableAutoGenTag = true
|
|
||||||
cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp)
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
|
|
||||||
if err := GenMarkdown(c, out); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
found := out.String()
|
|
||||||
|
|
||||||
unexpected := "Auto generated"
|
|
||||||
checkStringOmits(t, found, unexpected)
|
|
||||||
|
|
||||||
}
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
// Copyright 2015 Red Hat Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package doc
|
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
|
||||||
|
|
||||||
// Test to see if we have a reason to print See Also information in docs
|
|
||||||
// Basically this is a test for a parent commend or a subcommand which is
|
|
||||||
// both not deprecated and not the autogenerated help command.
|
|
||||||
func hasSeeAlso(cmd *cobra.Command) bool {
|
|
||||||
if cmd.HasParent() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, c := range cmd.Commands() {
|
|
||||||
if !c.IsAvailableCommand() || c.IsHelpCommand() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type byName []*cobra.Command
|
|
||||||
|
|
||||||
func (s byName) Len() int { return len(s) }
|
|
||||||
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
|
|
||||||
-180
@@ -1,180 +0,0 @@
|
|||||||
// Copyright 2009 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 pflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This value can be a boolean ("true", "false") or "maybe"
|
|
||||||
type triStateValue int
|
|
||||||
|
|
||||||
const (
|
|
||||||
triStateFalse triStateValue = 0
|
|
||||||
triStateTrue triStateValue = 1
|
|
||||||
triStateMaybe triStateValue = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
const strTriStateMaybe = "maybe"
|
|
||||||
|
|
||||||
func (v *triStateValue) IsBoolFlag() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *triStateValue) Get() interface{} {
|
|
||||||
return triStateValue(*v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *triStateValue) Set(s string) error {
|
|
||||||
if s == strTriStateMaybe {
|
|
||||||
*v = triStateMaybe
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
boolVal, err := strconv.ParseBool(s)
|
|
||||||
if boolVal {
|
|
||||||
*v = triStateTrue
|
|
||||||
} else {
|
|
||||||
*v = triStateFalse
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *triStateValue) String() string {
|
|
||||||
if *v == triStateMaybe {
|
|
||||||
return strTriStateMaybe
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%v", bool(*v == triStateTrue))
|
|
||||||
}
|
|
||||||
|
|
||||||
// The type of the flag as required by the pflag.Value interface
|
|
||||||
func (v *triStateValue) Type() string {
|
|
||||||
return "version"
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUpFlagSet(tristate *triStateValue) *FlagSet {
|
|
||||||
f := NewFlagSet("test", ContinueOnError)
|
|
||||||
*tristate = triStateFalse
|
|
||||||
flag := f.VarPF(tristate, "tristate", "t", "tristate value (true, maybe or false)")
|
|
||||||
flag.NoOptDefVal = "true"
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExplicitTrue(t *testing.T) {
|
|
||||||
var tristate triStateValue
|
|
||||||
f := setUpFlagSet(&tristate)
|
|
||||||
err := f.Parse([]string{"--tristate=true"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
if tristate != triStateTrue {
|
|
||||||
t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestImplicitTrue(t *testing.T) {
|
|
||||||
var tristate triStateValue
|
|
||||||
f := setUpFlagSet(&tristate)
|
|
||||||
err := f.Parse([]string{"--tristate"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
if tristate != triStateTrue {
|
|
||||||
t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShortFlag(t *testing.T) {
|
|
||||||
var tristate triStateValue
|
|
||||||
f := setUpFlagSet(&tristate)
|
|
||||||
err := f.Parse([]string{"-t"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
if tristate != triStateTrue {
|
|
||||||
t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShortFlagExtraArgument(t *testing.T) {
|
|
||||||
var tristate triStateValue
|
|
||||||
f := setUpFlagSet(&tristate)
|
|
||||||
// The"maybe"turns into an arg, since short boolean options will only do true/false
|
|
||||||
err := f.Parse([]string{"-t", "maybe"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
if tristate != triStateTrue {
|
|
||||||
t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead")
|
|
||||||
}
|
|
||||||
args := f.Args()
|
|
||||||
if len(args) != 1 || args[0] != "maybe" {
|
|
||||||
t.Fatal("expected an extra 'maybe' argument to stick around")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExplicitMaybe(t *testing.T) {
|
|
||||||
var tristate triStateValue
|
|
||||||
f := setUpFlagSet(&tristate)
|
|
||||||
err := f.Parse([]string{"--tristate=maybe"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
if tristate != triStateMaybe {
|
|
||||||
t.Fatal("expected", triStateMaybe, "(triStateMaybe) but got", tristate, "instead")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExplicitFalse(t *testing.T) {
|
|
||||||
var tristate triStateValue
|
|
||||||
f := setUpFlagSet(&tristate)
|
|
||||||
err := f.Parse([]string{"--tristate=false"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
if tristate != triStateFalse {
|
|
||||||
t.Fatal("expected", triStateFalse, "(triStateFalse) but got", tristate, "instead")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestImplicitFalse(t *testing.T) {
|
|
||||||
var tristate triStateValue
|
|
||||||
f := setUpFlagSet(&tristate)
|
|
||||||
err := f.Parse([]string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
if tristate != triStateFalse {
|
|
||||||
t.Fatal("expected", triStateFalse, "(triStateFalse) but got", tristate, "instead")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInvalidValue(t *testing.T) {
|
|
||||||
var tristate triStateValue
|
|
||||||
f := setUpFlagSet(&tristate)
|
|
||||||
var buf bytes.Buffer
|
|
||||||
f.SetOutput(&buf)
|
|
||||||
err := f.Parse([]string{"--tristate=invalid"})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected an error but did not get any, tristate has value", tristate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBoolP(t *testing.T) {
|
|
||||||
b := BoolP("bool", "b", false, "bool value in CommandLine")
|
|
||||||
c := BoolP("c", "c", false, "other bool value")
|
|
||||||
args := []string{"--bool"}
|
|
||||||
if err := CommandLine.Parse(args); err != nil {
|
|
||||||
t.Error("expected no error, got ", err)
|
|
||||||
}
|
|
||||||
if *b != true {
|
|
||||||
t.Errorf("expected b=true got b=%s", b)
|
|
||||||
}
|
|
||||||
if *c != false {
|
|
||||||
t.Errorf("expect c=false got c=%s", c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
package pflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = fmt.Printf
|
|
||||||
|
|
||||||
func setUpCount(c *int) *FlagSet {
|
|
||||||
f := NewFlagSet("test", ContinueOnError)
|
|
||||||
f.CountVarP(c, "verbose", "v", "a counter")
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCount(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
input []string
|
|
||||||
success bool
|
|
||||||
expected int
|
|
||||||
}{
|
|
||||||
{[]string{"-vvv"}, true, 3},
|
|
||||||
{[]string{"-v", "-v", "-v"}, true, 3},
|
|
||||||
{[]string{"-v", "--verbose", "-v"}, true, 3},
|
|
||||||
{[]string{"-v=3", "-v"}, true, 4},
|
|
||||||
{[]string{"-v=a"}, false, 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
devnull, _ := os.Open(os.DevNull)
|
|
||||||
os.Stderr = devnull
|
|
||||||
for i := range testCases {
|
|
||||||
var count int
|
|
||||||
f := setUpCount(&count)
|
|
||||||
|
|
||||||
tc := &testCases[i]
|
|
||||||
|
|
||||||
err := f.Parse(tc.input)
|
|
||||||
if err != nil && tc.success == true {
|
|
||||||
t.Errorf("expected success, got %q", err)
|
|
||||||
continue
|
|
||||||
} else if err == nil && tc.success == false {
|
|
||||||
t.Errorf("expected failure, got success")
|
|
||||||
continue
|
|
||||||
} else if tc.success {
|
|
||||||
c, err := f.GetCount("verbose")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Got error trying to fetch the counter flag")
|
|
||||||
}
|
|
||||||
if c != tc.expected {
|
|
||||||
t.Errorf("expected %q, got %q", tc.expected, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-77
@@ -1,77 +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.
|
|
||||||
|
|
||||||
// These examples demonstrate more intricate uses of the flag package.
|
|
||||||
package pflag_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
flag "github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Example 1: A single string flag called "species" with default value "gopher".
|
|
||||||
var species = flag.String("species", "gopher", "the species we are studying")
|
|
||||||
|
|
||||||
// Example 2: A flag with a shorthand letter.
|
|
||||||
var gopherType = flag.StringP("gopher_type", "g", "pocket", "the variety of gopher")
|
|
||||||
|
|
||||||
// Example 3: A user-defined flag type, a slice of durations.
|
|
||||||
type interval []time.Duration
|
|
||||||
|
|
||||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
|
||||||
// The String method's output will be used in diagnostics.
|
|
||||||
func (i *interval) String() string {
|
|
||||||
return fmt.Sprint(*i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *interval) Type() string {
|
|
||||||
return "interval"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set is the method to set the flag value, part of the flag.Value interface.
|
|
||||||
// Set's argument is a string to be parsed to set the flag.
|
|
||||||
// It's a comma-separated list, so we split it.
|
|
||||||
func (i *interval) Set(value string) error {
|
|
||||||
// If we wanted to allow the flag to be set multiple times,
|
|
||||||
// accumulating values, we would delete this if statement.
|
|
||||||
// That would permit usages such as
|
|
||||||
// -deltaT 10s -deltaT 15s
|
|
||||||
// and other combinations.
|
|
||||||
if len(*i) > 0 {
|
|
||||||
return errors.New("interval flag already set")
|
|
||||||
}
|
|
||||||
for _, dt := range strings.Split(value, ",") {
|
|
||||||
duration, err := time.ParseDuration(dt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*i = append(*i, duration)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define a flag to accumulate durations. Because it has a special type,
|
|
||||||
// we need to use the Var function and therefore create the flag during
|
|
||||||
// init.
|
|
||||||
|
|
||||||
var intervalFlag interval
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Tie the command-line flag to the intervalFlag variable and
|
|
||||||
// set a usage message.
|
|
||||||
flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Example() {
|
|
||||||
// All the interesting pieces are with the variables declared above, but
|
|
||||||
// to enable the flag package to see the flags defined there, one must
|
|
||||||
// execute, typically at the start of main (not init!):
|
|
||||||
// flag.Parse()
|
|
||||||
// We don't run it here because this is not a main function and
|
|
||||||
// the testing suite has already parsed the flags.
|
|
||||||
}
|
|
||||||
-29
@@ -1,29 +0,0 @@
|
|||||||
// Copyright 2010 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 pflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Additional routines compiled into the package only during testing.
|
|
||||||
|
|
||||||
// ResetForTesting clears all flag state and sets the usage function as directed.
|
|
||||||
// After calling ResetForTesting, parse errors in flag handling will not
|
|
||||||
// exit the program.
|
|
||||||
func ResetForTesting(usage func()) {
|
|
||||||
CommandLine = &FlagSet{
|
|
||||||
name: os.Args[0],
|
|
||||||
errorHandling: ContinueOnError,
|
|
||||||
output: ioutil.Discard,
|
|
||||||
}
|
|
||||||
Usage = usage
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCommandLine returns the default FlagSet.
|
|
||||||
func GetCommandLine() *FlagSet {
|
|
||||||
return CommandLine
|
|
||||||
}
|
|
||||||
-913
@@ -1,913 +0,0 @@
|
|||||||
// Copyright 2009 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 pflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
testBool = Bool("test_bool", false, "bool value")
|
|
||||||
testInt = Int("test_int", 0, "int value")
|
|
||||||
testInt64 = Int64("test_int64", 0, "int64 value")
|
|
||||||
testUint = Uint("test_uint", 0, "uint value")
|
|
||||||
testUint64 = Uint64("test_uint64", 0, "uint64 value")
|
|
||||||
testString = String("test_string", "0", "string value")
|
|
||||||
testFloat = Float64("test_float64", 0, "float64 value")
|
|
||||||
testDuration = Duration("test_duration", 0, "time.Duration value")
|
|
||||||
testOptionalInt = Int("test_optional_int", 0, "optional int value")
|
|
||||||
normalizeFlagNameInvocations = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
func boolString(s string) string {
|
|
||||||
if s == "0" {
|
|
||||||
return "false"
|
|
||||||
}
|
|
||||||
return "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEverything(t *testing.T) {
|
|
||||||
m := make(map[string]*Flag)
|
|
||||||
desired := "0"
|
|
||||||
visitor := func(f *Flag) {
|
|
||||||
if len(f.Name) > 5 && f.Name[0:5] == "test_" {
|
|
||||||
m[f.Name] = f
|
|
||||||
ok := false
|
|
||||||
switch {
|
|
||||||
case f.Value.String() == desired:
|
|
||||||
ok = true
|
|
||||||
case f.Name == "test_bool" && f.Value.String() == boolString(desired):
|
|
||||||
ok = true
|
|
||||||
case f.Name == "test_duration" && f.Value.String() == desired+"s":
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VisitAll(visitor)
|
|
||||||
if len(m) != 9 {
|
|
||||||
t.Error("VisitAll misses some flags")
|
|
||||||
for k, v := range m {
|
|
||||||
t.Log(k, *v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m = make(map[string]*Flag)
|
|
||||||
Visit(visitor)
|
|
||||||
if len(m) != 0 {
|
|
||||||
t.Errorf("Visit sees unset flags")
|
|
||||||
for k, v := range m {
|
|
||||||
t.Log(k, *v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Now set all flags
|
|
||||||
Set("test_bool", "true")
|
|
||||||
Set("test_int", "1")
|
|
||||||
Set("test_int64", "1")
|
|
||||||
Set("test_uint", "1")
|
|
||||||
Set("test_uint64", "1")
|
|
||||||
Set("test_string", "1")
|
|
||||||
Set("test_float64", "1")
|
|
||||||
Set("test_duration", "1s")
|
|
||||||
Set("test_optional_int", "1")
|
|
||||||
desired = "1"
|
|
||||||
Visit(visitor)
|
|
||||||
if len(m) != 9 {
|
|
||||||
t.Error("Visit fails after set")
|
|
||||||
for k, v := range m {
|
|
||||||
t.Log(k, *v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Now test they're visited in sort order.
|
|
||||||
var flagNames []string
|
|
||||||
Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) })
|
|
||||||
if !sort.StringsAreSorted(flagNames) {
|
|
||||||
t.Errorf("flag names not sorted: %v", flagNames)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsage(t *testing.T) {
|
|
||||||
called := false
|
|
||||||
ResetForTesting(func() { called = true })
|
|
||||||
if GetCommandLine().Parse([]string{"--x"}) == nil {
|
|
||||||
t.Error("parse did not fail for unknown flag")
|
|
||||||
}
|
|
||||||
if !called {
|
|
||||||
t.Error("did not call Usage for unknown flag")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddFlagSet(t *testing.T) {
|
|
||||||
oldSet := NewFlagSet("old", ContinueOnError)
|
|
||||||
newSet := NewFlagSet("new", ContinueOnError)
|
|
||||||
|
|
||||||
oldSet.String("flag1", "flag1", "flag1")
|
|
||||||
oldSet.String("flag2", "flag2", "flag2")
|
|
||||||
|
|
||||||
newSet.String("flag2", "flag2", "flag2")
|
|
||||||
newSet.String("flag3", "flag3", "flag3")
|
|
||||||
|
|
||||||
oldSet.AddFlagSet(newSet)
|
|
||||||
|
|
||||||
if len(oldSet.formal) != 3 {
|
|
||||||
t.Errorf("Unexpected result adding a FlagSet to a FlagSet %v", oldSet)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAnnotation(t *testing.T) {
|
|
||||||
f := NewFlagSet("shorthand", ContinueOnError)
|
|
||||||
|
|
||||||
if err := f.SetAnnotation("missing-flag", "key", nil); err == nil {
|
|
||||||
t.Errorf("Expected error setting annotation on non-existent flag")
|
|
||||||
}
|
|
||||||
|
|
||||||
f.StringP("stringa", "a", "", "string value")
|
|
||||||
if err := f.SetAnnotation("stringa", "key", nil); err != nil {
|
|
||||||
t.Errorf("Unexpected error setting new nil annotation: %v", err)
|
|
||||||
}
|
|
||||||
if annotation := f.Lookup("stringa").Annotations["key"]; annotation != nil {
|
|
||||||
t.Errorf("Unexpected annotation: %v", annotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.StringP("stringb", "b", "", "string2 value")
|
|
||||||
if err := f.SetAnnotation("stringb", "key", []string{"value1"}); err != nil {
|
|
||||||
t.Errorf("Unexpected error setting new annotation: %v", err)
|
|
||||||
}
|
|
||||||
if annotation := f.Lookup("stringb").Annotations["key"]; !reflect.DeepEqual(annotation, []string{"value1"}) {
|
|
||||||
t.Errorf("Unexpected annotation: %v", annotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.SetAnnotation("stringb", "key", []string{"value2"}); err != nil {
|
|
||||||
t.Errorf("Unexpected error updating annotation: %v", err)
|
|
||||||
}
|
|
||||||
if annotation := f.Lookup("stringb").Annotations["key"]; !reflect.DeepEqual(annotation, []string{"value2"}) {
|
|
||||||
t.Errorf("Unexpected annotation: %v", annotation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testParse(f *FlagSet, t *testing.T) {
|
|
||||||
if f.Parsed() {
|
|
||||||
t.Error("f.Parse() = true before Parse")
|
|
||||||
}
|
|
||||||
boolFlag := f.Bool("bool", false, "bool value")
|
|
||||||
bool2Flag := f.Bool("bool2", false, "bool2 value")
|
|
||||||
bool3Flag := f.Bool("bool3", false, "bool3 value")
|
|
||||||
intFlag := f.Int("int", 0, "int value")
|
|
||||||
int8Flag := f.Int8("int8", 0, "int value")
|
|
||||||
int32Flag := f.Int32("int32", 0, "int value")
|
|
||||||
int64Flag := f.Int64("int64", 0, "int64 value")
|
|
||||||
uintFlag := f.Uint("uint", 0, "uint value")
|
|
||||||
uint8Flag := f.Uint8("uint8", 0, "uint value")
|
|
||||||
uint16Flag := f.Uint16("uint16", 0, "uint value")
|
|
||||||
uint32Flag := f.Uint32("uint32", 0, "uint value")
|
|
||||||
uint64Flag := f.Uint64("uint64", 0, "uint64 value")
|
|
||||||
stringFlag := f.String("string", "0", "string value")
|
|
||||||
float32Flag := f.Float32("float32", 0, "float32 value")
|
|
||||||
float64Flag := f.Float64("float64", 0, "float64 value")
|
|
||||||
ipFlag := f.IP("ip", net.ParseIP("127.0.0.1"), "ip value")
|
|
||||||
maskFlag := f.IPMask("mask", ParseIPv4Mask("0.0.0.0"), "mask value")
|
|
||||||
durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
|
|
||||||
optionalIntNoValueFlag := f.Int("optional-int-no-value", 0, "int value")
|
|
||||||
f.Lookup("optional-int-no-value").NoOptDefVal = "9"
|
|
||||||
optionalIntWithValueFlag := f.Int("optional-int-with-value", 0, "int value")
|
|
||||||
f.Lookup("optional-int-no-value").NoOptDefVal = "9"
|
|
||||||
extra := "one-extra-argument"
|
|
||||||
args := []string{
|
|
||||||
"--bool",
|
|
||||||
"--bool2=true",
|
|
||||||
"--bool3=false",
|
|
||||||
"--int=22",
|
|
||||||
"--int8=-8",
|
|
||||||
"--int32=-32",
|
|
||||||
"--int64=0x23",
|
|
||||||
"--uint", "24",
|
|
||||||
"--uint8=8",
|
|
||||||
"--uint16=16",
|
|
||||||
"--uint32=32",
|
|
||||||
"--uint64=25",
|
|
||||||
"--string=hello",
|
|
||||||
"--float32=-172e12",
|
|
||||||
"--float64=2718e28",
|
|
||||||
"--ip=10.11.12.13",
|
|
||||||
"--mask=255.255.255.0",
|
|
||||||
"--duration=2m",
|
|
||||||
"--optional-int-no-value",
|
|
||||||
"--optional-int-with-value=42",
|
|
||||||
extra,
|
|
||||||
}
|
|
||||||
if err := f.Parse(args); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !f.Parsed() {
|
|
||||||
t.Error("f.Parse() = false after Parse")
|
|
||||||
}
|
|
||||||
if *boolFlag != true {
|
|
||||||
t.Error("bool flag should be true, is ", *boolFlag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetBool("bool"); err != nil || v != *boolFlag {
|
|
||||||
t.Error("GetBool does not work.")
|
|
||||||
}
|
|
||||||
if *bool2Flag != true {
|
|
||||||
t.Error("bool2 flag should be true, is ", *bool2Flag)
|
|
||||||
}
|
|
||||||
if *bool3Flag != false {
|
|
||||||
t.Error("bool3 flag should be false, is ", *bool2Flag)
|
|
||||||
}
|
|
||||||
if *intFlag != 22 {
|
|
||||||
t.Error("int flag should be 22, is ", *intFlag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetInt("int"); err != nil || v != *intFlag {
|
|
||||||
t.Error("GetInt does not work.")
|
|
||||||
}
|
|
||||||
if *int8Flag != -8 {
|
|
||||||
t.Error("int8 flag should be 0x23, is ", *int8Flag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetInt8("int8"); err != nil || v != *int8Flag {
|
|
||||||
t.Error("GetInt8 does not work.")
|
|
||||||
}
|
|
||||||
if *int32Flag != -32 {
|
|
||||||
t.Error("int32 flag should be 0x23, is ", *int32Flag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetInt32("int32"); err != nil || v != *int32Flag {
|
|
||||||
t.Error("GetInt32 does not work.")
|
|
||||||
}
|
|
||||||
if *int64Flag != 0x23 {
|
|
||||||
t.Error("int64 flag should be 0x23, is ", *int64Flag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetInt64("int64"); err != nil || v != *int64Flag {
|
|
||||||
t.Error("GetInt64 does not work.")
|
|
||||||
}
|
|
||||||
if *uintFlag != 24 {
|
|
||||||
t.Error("uint flag should be 24, is ", *uintFlag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetUint("uint"); err != nil || v != *uintFlag {
|
|
||||||
t.Error("GetUint does not work.")
|
|
||||||
}
|
|
||||||
if *uint8Flag != 8 {
|
|
||||||
t.Error("uint8 flag should be 8, is ", *uint8Flag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetUint8("uint8"); err != nil || v != *uint8Flag {
|
|
||||||
t.Error("GetUint8 does not work.")
|
|
||||||
}
|
|
||||||
if *uint16Flag != 16 {
|
|
||||||
t.Error("uint16 flag should be 16, is ", *uint16Flag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetUint16("uint16"); err != nil || v != *uint16Flag {
|
|
||||||
t.Error("GetUint16 does not work.")
|
|
||||||
}
|
|
||||||
if *uint32Flag != 32 {
|
|
||||||
t.Error("uint32 flag should be 32, is ", *uint32Flag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetUint32("uint32"); err != nil || v != *uint32Flag {
|
|
||||||
t.Error("GetUint32 does not work.")
|
|
||||||
}
|
|
||||||
if *uint64Flag != 25 {
|
|
||||||
t.Error("uint64 flag should be 25, is ", *uint64Flag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetUint64("uint64"); err != nil || v != *uint64Flag {
|
|
||||||
t.Error("GetUint64 does not work.")
|
|
||||||
}
|
|
||||||
if *stringFlag != "hello" {
|
|
||||||
t.Error("string flag should be `hello`, is ", *stringFlag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetString("string"); err != nil || v != *stringFlag {
|
|
||||||
t.Error("GetString does not work.")
|
|
||||||
}
|
|
||||||
if *float32Flag != -172e12 {
|
|
||||||
t.Error("float32 flag should be -172e12, is ", *float32Flag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetFloat32("float32"); err != nil || v != *float32Flag {
|
|
||||||
t.Errorf("GetFloat32 returned %v but float32Flag was %v", v, *float32Flag)
|
|
||||||
}
|
|
||||||
if *float64Flag != 2718e28 {
|
|
||||||
t.Error("float64 flag should be 2718e28, is ", *float64Flag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetFloat64("float64"); err != nil || v != *float64Flag {
|
|
||||||
t.Errorf("GetFloat64 returned %v but float64Flag was %v", v, *float64Flag)
|
|
||||||
}
|
|
||||||
if !(*ipFlag).Equal(net.ParseIP("10.11.12.13")) {
|
|
||||||
t.Error("ip flag should be 10.11.12.13, is ", *ipFlag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetIP("ip"); err != nil || !v.Equal(*ipFlag) {
|
|
||||||
t.Errorf("GetIP returned %v but ipFlag was %v", v, *ipFlag)
|
|
||||||
}
|
|
||||||
if (*maskFlag).String() != ParseIPv4Mask("255.255.255.0").String() {
|
|
||||||
t.Error("mask flag should be 255.255.255.0, is ", (*maskFlag).String())
|
|
||||||
}
|
|
||||||
if v, err := f.GetIPv4Mask("mask"); err != nil || v.String() != (*maskFlag).String() {
|
|
||||||
t.Errorf("GetIP returned %v maskFlag was %v error was %v", v, *maskFlag, err)
|
|
||||||
}
|
|
||||||
if *durationFlag != 2*time.Minute {
|
|
||||||
t.Error("duration flag should be 2m, is ", *durationFlag)
|
|
||||||
}
|
|
||||||
if v, err := f.GetDuration("duration"); err != nil || v != *durationFlag {
|
|
||||||
t.Error("GetDuration does not work.")
|
|
||||||
}
|
|
||||||
if _, err := f.GetInt("duration"); err == nil {
|
|
||||||
t.Error("GetInt parsed a time.Duration?!?!")
|
|
||||||
}
|
|
||||||
if *optionalIntNoValueFlag != 9 {
|
|
||||||
t.Error("optional int flag should be the default value, is ", *optionalIntNoValueFlag)
|
|
||||||
}
|
|
||||||
if *optionalIntWithValueFlag != 42 {
|
|
||||||
t.Error("optional int flag should be 42, is ", *optionalIntWithValueFlag)
|
|
||||||
}
|
|
||||||
if len(f.Args()) != 1 {
|
|
||||||
t.Error("expected one argument, got", len(f.Args()))
|
|
||||||
} else if f.Args()[0] != extra {
|
|
||||||
t.Errorf("expected argument %q got %q", extra, f.Args()[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShorthand(t *testing.T) {
|
|
||||||
f := NewFlagSet("shorthand", ContinueOnError)
|
|
||||||
if f.Parsed() {
|
|
||||||
t.Error("f.Parse() = true before Parse")
|
|
||||||
}
|
|
||||||
boolaFlag := f.BoolP("boola", "a", false, "bool value")
|
|
||||||
boolbFlag := f.BoolP("boolb", "b", false, "bool2 value")
|
|
||||||
boolcFlag := f.BoolP("boolc", "c", false, "bool3 value")
|
|
||||||
booldFlag := f.BoolP("boold", "d", false, "bool4 value")
|
|
||||||
stringaFlag := f.StringP("stringa", "s", "0", "string value")
|
|
||||||
stringzFlag := f.StringP("stringz", "z", "0", "string value")
|
|
||||||
extra := "interspersed-argument"
|
|
||||||
notaflag := "--i-look-like-a-flag"
|
|
||||||
args := []string{
|
|
||||||
"-ab",
|
|
||||||
extra,
|
|
||||||
"-cs",
|
|
||||||
"hello",
|
|
||||||
"-z=something",
|
|
||||||
"-d=true",
|
|
||||||
"--",
|
|
||||||
notaflag,
|
|
||||||
}
|
|
||||||
f.SetOutput(ioutil.Discard)
|
|
||||||
if err := f.Parse(args); err != nil {
|
|
||||||
t.Error("expected no error, got ", err)
|
|
||||||
}
|
|
||||||
if !f.Parsed() {
|
|
||||||
t.Error("f.Parse() = false after Parse")
|
|
||||||
}
|
|
||||||
if *boolaFlag != true {
|
|
||||||
t.Error("boola flag should be true, is ", *boolaFlag)
|
|
||||||
}
|
|
||||||
if *boolbFlag != true {
|
|
||||||
t.Error("boolb flag should be true, is ", *boolbFlag)
|
|
||||||
}
|
|
||||||
if *boolcFlag != true {
|
|
||||||
t.Error("boolc flag should be true, is ", *boolcFlag)
|
|
||||||
}
|
|
||||||
if *booldFlag != true {
|
|
||||||
t.Error("boold flag should be true, is ", *booldFlag)
|
|
||||||
}
|
|
||||||
if *stringaFlag != "hello" {
|
|
||||||
t.Error("stringa flag should be `hello`, is ", *stringaFlag)
|
|
||||||
}
|
|
||||||
if *stringzFlag != "something" {
|
|
||||||
t.Error("stringz flag should be `something`, is ", *stringzFlag)
|
|
||||||
}
|
|
||||||
if len(f.Args()) != 2 {
|
|
||||||
t.Error("expected one argument, got", len(f.Args()))
|
|
||||||
} else if f.Args()[0] != extra {
|
|
||||||
t.Errorf("expected argument %q got %q", extra, f.Args()[0])
|
|
||||||
} else if f.Args()[1] != notaflag {
|
|
||||||
t.Errorf("expected argument %q got %q", notaflag, f.Args()[1])
|
|
||||||
}
|
|
||||||
if f.ArgsLenAtDash() != 1 {
|
|
||||||
t.Errorf("expected argsLenAtDash %d got %d", f.ArgsLenAtDash(), 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
|
||||||
ResetForTesting(func() { t.Error("bad parse") })
|
|
||||||
testParse(GetCommandLine(), t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlagSetParse(t *testing.T) {
|
|
||||||
testParse(NewFlagSet("test", ContinueOnError), t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChangedHelper(t *testing.T) {
|
|
||||||
f := NewFlagSet("changedtest", ContinueOnError)
|
|
||||||
_ = f.Bool("changed", false, "changed bool")
|
|
||||||
_ = f.Bool("settrue", true, "true to true")
|
|
||||||
_ = f.Bool("setfalse", false, "false to false")
|
|
||||||
_ = f.Bool("unchanged", false, "unchanged bool")
|
|
||||||
|
|
||||||
args := []string{"--changed", "--settrue", "--setfalse=false"}
|
|
||||||
if err := f.Parse(args); err != nil {
|
|
||||||
t.Error("f.Parse() = false after Parse")
|
|
||||||
}
|
|
||||||
if !f.Changed("changed") {
|
|
||||||
t.Errorf("--changed wasn't changed!")
|
|
||||||
}
|
|
||||||
if !f.Changed("settrue") {
|
|
||||||
t.Errorf("--settrue wasn't changed!")
|
|
||||||
}
|
|
||||||
if !f.Changed("setfalse") {
|
|
||||||
t.Errorf("--setfalse wasn't changed!")
|
|
||||||
}
|
|
||||||
if f.Changed("unchanged") {
|
|
||||||
t.Errorf("--unchanged was changed!")
|
|
||||||
}
|
|
||||||
if f.Changed("invalid") {
|
|
||||||
t.Errorf("--invalid was changed!")
|
|
||||||
}
|
|
||||||
if f.ArgsLenAtDash() != -1 {
|
|
||||||
t.Errorf("Expected argsLenAtDash: %d but got %d", -1, f.ArgsLenAtDash())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func replaceSeparators(name string, from []string, to string) string {
|
|
||||||
result := name
|
|
||||||
for _, sep := range from {
|
|
||||||
result = strings.Replace(result, sep, to, -1)
|
|
||||||
}
|
|
||||||
// Type convert to indicate normalization has been done.
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func wordSepNormalizeFunc(f *FlagSet, name string) NormalizedName {
|
|
||||||
seps := []string{"-", "_"}
|
|
||||||
name = replaceSeparators(name, seps, ".")
|
|
||||||
normalizeFlagNameInvocations++
|
|
||||||
|
|
||||||
return NormalizedName(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testWordSepNormalizedNames(args []string, t *testing.T) {
|
|
||||||
f := NewFlagSet("normalized", ContinueOnError)
|
|
||||||
if f.Parsed() {
|
|
||||||
t.Error("f.Parse() = true before Parse")
|
|
||||||
}
|
|
||||||
withDashFlag := f.Bool("with-dash-flag", false, "bool value")
|
|
||||||
// Set this after some flags have been added and before others.
|
|
||||||
f.SetNormalizeFunc(wordSepNormalizeFunc)
|
|
||||||
withUnderFlag := f.Bool("with_under_flag", false, "bool value")
|
|
||||||
withBothFlag := f.Bool("with-both_flag", false, "bool value")
|
|
||||||
if err := f.Parse(args); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !f.Parsed() {
|
|
||||||
t.Error("f.Parse() = false after Parse")
|
|
||||||
}
|
|
||||||
if *withDashFlag != true {
|
|
||||||
t.Error("withDashFlag flag should be true, is ", *withDashFlag)
|
|
||||||
}
|
|
||||||
if *withUnderFlag != true {
|
|
||||||
t.Error("withUnderFlag flag should be true, is ", *withUnderFlag)
|
|
||||||
}
|
|
||||||
if *withBothFlag != true {
|
|
||||||
t.Error("withBothFlag flag should be true, is ", *withBothFlag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWordSepNormalizedNames(t *testing.T) {
|
|
||||||
args := []string{
|
|
||||||
"--with-dash-flag",
|
|
||||||
"--with-under-flag",
|
|
||||||
"--with-both-flag",
|
|
||||||
}
|
|
||||||
testWordSepNormalizedNames(args, t)
|
|
||||||
|
|
||||||
args = []string{
|
|
||||||
"--with_dash_flag",
|
|
||||||
"--with_under_flag",
|
|
||||||
"--with_both_flag",
|
|
||||||
}
|
|
||||||
testWordSepNormalizedNames(args, t)
|
|
||||||
|
|
||||||
args = []string{
|
|
||||||
"--with-dash_flag",
|
|
||||||
"--with-under_flag",
|
|
||||||
"--with-both_flag",
|
|
||||||
}
|
|
||||||
testWordSepNormalizedNames(args, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func aliasAndWordSepFlagNames(f *FlagSet, name string) NormalizedName {
|
|
||||||
seps := []string{"-", "_"}
|
|
||||||
|
|
||||||
oldName := replaceSeparators("old-valid_flag", seps, ".")
|
|
||||||
newName := replaceSeparators("valid-flag", seps, ".")
|
|
||||||
|
|
||||||
name = replaceSeparators(name, seps, ".")
|
|
||||||
switch name {
|
|
||||||
case oldName:
|
|
||||||
name = newName
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return NormalizedName(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCustomNormalizedNames(t *testing.T) {
|
|
||||||
f := NewFlagSet("normalized", ContinueOnError)
|
|
||||||
if f.Parsed() {
|
|
||||||
t.Error("f.Parse() = true before Parse")
|
|
||||||
}
|
|
||||||
|
|
||||||
validFlag := f.Bool("valid-flag", false, "bool value")
|
|
||||||
f.SetNormalizeFunc(aliasAndWordSepFlagNames)
|
|
||||||
someOtherFlag := f.Bool("some-other-flag", false, "bool value")
|
|
||||||
|
|
||||||
args := []string{"--old_valid_flag", "--some-other_flag"}
|
|
||||||
if err := f.Parse(args); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *validFlag != true {
|
|
||||||
t.Errorf("validFlag is %v even though we set the alias --old_valid_falg", *validFlag)
|
|
||||||
}
|
|
||||||
if *someOtherFlag != true {
|
|
||||||
t.Error("someOtherFlag should be true, is ", *someOtherFlag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Every flag we add, the name (displayed also in usage) should normalized
|
|
||||||
func TestNormalizationFuncShouldChangeFlagName(t *testing.T) {
|
|
||||||
// Test normalization after addition
|
|
||||||
f := NewFlagSet("normalized", ContinueOnError)
|
|
||||||
|
|
||||||
f.Bool("valid_flag", false, "bool value")
|
|
||||||
if f.Lookup("valid_flag").Name != "valid_flag" {
|
|
||||||
t.Error("The new flag should have the name 'valid_flag' instead of ", f.Lookup("valid_flag").Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.SetNormalizeFunc(wordSepNormalizeFunc)
|
|
||||||
if f.Lookup("valid_flag").Name != "valid.flag" {
|
|
||||||
t.Error("The new flag should have the name 'valid.flag' instead of ", f.Lookup("valid_flag").Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test normalization before addition
|
|
||||||
f = NewFlagSet("normalized", ContinueOnError)
|
|
||||||
f.SetNormalizeFunc(wordSepNormalizeFunc)
|
|
||||||
|
|
||||||
f.Bool("valid_flag", false, "bool value")
|
|
||||||
if f.Lookup("valid_flag").Name != "valid.flag" {
|
|
||||||
t.Error("The new flag should have the name 'valid.flag' instead of ", f.Lookup("valid_flag").Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare a user-defined flag type.
|
|
||||||
type flagVar []string
|
|
||||||
|
|
||||||
func (f *flagVar) String() string {
|
|
||||||
return fmt.Sprint([]string(*f))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *flagVar) Set(value string) error {
|
|
||||||
*f = append(*f, value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *flagVar) Type() string {
|
|
||||||
return "flagVar"
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUserDefined(t *testing.T) {
|
|
||||||
var flags FlagSet
|
|
||||||
flags.Init("test", ContinueOnError)
|
|
||||||
var v flagVar
|
|
||||||
flags.VarP(&v, "v", "v", "usage")
|
|
||||||
if err := flags.Parse([]string{"--v=1", "-v2", "-v", "3"}); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if len(v) != 3 {
|
|
||||||
t.Fatal("expected 3 args; got ", len(v))
|
|
||||||
}
|
|
||||||
expect := "[1 2 3]"
|
|
||||||
if v.String() != expect {
|
|
||||||
t.Errorf("expected value %q got %q", expect, v.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetOutput(t *testing.T) {
|
|
||||||
var flags FlagSet
|
|
||||||
var buf bytes.Buffer
|
|
||||||
flags.SetOutput(&buf)
|
|
||||||
flags.Init("test", ContinueOnError)
|
|
||||||
flags.Parse([]string{"--unknown"})
|
|
||||||
if out := buf.String(); !strings.Contains(out, "--unknown") {
|
|
||||||
t.Logf("expected output mentioning unknown; got %q", out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This tests that one can reset the flags. This still works but not well, and is
|
|
||||||
// superseded by FlagSet.
|
|
||||||
func TestChangingArgs(t *testing.T) {
|
|
||||||
ResetForTesting(func() { t.Fatal("bad parse") })
|
|
||||||
oldArgs := os.Args
|
|
||||||
defer func() { os.Args = oldArgs }()
|
|
||||||
os.Args = []string{"cmd", "--before", "subcmd"}
|
|
||||||
before := Bool("before", false, "")
|
|
||||||
if err := GetCommandLine().Parse(os.Args[1:]); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
cmd := Arg(0)
|
|
||||||
os.Args = []string{"subcmd", "--after", "args"}
|
|
||||||
after := Bool("after", false, "")
|
|
||||||
Parse()
|
|
||||||
args := Args()
|
|
||||||
|
|
||||||
if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
|
|
||||||
t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that -help invokes the usage message and returns ErrHelp.
|
|
||||||
func TestHelp(t *testing.T) {
|
|
||||||
var helpCalled = false
|
|
||||||
fs := NewFlagSet("help test", ContinueOnError)
|
|
||||||
fs.Usage = func() { helpCalled = true }
|
|
||||||
var flag bool
|
|
||||||
fs.BoolVar(&flag, "flag", false, "regular flag")
|
|
||||||
// Regular flag invocation should work
|
|
||||||
err := fs.Parse([]string{"--flag=true"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got ", err)
|
|
||||||
}
|
|
||||||
if !flag {
|
|
||||||
t.Error("flag was not set by --flag")
|
|
||||||
}
|
|
||||||
if helpCalled {
|
|
||||||
t.Error("help called for regular flag")
|
|
||||||
helpCalled = false // reset for next test
|
|
||||||
}
|
|
||||||
// Help flag should work as expected.
|
|
||||||
err = fs.Parse([]string{"--help"})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("error expected")
|
|
||||||
}
|
|
||||||
if err != ErrHelp {
|
|
||||||
t.Fatal("expected ErrHelp; got ", err)
|
|
||||||
}
|
|
||||||
if !helpCalled {
|
|
||||||
t.Fatal("help was not called")
|
|
||||||
}
|
|
||||||
// If we define a help flag, that should override.
|
|
||||||
var help bool
|
|
||||||
fs.BoolVar(&help, "help", false, "help flag")
|
|
||||||
helpCalled = false
|
|
||||||
err = fs.Parse([]string{"--help"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error for defined --help; got ", err)
|
|
||||||
}
|
|
||||||
if helpCalled {
|
|
||||||
t.Fatal("help was called; should not have been for defined help flag")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoInterspersed(t *testing.T) {
|
|
||||||
f := NewFlagSet("test", ContinueOnError)
|
|
||||||
f.SetInterspersed(false)
|
|
||||||
f.Bool("true", true, "always true")
|
|
||||||
f.Bool("false", false, "always false")
|
|
||||||
err := f.Parse([]string{"--true", "break", "--false"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got ", err)
|
|
||||||
}
|
|
||||||
args := f.Args()
|
|
||||||
if len(args) != 2 || args[0] != "break" || args[1] != "--false" {
|
|
||||||
t.Fatal("expected interspersed options/non-options to fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTermination(t *testing.T) {
|
|
||||||
f := NewFlagSet("termination", ContinueOnError)
|
|
||||||
boolFlag := f.BoolP("bool", "l", false, "bool value")
|
|
||||||
if f.Parsed() {
|
|
||||||
t.Error("f.Parse() = true before Parse")
|
|
||||||
}
|
|
||||||
arg1 := "ls"
|
|
||||||
arg2 := "-l"
|
|
||||||
args := []string{
|
|
||||||
"--",
|
|
||||||
arg1,
|
|
||||||
arg2,
|
|
||||||
}
|
|
||||||
f.SetOutput(ioutil.Discard)
|
|
||||||
if err := f.Parse(args); err != nil {
|
|
||||||
t.Fatal("expected no error; got ", err)
|
|
||||||
}
|
|
||||||
if !f.Parsed() {
|
|
||||||
t.Error("f.Parse() = false after Parse")
|
|
||||||
}
|
|
||||||
if *boolFlag {
|
|
||||||
t.Error("expected boolFlag=false, got true")
|
|
||||||
}
|
|
||||||
if len(f.Args()) != 2 {
|
|
||||||
t.Errorf("expected 2 arguments, got %d: %v", len(f.Args()), f.Args())
|
|
||||||
}
|
|
||||||
if f.Args()[0] != arg1 {
|
|
||||||
t.Errorf("expected argument %q got %q", arg1, f.Args()[0])
|
|
||||||
}
|
|
||||||
if f.Args()[1] != arg2 {
|
|
||||||
t.Errorf("expected argument %q got %q", arg2, f.Args()[1])
|
|
||||||
}
|
|
||||||
if f.ArgsLenAtDash() != 0 {
|
|
||||||
t.Errorf("expected argsLenAtDash %d got %d", 0, f.ArgsLenAtDash())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeprecatedFlagInDocs(t *testing.T) {
|
|
||||||
f := NewFlagSet("bob", ContinueOnError)
|
|
||||||
f.Bool("badflag", true, "always true")
|
|
||||||
f.MarkDeprecated("badflag", "use --good-flag instead")
|
|
||||||
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
f.SetOutput(out)
|
|
||||||
f.PrintDefaults()
|
|
||||||
|
|
||||||
if strings.Contains(out.String(), "badflag") {
|
|
||||||
t.Errorf("found deprecated flag in usage!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeprecatedFlagShorthandInDocs(t *testing.T) {
|
|
||||||
f := NewFlagSet("bob", ContinueOnError)
|
|
||||||
name := "noshorthandflag"
|
|
||||||
f.BoolP(name, "n", true, "always true")
|
|
||||||
f.MarkShorthandDeprecated("noshorthandflag", fmt.Sprintf("use --%s instead", name))
|
|
||||||
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
f.SetOutput(out)
|
|
||||||
f.PrintDefaults()
|
|
||||||
|
|
||||||
if strings.Contains(out.String(), "-n,") {
|
|
||||||
t.Errorf("found deprecated flag shorthand in usage!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) {
|
|
||||||
oldStderr := os.Stderr
|
|
||||||
r, w, _ := os.Pipe()
|
|
||||||
os.Stderr = w
|
|
||||||
|
|
||||||
err := f.Parse(args)
|
|
||||||
|
|
||||||
outC := make(chan string)
|
|
||||||
// copy the output in a separate goroutine so printing can't block indefinitely
|
|
||||||
go func() {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
io.Copy(&buf, r)
|
|
||||||
outC <- buf.String()
|
|
||||||
}()
|
|
||||||
|
|
||||||
w.Close()
|
|
||||||
os.Stderr = oldStderr
|
|
||||||
out := <-outC
|
|
||||||
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeprecatedFlagUsage(t *testing.T) {
|
|
||||||
f := NewFlagSet("bob", ContinueOnError)
|
|
||||||
f.Bool("badflag", true, "always true")
|
|
||||||
usageMsg := "use --good-flag instead"
|
|
||||||
f.MarkDeprecated("badflag", usageMsg)
|
|
||||||
|
|
||||||
args := []string{"--badflag"}
|
|
||||||
out, err := parseReturnStderr(t, f, args)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(out, usageMsg) {
|
|
||||||
t.Errorf("usageMsg not printed when using a deprecated flag!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeprecatedFlagShorthandUsage(t *testing.T) {
|
|
||||||
f := NewFlagSet("bob", ContinueOnError)
|
|
||||||
name := "noshorthandflag"
|
|
||||||
f.BoolP(name, "n", true, "always true")
|
|
||||||
usageMsg := fmt.Sprintf("use --%s instead", name)
|
|
||||||
f.MarkShorthandDeprecated(name, usageMsg)
|
|
||||||
|
|
||||||
args := []string{"-n"}
|
|
||||||
out, err := parseReturnStderr(t, f, args)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(out, usageMsg) {
|
|
||||||
t.Errorf("usageMsg not printed when using a deprecated flag!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeprecatedFlagUsageNormalized(t *testing.T) {
|
|
||||||
f := NewFlagSet("bob", ContinueOnError)
|
|
||||||
f.Bool("bad-double_flag", true, "always true")
|
|
||||||
f.SetNormalizeFunc(wordSepNormalizeFunc)
|
|
||||||
usageMsg := "use --good-flag instead"
|
|
||||||
f.MarkDeprecated("bad_double-flag", usageMsg)
|
|
||||||
|
|
||||||
args := []string{"--bad_double_flag"}
|
|
||||||
out, err := parseReturnStderr(t, f, args)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(out, usageMsg) {
|
|
||||||
t.Errorf("usageMsg not printed when using a deprecated flag!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name normalization function should be called only once on flag addition
|
|
||||||
func TestMultipleNormalizeFlagNameInvocations(t *testing.T) {
|
|
||||||
normalizeFlagNameInvocations = 0
|
|
||||||
|
|
||||||
f := NewFlagSet("normalized", ContinueOnError)
|
|
||||||
f.SetNormalizeFunc(wordSepNormalizeFunc)
|
|
||||||
f.Bool("with_under_flag", false, "bool value")
|
|
||||||
|
|
||||||
if normalizeFlagNameInvocations != 1 {
|
|
||||||
t.Fatal("Expected normalizeFlagNameInvocations to be 1; got ", normalizeFlagNameInvocations)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
func TestHiddenFlagInUsage(t *testing.T) {
|
|
||||||
f := NewFlagSet("bob", ContinueOnError)
|
|
||||||
f.Bool("secretFlag", true, "shhh")
|
|
||||||
f.MarkHidden("secretFlag")
|
|
||||||
|
|
||||||
out := new(bytes.Buffer)
|
|
||||||
f.SetOutput(out)
|
|
||||||
f.PrintDefaults()
|
|
||||||
|
|
||||||
if strings.Contains(out.String(), "secretFlag") {
|
|
||||||
t.Errorf("found hidden flag in usage!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
func TestHiddenFlagUsage(t *testing.T) {
|
|
||||||
f := NewFlagSet("bob", ContinueOnError)
|
|
||||||
f.Bool("secretFlag", true, "shhh")
|
|
||||||
f.MarkHidden("secretFlag")
|
|
||||||
|
|
||||||
args := []string{"--secretFlag"}
|
|
||||||
out, err := parseReturnStderr(t, f, args)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Contains(out, "shhh") {
|
|
||||||
t.Errorf("usage message printed when using a hidden flag!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultOutput = ` --A for bootstrapping, allow 'any' type
|
|
||||||
--Alongflagname disable bounds checking
|
|
||||||
-C, --CCC a boolean defaulting to true (default true)
|
|
||||||
--D path set relative path for local imports
|
|
||||||
--F number a non-zero number (default 2.7)
|
|
||||||
--G float a float that defaults to zero
|
|
||||||
--N int a non-zero int (default 27)
|
|
||||||
--ND1 string[="bar"] a string with NoOptDefVal (default "foo")
|
|
||||||
--ND2 num[=4321] a num with NoOptDefVal (default 1234)
|
|
||||||
--Z int an int that defaults to zero
|
|
||||||
--maxT timeout set timeout for dial
|
|
||||||
`
|
|
||||||
|
|
||||||
func TestPrintDefaults(t *testing.T) {
|
|
||||||
fs := NewFlagSet("print defaults test", ContinueOnError)
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fs.SetOutput(&buf)
|
|
||||||
fs.Bool("A", false, "for bootstrapping, allow 'any' type")
|
|
||||||
fs.Bool("Alongflagname", false, "disable bounds checking")
|
|
||||||
fs.BoolP("CCC", "C", true, "a boolean defaulting to true")
|
|
||||||
fs.String("D", "", "set relative `path` for local imports")
|
|
||||||
fs.Float64("F", 2.7, "a non-zero `number`")
|
|
||||||
fs.Float64("G", 0, "a float that defaults to zero")
|
|
||||||
fs.Int("N", 27, "a non-zero int")
|
|
||||||
fs.Int("Z", 0, "an int that defaults to zero")
|
|
||||||
fs.Duration("maxT", 0, "set `timeout` for dial")
|
|
||||||
fs.String("ND1", "foo", "a string with NoOptDefVal")
|
|
||||||
fs.Lookup("ND1").NoOptDefVal = "bar"
|
|
||||||
fs.Int("ND2", 1234, "a `num` with NoOptDefVal")
|
|
||||||
fs.Lookup("ND2").NoOptDefVal = "4321"
|
|
||||||
fs.PrintDefaults()
|
|
||||||
got := buf.String()
|
|
||||||
if got != defaultOutput {
|
|
||||||
fmt.Println("\n" + got)
|
|
||||||
fmt.Println("\n" + defaultOutput)
|
|
||||||
t.Errorf("got %q want %q\n", got, defaultOutput)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
// Copyright 2009 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 pflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
goflag "flag"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGoflags(t *testing.T) {
|
|
||||||
goflag.String("stringFlag", "stringFlag", "stringFlag")
|
|
||||||
goflag.Bool("boolFlag", false, "boolFlag")
|
|
||||||
|
|
||||||
f := NewFlagSet("test", ContinueOnError)
|
|
||||||
|
|
||||||
f.AddGoFlagSet(goflag.CommandLine)
|
|
||||||
err := f.Parse([]string{"--stringFlag=bob", "--boolFlag"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; get", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
getString, err := f.GetString("stringFlag")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; get", err)
|
|
||||||
}
|
|
||||||
if getString != "bob" {
|
|
||||||
t.Fatalf("expected getString=bob but got getString=%s", getString)
|
|
||||||
}
|
|
||||||
|
|
||||||
getBool, err := f.GetBool("boolFlag")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; get", err)
|
|
||||||
}
|
|
||||||
if getBool != true {
|
|
||||||
t.Fatalf("expected getBool=true but got getBool=%v", getBool)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-162
@@ -1,162 +0,0 @@
|
|||||||
// Copyright 2009 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 pflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setUpISFlagSet(isp *[]int) *FlagSet {
|
|
||||||
f := NewFlagSet("test", ContinueOnError)
|
|
||||||
f.IntSliceVar(isp, "is", []int{}, "Command separated list!")
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUpISFlagSetWithDefault(isp *[]int) *FlagSet {
|
|
||||||
f := NewFlagSet("test", ContinueOnError)
|
|
||||||
f.IntSliceVar(isp, "is", []int{0, 1}, "Command separated list!")
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptyIS(t *testing.T) {
|
|
||||||
var is []int
|
|
||||||
f := setUpISFlagSet(&is)
|
|
||||||
err := f.Parse([]string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
getIS, err := f.GetIntSlice("is")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("got an error from GetIntSlice():", err)
|
|
||||||
}
|
|
||||||
if len(getIS) != 0 {
|
|
||||||
t.Fatalf("got is %v with len=%d but expected length=0", getIS, len(getIS))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIS(t *testing.T) {
|
|
||||||
var is []int
|
|
||||||
f := setUpISFlagSet(&is)
|
|
||||||
|
|
||||||
vals := []string{"1", "2", "4", "3"}
|
|
||||||
arg := fmt.Sprintf("--is=%s", strings.Join(vals, ","))
|
|
||||||
err := f.Parse([]string{arg})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
for i, v := range is {
|
|
||||||
d, err := strconv.Atoi(vals[i])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("got error: %v", err)
|
|
||||||
}
|
|
||||||
if d != v {
|
|
||||||
t.Fatalf("expected is[%d] to be %s but got: %d", i, vals[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getIS, err := f.GetIntSlice("is")
|
|
||||||
for i, v := range getIS {
|
|
||||||
d, err := strconv.Atoi(vals[i])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("got error: %v", err)
|
|
||||||
}
|
|
||||||
if d != v {
|
|
||||||
t.Fatalf("expected is[%d] to be %s but got: %d from GetIntSlice", i, vals[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestISDefault(t *testing.T) {
|
|
||||||
var is []int
|
|
||||||
f := setUpISFlagSetWithDefault(&is)
|
|
||||||
|
|
||||||
vals := []string{"0", "1"}
|
|
||||||
|
|
||||||
err := f.Parse([]string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
for i, v := range is {
|
|
||||||
d, err := strconv.Atoi(vals[i])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("got error: %v", err)
|
|
||||||
}
|
|
||||||
if d != v {
|
|
||||||
t.Fatalf("expected is[%d] to be %d but got: %d", i, d, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getIS, err := f.GetIntSlice("is")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("got an error from GetIntSlice():", err)
|
|
||||||
}
|
|
||||||
for i, v := range getIS {
|
|
||||||
d, err := strconv.Atoi(vals[i])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("got an error from GetIntSlice():", err)
|
|
||||||
}
|
|
||||||
if d != v {
|
|
||||||
t.Fatalf("expected is[%d] to be %d from GetIntSlice but got: %d", i, d, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestISWithDefault(t *testing.T) {
|
|
||||||
var is []int
|
|
||||||
f := setUpISFlagSetWithDefault(&is)
|
|
||||||
|
|
||||||
vals := []string{"1", "2"}
|
|
||||||
arg := fmt.Sprintf("--is=%s", strings.Join(vals, ","))
|
|
||||||
err := f.Parse([]string{arg})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
for i, v := range is {
|
|
||||||
d, err := strconv.Atoi(vals[i])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("got error: %v", err)
|
|
||||||
}
|
|
||||||
if d != v {
|
|
||||||
t.Fatalf("expected is[%d] to be %d but got: %d", i, d, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getIS, err := f.GetIntSlice("is")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("got an error from GetIntSlice():", err)
|
|
||||||
}
|
|
||||||
for i, v := range getIS {
|
|
||||||
d, err := strconv.Atoi(vals[i])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("got error: %v", err)
|
|
||||||
}
|
|
||||||
if d != v {
|
|
||||||
t.Fatalf("expected is[%d] to be %d from GetIntSlice but got: %d", i, d, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestISCalledTwice(t *testing.T) {
|
|
||||||
var is []int
|
|
||||||
f := setUpISFlagSet(&is)
|
|
||||||
|
|
||||||
in := []string{"1,2", "3"}
|
|
||||||
expected := []int{1, 2, 3}
|
|
||||||
argfmt := "--is=%s"
|
|
||||||
arg1 := fmt.Sprintf(argfmt, in[0])
|
|
||||||
arg2 := fmt.Sprintf(argfmt, in[1])
|
|
||||||
err := f.Parse([]string{arg1, arg2})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
for i, v := range is {
|
|
||||||
if expected[i] != v {
|
|
||||||
t.Fatalf("expected is[%d] to be %d but got: %d", i, expected[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-63
@@ -1,63 +0,0 @@
|
|||||||
package pflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setUpIP(ip *net.IP) *FlagSet {
|
|
||||||
f := NewFlagSet("test", ContinueOnError)
|
|
||||||
f.IPVar(ip, "address", net.ParseIP("0.0.0.0"), "IP Address")
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIP(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
input string
|
|
||||||
success bool
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"0.0.0.0", true, "0.0.0.0"},
|
|
||||||
{" 0.0.0.0 ", true, "0.0.0.0"},
|
|
||||||
{"1.2.3.4", true, "1.2.3.4"},
|
|
||||||
{"127.0.0.1", true, "127.0.0.1"},
|
|
||||||
{"255.255.255.255", true, "255.255.255.255"},
|
|
||||||
{"", false, ""},
|
|
||||||
{"0", false, ""},
|
|
||||||
{"localhost", false, ""},
|
|
||||||
{"0.0.0", false, ""},
|
|
||||||
{"0.0.0.", false, ""},
|
|
||||||
{"0.0.0.0.", false, ""},
|
|
||||||
{"0.0.0.256", false, ""},
|
|
||||||
{"0 . 0 . 0 . 0", false, ""},
|
|
||||||
}
|
|
||||||
|
|
||||||
devnull, _ := os.Open(os.DevNull)
|
|
||||||
os.Stderr = devnull
|
|
||||||
for i := range testCases {
|
|
||||||
var addr net.IP
|
|
||||||
f := setUpIP(&addr)
|
|
||||||
|
|
||||||
tc := &testCases[i]
|
|
||||||
|
|
||||||
arg := fmt.Sprintf("--address=%s", tc.input)
|
|
||||||
err := f.Parse([]string{arg})
|
|
||||||
if err != nil && tc.success == true {
|
|
||||||
t.Errorf("expected success, got %q", err)
|
|
||||||
continue
|
|
||||||
} else if err == nil && tc.success == false {
|
|
||||||
t.Errorf("expected failure")
|
|
||||||
continue
|
|
||||||
} else if tc.success {
|
|
||||||
ip, err := f.GetIP("address")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Got error trying to fetch the IP flag: %v", err)
|
|
||||||
}
|
|
||||||
if ip.String() != tc.expected {
|
|
||||||
t.Errorf("expected %q, got %q", tc.expected, ip.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-70
@@ -1,70 +0,0 @@
|
|||||||
package pflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setUpIPNet(ip *net.IPNet) *FlagSet {
|
|
||||||
f := NewFlagSet("test", ContinueOnError)
|
|
||||||
_, def, _ := net.ParseCIDR("0.0.0.0/0")
|
|
||||||
f.IPNetVar(ip, "address", *def, "IP Address")
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIPNet(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
input string
|
|
||||||
success bool
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"0.0.0.0/0", true, "0.0.0.0/0"},
|
|
||||||
{" 0.0.0.0/0 ", true, "0.0.0.0/0"},
|
|
||||||
{"1.2.3.4/8", true, "1.0.0.0/8"},
|
|
||||||
{"127.0.0.1/16", true, "127.0.0.0/16"},
|
|
||||||
{"255.255.255.255/19", true, "255.255.224.0/19"},
|
|
||||||
{"255.255.255.255/32", true, "255.255.255.255/32"},
|
|
||||||
{"", false, ""},
|
|
||||||
{"/0", false, ""},
|
|
||||||
{"0", false, ""},
|
|
||||||
{"0/0", false, ""},
|
|
||||||
{"localhost/0", false, ""},
|
|
||||||
{"0.0.0/4", false, ""},
|
|
||||||
{"0.0.0./8", false, ""},
|
|
||||||
{"0.0.0.0./12", false, ""},
|
|
||||||
{"0.0.0.256/16", false, ""},
|
|
||||||
{"0.0.0.0 /20", false, ""},
|
|
||||||
{"0.0.0.0/ 24", false, ""},
|
|
||||||
{"0 . 0 . 0 . 0 / 28", false, ""},
|
|
||||||
{"0.0.0.0/33", false, ""},
|
|
||||||
}
|
|
||||||
|
|
||||||
devnull, _ := os.Open(os.DevNull)
|
|
||||||
os.Stderr = devnull
|
|
||||||
for i := range testCases {
|
|
||||||
var addr net.IPNet
|
|
||||||
f := setUpIPNet(&addr)
|
|
||||||
|
|
||||||
tc := &testCases[i]
|
|
||||||
|
|
||||||
arg := fmt.Sprintf("--address=%s", tc.input)
|
|
||||||
err := f.Parse([]string{arg})
|
|
||||||
if err != nil && tc.success == true {
|
|
||||||
t.Errorf("expected success, got %q", err)
|
|
||||||
continue
|
|
||||||
} else if err == nil && tc.success == false {
|
|
||||||
t.Errorf("expected failure")
|
|
||||||
continue
|
|
||||||
} else if tc.success {
|
|
||||||
ip, err := f.GetIPNet("address")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Got error trying to fetch the IP flag: %v", err)
|
|
||||||
}
|
|
||||||
if ip.String() != tc.expected {
|
|
||||||
t.Errorf("expected %q, got %q", tc.expected, ip.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-161
@@ -1,161 +0,0 @@
|
|||||||
// Copyright 2009 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 pflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setUpSSFlagSet(ssp *[]string) *FlagSet {
|
|
||||||
f := NewFlagSet("test", ContinueOnError)
|
|
||||||
f.StringSliceVar(ssp, "ss", []string{}, "Command separated list!")
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUpSSFlagSetWithDefault(ssp *[]string) *FlagSet {
|
|
||||||
f := NewFlagSet("test", ContinueOnError)
|
|
||||||
f.StringSliceVar(ssp, "ss", []string{"default", "values"}, "Command separated list!")
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptySS(t *testing.T) {
|
|
||||||
var ss []string
|
|
||||||
f := setUpSSFlagSet(&ss)
|
|
||||||
err := f.Parse([]string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
getSS, err := f.GetStringSlice("ss")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("got an error from GetStringSlice():", err)
|
|
||||||
}
|
|
||||||
if len(getSS) != 0 {
|
|
||||||
t.Fatalf("got ss %v with len=%d but expected length=0", getSS, len(getSS))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSS(t *testing.T) {
|
|
||||||
var ss []string
|
|
||||||
f := setUpSSFlagSet(&ss)
|
|
||||||
|
|
||||||
vals := []string{"one", "two", "4", "3"}
|
|
||||||
arg := fmt.Sprintf("--ss=%s", strings.Join(vals, ","))
|
|
||||||
err := f.Parse([]string{arg})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
for i, v := range ss {
|
|
||||||
if vals[i] != v {
|
|
||||||
t.Fatalf("expected ss[%d] to be %s but got: %s", i, vals[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSS, err := f.GetStringSlice("ss")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("got an error from GetStringSlice():", err)
|
|
||||||
}
|
|
||||||
for i, v := range getSS {
|
|
||||||
if vals[i] != v {
|
|
||||||
t.Fatalf("expected ss[%d] to be %s from GetStringSlice but got: %s", i, vals[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSSDefault(t *testing.T) {
|
|
||||||
var ss []string
|
|
||||||
f := setUpSSFlagSetWithDefault(&ss)
|
|
||||||
|
|
||||||
vals := []string{"default", "values"}
|
|
||||||
|
|
||||||
err := f.Parse([]string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
for i, v := range ss {
|
|
||||||
if vals[i] != v {
|
|
||||||
t.Fatalf("expected ss[%d] to be %s but got: %s", i, vals[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSS, err := f.GetStringSlice("ss")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("got an error from GetStringSlice():", err)
|
|
||||||
}
|
|
||||||
for i, v := range getSS {
|
|
||||||
if vals[i] != v {
|
|
||||||
t.Fatalf("expected ss[%d] to be %s from GetStringSlice but got: %s", i, vals[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSSWithDefault(t *testing.T) {
|
|
||||||
var ss []string
|
|
||||||
f := setUpSSFlagSetWithDefault(&ss)
|
|
||||||
|
|
||||||
vals := []string{"one", "two", "4", "3"}
|
|
||||||
arg := fmt.Sprintf("--ss=%s", strings.Join(vals, ","))
|
|
||||||
err := f.Parse([]string{arg})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
for i, v := range ss {
|
|
||||||
if vals[i] != v {
|
|
||||||
t.Fatalf("expected ss[%d] to be %s but got: %s", i, vals[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSS, err := f.GetStringSlice("ss")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("got an error from GetStringSlice():", err)
|
|
||||||
}
|
|
||||||
for i, v := range getSS {
|
|
||||||
if vals[i] != v {
|
|
||||||
t.Fatalf("expected ss[%d] to be %s from GetStringSlice but got: %s", i, vals[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSSCalledTwice(t *testing.T) {
|
|
||||||
var ss []string
|
|
||||||
f := setUpSSFlagSet(&ss)
|
|
||||||
|
|
||||||
in := []string{"one,two", "three"}
|
|
||||||
expected := []string{"one", "two", "three"}
|
|
||||||
argfmt := "--ss=%s"
|
|
||||||
arg1 := fmt.Sprintf(argfmt, in[0])
|
|
||||||
arg2 := fmt.Sprintf(argfmt, in[1])
|
|
||||||
err := f.Parse([]string{arg1, arg2})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
for i, v := range ss {
|
|
||||||
if expected[i] != v {
|
|
||||||
t.Fatalf("expected ss[%d] to be %s but got: %s", i, expected[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSSWithComma(t *testing.T) {
|
|
||||||
var ss []string
|
|
||||||
f := setUpSSFlagSet(&ss)
|
|
||||||
|
|
||||||
in := []string{`"one,two"`, `"three"`}
|
|
||||||
expected := []string{"one,two", "three"}
|
|
||||||
argfmt := "--ss=%s"
|
|
||||||
arg1 := fmt.Sprintf(argfmt, in[0])
|
|
||||||
arg2 := fmt.Sprintf(argfmt, in[1])
|
|
||||||
err := f.Parse([]string{arg1, arg2})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("expected no error; got", err)
|
|
||||||
}
|
|
||||||
for i, v := range ss {
|
|
||||||
if expected[i] != v {
|
|
||||||
t.Fatalf("expected ss[%d] to be %s but got: %s", i, expected[i], v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-69
@@ -1,69 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -o errexit
|
|
||||||
set -o nounset
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
ROOT=$(dirname "${BASH_SOURCE}")/..
|
|
||||||
|
|
||||||
# Some useful colors.
|
|
||||||
if [[ -z "${color_start-}" ]]; then
|
|
||||||
declare -r color_start="\033["
|
|
||||||
declare -r color_red="${color_start}0;31m"
|
|
||||||
declare -r color_yellow="${color_start}0;33m"
|
|
||||||
declare -r color_green="${color_start}0;32m"
|
|
||||||
declare -r color_norm="${color_start}0m"
|
|
||||||
fi
|
|
||||||
|
|
||||||
SILENT=true
|
|
||||||
|
|
||||||
function is-excluded {
|
|
||||||
for e in $EXCLUDE; do
|
|
||||||
if [[ $1 -ef ${BASH_SOURCE} ]]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
if [[ $1 -ef "$ROOT/hack/$e" ]]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
while getopts ":v" opt; do
|
|
||||||
case $opt in
|
|
||||||
v)
|
|
||||||
SILENT=false
|
|
||||||
;;
|
|
||||||
\?)
|
|
||||||
echo "Invalid flag: -$OPTARG" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if $SILENT ; then
|
|
||||||
echo "Running in the silent mode, run with -v if you want to see script logs."
|
|
||||||
fi
|
|
||||||
|
|
||||||
EXCLUDE="all.sh"
|
|
||||||
|
|
||||||
ret=0
|
|
||||||
for t in `ls $ROOT/verify/*.sh`
|
|
||||||
do
|
|
||||||
if is-excluded $t ; then
|
|
||||||
echo "Skipping $t"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
if $SILENT ; then
|
|
||||||
echo -e "Verifying $t"
|
|
||||||
if bash "$t" &> /dev/null; then
|
|
||||||
echo -e "${color_green}SUCCESS${color_norm}"
|
|
||||||
else
|
|
||||||
echo -e "${color_red}FAILED${color_norm}"
|
|
||||||
ret=1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
bash "$t" || ret=1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
exit $ret
|
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -o errexit
|
|
||||||
set -o nounset
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
ROOT=$(dirname "${BASH_SOURCE}")/..
|
|
||||||
|
|
||||||
pushd "${ROOT}" > /dev/null
|
|
||||||
|
|
||||||
GOFMT=${GOFMT:-"gofmt"}
|
|
||||||
bad_files=$(find . -name '*.go' | xargs $GOFMT -s -l)
|
|
||||||
if [[ -n "${bad_files}" ]]; then
|
|
||||||
echo "!!! '$GOFMT' needs to be run on the following files: "
|
|
||||||
echo "${bad_files}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ex: ts=2 sw=2 et filetype=sh
|
|
||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
ROOT=$(dirname "${BASH_SOURCE}")/..
|
|
||||||
GOLINT=${GOLINT:-"golint"}
|
|
||||||
|
|
||||||
pushd "${ROOT}" > /dev/null
|
|
||||||
bad_files=$($GOLINT -min_confidence=0.9 ./...)
|
|
||||||
if [[ -n "${bad_files}" ]]; then
|
|
||||||
echo "!!! '$GOLINT' problems: "
|
|
||||||
echo "${bad_files}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
popd > /dev/null
|
|
||||||
|
|
||||||
# ex: ts=2 sw=2 et filetype=sh
|
|
||||||
-575
@@ -1,575 +0,0 @@
|
|||||||
// Copyright 2014 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 context
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// otherContext is a Context that's not one of the types defined in context.go.
|
|
||||||
// This lets us test code paths that differ based on the underlying type of the
|
|
||||||
// Context.
|
|
||||||
type otherContext struct {
|
|
||||||
Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBackground(t *testing.T) {
|
|
||||||
c := Background()
|
|
||||||
if c == nil {
|
|
||||||
t.Fatalf("Background returned nil")
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case x := <-c.Done():
|
|
||||||
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if got, want := fmt.Sprint(c), "context.Background"; got != want {
|
|
||||||
t.Errorf("Background().String() = %q want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTODO(t *testing.T) {
|
|
||||||
c := TODO()
|
|
||||||
if c == nil {
|
|
||||||
t.Fatalf("TODO returned nil")
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case x := <-c.Done():
|
|
||||||
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if got, want := fmt.Sprint(c), "context.TODO"; got != want {
|
|
||||||
t.Errorf("TODO().String() = %q want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithCancel(t *testing.T) {
|
|
||||||
c1, cancel := WithCancel(Background())
|
|
||||||
|
|
||||||
if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
|
|
||||||
t.Errorf("c1.String() = %q want %q", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
o := otherContext{c1}
|
|
||||||
c2, _ := WithCancel(o)
|
|
||||||
contexts := []Context{c1, o, c2}
|
|
||||||
|
|
||||||
for i, c := range contexts {
|
|
||||||
if d := c.Done(); d == nil {
|
|
||||||
t.Errorf("c[%d].Done() == %v want non-nil", i, d)
|
|
||||||
}
|
|
||||||
if e := c.Err(); e != nil {
|
|
||||||
t.Errorf("c[%d].Err() == %v want nil", i, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case x := <-c.Done():
|
|
||||||
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel()
|
|
||||||
time.Sleep(100 * time.Millisecond) // let cancelation propagate
|
|
||||||
|
|
||||||
for i, c := range contexts {
|
|
||||||
select {
|
|
||||||
case <-c.Done():
|
|
||||||
default:
|
|
||||||
t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i)
|
|
||||||
}
|
|
||||||
if e := c.Err(); e != Canceled {
|
|
||||||
t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParentFinishesChild(t *testing.T) {
|
|
||||||
// Context tree:
|
|
||||||
// parent -> cancelChild
|
|
||||||
// parent -> valueChild -> timerChild
|
|
||||||
parent, cancel := WithCancel(Background())
|
|
||||||
cancelChild, stop := WithCancel(parent)
|
|
||||||
defer stop()
|
|
||||||
valueChild := WithValue(parent, "key", "value")
|
|
||||||
timerChild, stop := WithTimeout(valueChild, 10000*time.Hour)
|
|
||||||
defer stop()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case x := <-parent.Done():
|
|
||||||
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
|
|
||||||
case x := <-cancelChild.Done():
|
|
||||||
t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
|
|
||||||
case x := <-timerChild.Done():
|
|
||||||
t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
|
|
||||||
case x := <-valueChild.Done():
|
|
||||||
t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
// The parent's children should contain the two cancelable children.
|
|
||||||
pc := parent.(*cancelCtx)
|
|
||||||
cc := cancelChild.(*cancelCtx)
|
|
||||||
tc := timerChild.(*timerCtx)
|
|
||||||
pc.mu.Lock()
|
|
||||||
if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] {
|
|
||||||
t.Errorf("bad linkage: pc.children = %v, want %v and %v",
|
|
||||||
pc.children, cc, tc)
|
|
||||||
}
|
|
||||||
pc.mu.Unlock()
|
|
||||||
|
|
||||||
if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
|
|
||||||
t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
|
|
||||||
}
|
|
||||||
if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
|
|
||||||
t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
pc.mu.Lock()
|
|
||||||
if len(pc.children) != 0 {
|
|
||||||
t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
|
|
||||||
}
|
|
||||||
pc.mu.Unlock()
|
|
||||||
|
|
||||||
// parent and children should all be finished.
|
|
||||||
check := func(ctx Context, name string) {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
default:
|
|
||||||
t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
|
|
||||||
}
|
|
||||||
if e := ctx.Err(); e != Canceled {
|
|
||||||
t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check(parent, "parent")
|
|
||||||
check(cancelChild, "cancelChild")
|
|
||||||
check(valueChild, "valueChild")
|
|
||||||
check(timerChild, "timerChild")
|
|
||||||
|
|
||||||
// WithCancel should return a canceled context on a canceled parent.
|
|
||||||
precanceledChild := WithValue(parent, "key", "value")
|
|
||||||
select {
|
|
||||||
case <-precanceledChild.Done():
|
|
||||||
default:
|
|
||||||
t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
|
|
||||||
}
|
|
||||||
if e := precanceledChild.Err(); e != Canceled {
|
|
||||||
t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChildFinishesFirst(t *testing.T) {
|
|
||||||
cancelable, stop := WithCancel(Background())
|
|
||||||
defer stop()
|
|
||||||
for _, parent := range []Context{Background(), cancelable} {
|
|
||||||
child, cancel := WithCancel(parent)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case x := <-parent.Done():
|
|
||||||
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
|
|
||||||
case x := <-child.Done():
|
|
||||||
t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
cc := child.(*cancelCtx)
|
|
||||||
pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background()
|
|
||||||
if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
|
|
||||||
t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pcok {
|
|
||||||
pc.mu.Lock()
|
|
||||||
if len(pc.children) != 1 || !pc.children[cc] {
|
|
||||||
t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
|
|
||||||
}
|
|
||||||
pc.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
if pcok {
|
|
||||||
pc.mu.Lock()
|
|
||||||
if len(pc.children) != 0 {
|
|
||||||
t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
|
|
||||||
}
|
|
||||||
pc.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// child should be finished.
|
|
||||||
select {
|
|
||||||
case <-child.Done():
|
|
||||||
default:
|
|
||||||
t.Errorf("<-child.Done() blocked, but shouldn't have")
|
|
||||||
}
|
|
||||||
if e := child.Err(); e != Canceled {
|
|
||||||
t.Errorf("child.Err() == %v want %v", e, Canceled)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parent should not be finished.
|
|
||||||
select {
|
|
||||||
case x := <-parent.Done():
|
|
||||||
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if e := parent.Err(); e != nil {
|
|
||||||
t.Errorf("parent.Err() == %v want nil", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testDeadline(c Context, wait time.Duration, t *testing.T) {
|
|
||||||
select {
|
|
||||||
case <-time.After(wait):
|
|
||||||
t.Fatalf("context should have timed out")
|
|
||||||
case <-c.Done():
|
|
||||||
}
|
|
||||||
if e := c.Err(); e != DeadlineExceeded {
|
|
||||||
t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeadline(t *testing.T) {
|
|
||||||
c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
|
|
||||||
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
|
|
||||||
t.Errorf("c.String() = %q want prefix %q", got, prefix)
|
|
||||||
}
|
|
||||||
testDeadline(c, 200*time.Millisecond, t)
|
|
||||||
|
|
||||||
c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
|
|
||||||
o := otherContext{c}
|
|
||||||
testDeadline(o, 200*time.Millisecond, t)
|
|
||||||
|
|
||||||
c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
|
|
||||||
o = otherContext{c}
|
|
||||||
c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond))
|
|
||||||
testDeadline(c, 200*time.Millisecond, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimeout(t *testing.T) {
|
|
||||||
c, _ := WithTimeout(Background(), 100*time.Millisecond)
|
|
||||||
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
|
|
||||||
t.Errorf("c.String() = %q want prefix %q", got, prefix)
|
|
||||||
}
|
|
||||||
testDeadline(c, 200*time.Millisecond, t)
|
|
||||||
|
|
||||||
c, _ = WithTimeout(Background(), 100*time.Millisecond)
|
|
||||||
o := otherContext{c}
|
|
||||||
testDeadline(o, 200*time.Millisecond, t)
|
|
||||||
|
|
||||||
c, _ = WithTimeout(Background(), 100*time.Millisecond)
|
|
||||||
o = otherContext{c}
|
|
||||||
c, _ = WithTimeout(o, 300*time.Millisecond)
|
|
||||||
testDeadline(c, 200*time.Millisecond, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanceledTimeout(t *testing.T) {
|
|
||||||
c, _ := WithTimeout(Background(), 200*time.Millisecond)
|
|
||||||
o := otherContext{c}
|
|
||||||
c, cancel := WithTimeout(o, 400*time.Millisecond)
|
|
||||||
cancel()
|
|
||||||
time.Sleep(100 * time.Millisecond) // let cancelation propagate
|
|
||||||
select {
|
|
||||||
case <-c.Done():
|
|
||||||
default:
|
|
||||||
t.Errorf("<-c.Done() blocked, but shouldn't have")
|
|
||||||
}
|
|
||||||
if e := c.Err(); e != Canceled {
|
|
||||||
t.Errorf("c.Err() == %v want %v", e, Canceled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type key1 int
|
|
||||||
type key2 int
|
|
||||||
|
|
||||||
var k1 = key1(1)
|
|
||||||
var k2 = key2(1) // same int as k1, different type
|
|
||||||
var k3 = key2(3) // same type as k2, different int
|
|
||||||
|
|
||||||
func TestValues(t *testing.T) {
|
|
||||||
check := func(c Context, nm, v1, v2, v3 string) {
|
|
||||||
if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
|
|
||||||
t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
|
|
||||||
}
|
|
||||||
if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
|
|
||||||
t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
|
|
||||||
}
|
|
||||||
if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
|
|
||||||
t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c0 := Background()
|
|
||||||
check(c0, "c0", "", "", "")
|
|
||||||
|
|
||||||
c1 := WithValue(Background(), k1, "c1k1")
|
|
||||||
check(c1, "c1", "c1k1", "", "")
|
|
||||||
|
|
||||||
if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want {
|
|
||||||
t.Errorf("c.String() = %q want %q", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
c2 := WithValue(c1, k2, "c2k2")
|
|
||||||
check(c2, "c2", "c1k1", "c2k2", "")
|
|
||||||
|
|
||||||
c3 := WithValue(c2, k3, "c3k3")
|
|
||||||
check(c3, "c2", "c1k1", "c2k2", "c3k3")
|
|
||||||
|
|
||||||
c4 := WithValue(c3, k1, nil)
|
|
||||||
check(c4, "c4", "", "c2k2", "c3k3")
|
|
||||||
|
|
||||||
o0 := otherContext{Background()}
|
|
||||||
check(o0, "o0", "", "", "")
|
|
||||||
|
|
||||||
o1 := otherContext{WithValue(Background(), k1, "c1k1")}
|
|
||||||
check(o1, "o1", "c1k1", "", "")
|
|
||||||
|
|
||||||
o2 := WithValue(o1, k2, "o2k2")
|
|
||||||
check(o2, "o2", "c1k1", "o2k2", "")
|
|
||||||
|
|
||||||
o3 := otherContext{c4}
|
|
||||||
check(o3, "o3", "", "c2k2", "c3k3")
|
|
||||||
|
|
||||||
o4 := WithValue(o3, k3, nil)
|
|
||||||
check(o4, "o4", "", "c2k2", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAllocs(t *testing.T) {
|
|
||||||
bg := Background()
|
|
||||||
for _, test := range []struct {
|
|
||||||
desc string
|
|
||||||
f func()
|
|
||||||
limit float64
|
|
||||||
gccgoLimit float64
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "Background()",
|
|
||||||
f: func() { Background() },
|
|
||||||
limit: 0,
|
|
||||||
gccgoLimit: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
|
|
||||||
f: func() {
|
|
||||||
c := WithValue(bg, k1, nil)
|
|
||||||
c.Value(k1)
|
|
||||||
},
|
|
||||||
limit: 3,
|
|
||||||
gccgoLimit: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "WithTimeout(bg, 15*time.Millisecond)",
|
|
||||||
f: func() {
|
|
||||||
c, _ := WithTimeout(bg, 15*time.Millisecond)
|
|
||||||
<-c.Done()
|
|
||||||
},
|
|
||||||
limit: 8,
|
|
||||||
gccgoLimit: 13,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "WithCancel(bg)",
|
|
||||||
f: func() {
|
|
||||||
c, cancel := WithCancel(bg)
|
|
||||||
cancel()
|
|
||||||
<-c.Done()
|
|
||||||
},
|
|
||||||
limit: 5,
|
|
||||||
gccgoLimit: 8,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "WithTimeout(bg, 100*time.Millisecond)",
|
|
||||||
f: func() {
|
|
||||||
c, cancel := WithTimeout(bg, 100*time.Millisecond)
|
|
||||||
cancel()
|
|
||||||
<-c.Done()
|
|
||||||
},
|
|
||||||
limit: 8,
|
|
||||||
gccgoLimit: 25,
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
limit := test.limit
|
|
||||||
if runtime.Compiler == "gccgo" {
|
|
||||||
// gccgo does not yet do escape analysis.
|
|
||||||
// TOOD(iant): Remove this when gccgo does do escape analysis.
|
|
||||||
limit = test.gccgoLimit
|
|
||||||
}
|
|
||||||
if n := testing.AllocsPerRun(100, test.f); n > limit {
|
|
||||||
t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSimultaneousCancels(t *testing.T) {
|
|
||||||
root, cancel := WithCancel(Background())
|
|
||||||
m := map[Context]CancelFunc{root: cancel}
|
|
||||||
q := []Context{root}
|
|
||||||
// Create a tree of contexts.
|
|
||||||
for len(q) != 0 && len(m) < 100 {
|
|
||||||
parent := q[0]
|
|
||||||
q = q[1:]
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
ctx, cancel := WithCancel(parent)
|
|
||||||
m[ctx] = cancel
|
|
||||||
q = append(q, ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Start all the cancels in a random order.
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(len(m))
|
|
||||||
for _, cancel := range m {
|
|
||||||
go func(cancel CancelFunc) {
|
|
||||||
cancel()
|
|
||||||
wg.Done()
|
|
||||||
}(cancel)
|
|
||||||
}
|
|
||||||
// Wait on all the contexts in a random order.
|
|
||||||
for ctx := range m {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
case <-time.After(1 * time.Second):
|
|
||||||
buf := make([]byte, 10<<10)
|
|
||||||
n := runtime.Stack(buf, true)
|
|
||||||
t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Wait for all the cancel functions to return.
|
|
||||||
done := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
case <-time.After(1 * time.Second):
|
|
||||||
buf := make([]byte, 10<<10)
|
|
||||||
n := runtime.Stack(buf, true)
|
|
||||||
t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterlockedCancels(t *testing.T) {
|
|
||||||
parent, cancelParent := WithCancel(Background())
|
|
||||||
child, cancelChild := WithCancel(parent)
|
|
||||||
go func() {
|
|
||||||
parent.Done()
|
|
||||||
cancelChild()
|
|
||||||
}()
|
|
||||||
cancelParent()
|
|
||||||
select {
|
|
||||||
case <-child.Done():
|
|
||||||
case <-time.After(1 * time.Second):
|
|
||||||
buf := make([]byte, 10<<10)
|
|
||||||
n := runtime.Stack(buf, true)
|
|
||||||
t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLayersCancel(t *testing.T) {
|
|
||||||
testLayers(t, time.Now().UnixNano(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLayersTimeout(t *testing.T) {
|
|
||||||
testLayers(t, time.Now().UnixNano(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLayers(t *testing.T, seed int64, testTimeout bool) {
|
|
||||||
rand.Seed(seed)
|
|
||||||
errorf := func(format string, a ...interface{}) {
|
|
||||||
t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
|
|
||||||
}
|
|
||||||
const (
|
|
||||||
timeout = 200 * time.Millisecond
|
|
||||||
minLayers = 30
|
|
||||||
)
|
|
||||||
type value int
|
|
||||||
var (
|
|
||||||
vals []*value
|
|
||||||
cancels []CancelFunc
|
|
||||||
numTimers int
|
|
||||||
ctx = Background()
|
|
||||||
)
|
|
||||||
for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
|
|
||||||
switch rand.Intn(3) {
|
|
||||||
case 0:
|
|
||||||
v := new(value)
|
|
||||||
ctx = WithValue(ctx, v, v)
|
|
||||||
vals = append(vals, v)
|
|
||||||
case 1:
|
|
||||||
var cancel CancelFunc
|
|
||||||
ctx, cancel = WithCancel(ctx)
|
|
||||||
cancels = append(cancels, cancel)
|
|
||||||
case 2:
|
|
||||||
var cancel CancelFunc
|
|
||||||
ctx, cancel = WithTimeout(ctx, timeout)
|
|
||||||
cancels = append(cancels, cancel)
|
|
||||||
numTimers++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkValues := func(when string) {
|
|
||||||
for _, key := range vals {
|
|
||||||
if val := ctx.Value(key).(*value); key != val {
|
|
||||||
errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
errorf("ctx should not be canceled yet")
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
|
|
||||||
t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
|
|
||||||
}
|
|
||||||
t.Log(ctx)
|
|
||||||
checkValues("before cancel")
|
|
||||||
if testTimeout {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
case <-time.After(timeout + timeout/10):
|
|
||||||
errorf("ctx should have timed out")
|
|
||||||
}
|
|
||||||
checkValues("after timeout")
|
|
||||||
} else {
|
|
||||||
cancel := cancels[rand.Intn(len(cancels))]
|
|
||||||
cancel()
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
default:
|
|
||||||
errorf("ctx should be canceled")
|
|
||||||
}
|
|
||||||
checkValues("after cancel")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCancelRemoves(t *testing.T) {
|
|
||||||
checkChildren := func(when string, ctx Context, want int) {
|
|
||||||
if got := len(ctx.(*cancelCtx).children); got != want {
|
|
||||||
t.Errorf("%s: context has %d children, want %d", when, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, _ := WithCancel(Background())
|
|
||||||
checkChildren("after creation", ctx, 0)
|
|
||||||
_, cancel := WithCancel(ctx)
|
|
||||||
checkChildren("with WithCancel child ", ctx, 1)
|
|
||||||
cancel()
|
|
||||||
checkChildren("after cancelling WithCancel child", ctx, 0)
|
|
||||||
|
|
||||||
ctx, _ = WithCancel(Background())
|
|
||||||
checkChildren("after creation", ctx, 0)
|
|
||||||
_, cancel = WithTimeout(ctx, 60*time.Minute)
|
|
||||||
checkChildren("with WithTimeout child ", ctx, 1)
|
|
||||||
cancel()
|
|
||||||
checkChildren("after cancelling WithTimeout child", ctx, 0)
|
|
||||||
}
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
// Copyright 2014 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 context_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleWithTimeout() {
|
|
||||||
// Pass a context with a timeout to tell a blocking function that it
|
|
||||||
// should abandon its work after the timeout elapses.
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
||||||
select {
|
|
||||||
case <-time.After(200 * time.Millisecond):
|
|
||||||
fmt.Println("overslept")
|
|
||||||
case <-ctx.Done():
|
|
||||||
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
|
|
||||||
}
|
|
||||||
// Output:
|
|
||||||
// context deadline exceeded
|
|
||||||
}
|
|
||||||
-55
@@ -1,55 +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 proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type recordingProxy struct {
|
|
||||||
addrs []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *recordingProxy) Dial(network, addr string) (net.Conn, error) {
|
|
||||||
r.addrs = append(r.addrs, addr)
|
|
||||||
return nil, errors.New("recordingProxy")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPerHost(t *testing.T) {
|
|
||||||
var def, bypass recordingProxy
|
|
||||||
perHost := NewPerHost(&def, &bypass)
|
|
||||||
perHost.AddFromString("localhost,*.zone,127.0.0.1,10.0.0.1/8,1000::/16")
|
|
||||||
|
|
||||||
expectedDef := []string{
|
|
||||||
"example.com:123",
|
|
||||||
"1.2.3.4:123",
|
|
||||||
"[1001::]:123",
|
|
||||||
}
|
|
||||||
expectedBypass := []string{
|
|
||||||
"localhost:123",
|
|
||||||
"zone:123",
|
|
||||||
"foo.zone:123",
|
|
||||||
"127.0.0.1:123",
|
|
||||||
"10.1.2.3:123",
|
|
||||||
"[1000::]:123",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, addr := range expectedDef {
|
|
||||||
perHost.Dial("tcp", addr)
|
|
||||||
}
|
|
||||||
for _, addr := range expectedBypass {
|
|
||||||
perHost.Dial("tcp", addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(expectedDef, def.addrs) {
|
|
||||||
t.Errorf("Hosts which went to the default proxy didn't match. Got %v, want %v", def.addrs, expectedDef)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(expectedBypass, bypass.addrs) {
|
|
||||||
t.Errorf("Hosts which went to the bypass proxy didn't match. Got %v, want %v", bypass.addrs, expectedBypass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-142
@@ -1,142 +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 proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFromURL(t *testing.T) {
|
|
||||||
endSystem, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("net.Listen failed: %v", err)
|
|
||||||
}
|
|
||||||
defer endSystem.Close()
|
|
||||||
gateway, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("net.Listen failed: %v", err)
|
|
||||||
}
|
|
||||||
defer gateway.Close()
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
go socks5Gateway(t, gateway, endSystem, socks5Domain, &wg)
|
|
||||||
|
|
||||||
url, err := url.Parse("socks5://user:password@" + gateway.Addr().String())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("url.Parse failed: %v", err)
|
|
||||||
}
|
|
||||||
proxy, err := FromURL(url, Direct)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("FromURL failed: %v", err)
|
|
||||||
}
|
|
||||||
_, port, err := net.SplitHostPort(endSystem.Addr().String())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("net.SplitHostPort failed: %v", err)
|
|
||||||
}
|
|
||||||
if c, err := proxy.Dial("tcp", "localhost:"+port); err != nil {
|
|
||||||
t.Fatalf("FromURL.Dial failed: %v", err)
|
|
||||||
} else {
|
|
||||||
c.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSOCKS5(t *testing.T) {
|
|
||||||
endSystem, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("net.Listen failed: %v", err)
|
|
||||||
}
|
|
||||||
defer endSystem.Close()
|
|
||||||
gateway, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("net.Listen failed: %v", err)
|
|
||||||
}
|
|
||||||
defer gateway.Close()
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
go socks5Gateway(t, gateway, endSystem, socks5IP4, &wg)
|
|
||||||
|
|
||||||
proxy, err := SOCKS5("tcp", gateway.Addr().String(), nil, Direct)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("SOCKS5 failed: %v", err)
|
|
||||||
}
|
|
||||||
if c, err := proxy.Dial("tcp", endSystem.Addr().String()); err != nil {
|
|
||||||
t.Fatalf("SOCKS5.Dial failed: %v", err)
|
|
||||||
} else {
|
|
||||||
c.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func socks5Gateway(t *testing.T, gateway, endSystem net.Listener, typ byte, wg *sync.WaitGroup) {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
c, err := gateway.Accept()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("net.Listener.Accept failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
b := make([]byte, 32)
|
|
||||||
var n int
|
|
||||||
if typ == socks5Domain {
|
|
||||||
n = 4
|
|
||||||
} else {
|
|
||||||
n = 3
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(c, b[:n]); err != nil {
|
|
||||||
t.Errorf("io.ReadFull failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := c.Write([]byte{socks5Version, socks5AuthNone}); err != nil {
|
|
||||||
t.Errorf("net.Conn.Write failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if typ == socks5Domain {
|
|
||||||
n = 16
|
|
||||||
} else {
|
|
||||||
n = 10
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(c, b[:n]); err != nil {
|
|
||||||
t.Errorf("io.ReadFull failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if b[0] != socks5Version || b[1] != socks5Connect || b[2] != 0x00 || b[3] != typ {
|
|
||||||
t.Errorf("got an unexpected packet: %#02x %#02x %#02x %#02x", b[0], b[1], b[2], b[3])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if typ == socks5Domain {
|
|
||||||
copy(b[:5], []byte{socks5Version, 0x00, 0x00, socks5Domain, 9})
|
|
||||||
b = append(b, []byte("localhost")...)
|
|
||||||
} else {
|
|
||||||
copy(b[:4], []byte{socks5Version, 0x00, 0x00, socks5IP4})
|
|
||||||
}
|
|
||||||
host, port, err := net.SplitHostPort(endSystem.Addr().String())
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("net.SplitHostPort failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b = append(b, []byte(net.ParseIP(host).To4())...)
|
|
||||||
p, err := strconv.Atoi(port)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("strconv.Atoi failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b = append(b, []byte{byte(p >> 8), byte(p)}...)
|
|
||||||
if _, err := c.Write(b); err != nil {
|
|
||||||
t.Errorf("net.Conn.Write failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Generated
+129
@@ -0,0 +1,129 @@
|
|||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:fb33d88060f0064499d409bddd1ae4ef7028a1646d536fbf0d967a2bffcb4018"
|
||||||
|
name = "github.com/Microsoft/go-winio"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "8f9387ea7efabb228a981b9c381142be7667967f"
|
||||||
|
version = "v0.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:8f09ad7fe61704c7ed549f70d6709e7bf59cad64bd64b0eb4be79b3305fb257c"
|
||||||
|
name = "github.com/Sirupsen/logrus"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "f3cfb454f4c209e6668c95216c4744b8fddb2356"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:30f50828e5a6aa9257130334f34425a1bcba4e19b9f531f1da7747fbdc3235be"
|
||||||
|
name = "github.com/ceph/go-ceph"
|
||||||
|
packages = [
|
||||||
|
"rados",
|
||||||
|
"rbd",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "bd5bc6d4cb3e3d3441f2ec4e9f89899178edfc71"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:37b31900270ee8fa466a1879fb6f5920fcb8a7e9a7130769a5bc17b844441fb6"
|
||||||
|
name = "github.com/coreos/go-systemd"
|
||||||
|
packages = ["activation"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "6dc8b843c670f2027cc26b164935635840a40526"
|
||||||
|
version = "v9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:42a694aa820ffbb565bcb52ae1f2d1c11b341c1fadc447d05db2a66a68581bcc"
|
||||||
|
name = "github.com/docker/go-connections"
|
||||||
|
packages = ["sockets"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "990a1a1a70b0da4c4cb70e117971a4f0babfbf1a"
|
||||||
|
version = "v0.2.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:874ca00e54068a78ce555979489511ec62cd4682bf068134dc4355753827895c"
|
||||||
|
name = "github.com/gorilla/context"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "215affda49addc4c8ef7e2534915df2c8c35c6cd"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:a6283ced592faaeedc9f622fc7ec96bdcd3e97372457d2bc81b7307bf873ce4c"
|
||||||
|
name = "github.com/gorilla/mux"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "8096f47503459bcc74d1f4c487b7e6e42e5746b5"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
|
||||||
|
name = "github.com/inconshreveable/mousetrap"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||||
|
version = "v1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:1cff1ec506a4e1660095f349326b13b365dd4457fa666ed9e159b9d29694a360"
|
||||||
|
name = "github.com/opencontainers/runc"
|
||||||
|
packages = ["libcontainer/user"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "47e3f834d73e76bc2a6a585b48d2a93325b34979"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:274f67cb6fed9588ea2521ecdac05a6d62a8c51c074c1fccc6a49a40ba80e925"
|
||||||
|
name = "github.com/satori/go.uuid"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3"
|
||||||
|
version = "v1.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:bcc9aae4fb6d8d13ad94762090d9fe03a4d082f6682949e4a19180843afcc301"
|
||||||
|
name = "github.com/spf13/cobra"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "1238ba19d24b0b9ceee2094e1cb31947d45c3e86"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:ea972f0a9584308d97c107a818dbb340e4fdf261105cf9b0c51fa2987f283a05"
|
||||||
|
name = "github.com/spf13/pflag"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "367864438f1b1a3c7db4da06a2f55b144e6784e0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:007c759d6b90bf206d4628e4ff501c20bcc3967fb15baec2e95f6e9af9c7b14d"
|
||||||
|
name = "golang.org/x/net"
|
||||||
|
packages = [
|
||||||
|
"context",
|
||||||
|
"proxy",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "d9558e5c97f85372afee28cf2b6059d7d3818919"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:466e5917ea5f69d435d0ab3015dc4b57b80d34a6caf1104097ac82ab462b86e4"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
packages = ["unix"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "7e31e0c00fa05cb5fbf4347b585621d6709e19a4"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
input-imports = [
|
||||||
|
"github.com/Sirupsen/logrus",
|
||||||
|
"github.com/ceph/go-ceph/rados",
|
||||||
|
"github.com/ceph/go-ceph/rbd",
|
||||||
|
"github.com/coreos/go-systemd/activation",
|
||||||
|
"github.com/docker/go-connections/sockets",
|
||||||
|
"github.com/gorilla/mux",
|
||||||
|
"github.com/satori/go.uuid",
|
||||||
|
"github.com/spf13/cobra",
|
||||||
|
"golang.org/x/net/context",
|
||||||
|
]
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/coreos/go-systemd"
|
||||||
|
version = "9.0.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/docker/go-connections"
|
||||||
|
version = "0.2.0-8-g990a1a1"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/satori/go.uuid"
|
||||||
|
version = "1.2.0"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
||||||
Generated
Vendored
Generated
Vendored
+27
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user