diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 639812eca8..5bb8746a01 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -34,6 +34,9 @@ Please submit a PR to contain changes bit by bit. A PR consisting of a lot featu
You can propose new designs for existing Harbor features. You can also design
entirely new features. Please do open an issue on Github for discussion first. This is necessary to ensure the overall architecture is consistent and to avoid duplicated work in the roadmap.
+### Dependency management
+Harbor uses [dep](https://github.com/golang/dep) for dependency management of go code. The official maintainers will take the responsibility for managing the code in `vendor` directory. Please don't try to submit PR to update the dependency code, open an issue instead. If your PR requires a change in the vendor code please make sure you discuss it with maintainers in advance.
+
### Conventions
Fork Harbor's repository and make changes on your own fork in a new branch. The branch should be named XXX-description where XXX is the number of the issue. Please run the full test suite on your branch before creating a PR.
diff --git a/src/Gopkg.lock b/src/Gopkg.lock
new file mode 100644
index 0000000000..620abcfa46
--- /dev/null
+++ b/src/Gopkg.lock
@@ -0,0 +1,250 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+ name = "github.com/Sirupsen/logrus"
+ packages = ["."]
+ revision = "a283a10442df8dc09befd873fab202bf8a253d6a"
+
+[[projects]]
+ name = "github.com/Unknwon/goconfig"
+ packages = ["."]
+ revision = "5f601ca6ef4d5cea8d52be2f8b3a420ee4b574a5"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/agl/ed25519"
+ packages = [
+ ".",
+ "edwards25519"
+ ]
+ revision = "5312a61534124124185d41f09206b9fef1d88403"
+
+[[projects]]
+ name = "github.com/astaxie/beego"
+ packages = [
+ ".",
+ "cache",
+ "config",
+ "context",
+ "grace",
+ "logs",
+ "orm",
+ "session",
+ "session/redis",
+ "toolbox",
+ "utils",
+ "validation"
+ ]
+ revision = "1aeb3d90512734def678c7aa9f612fe6f659e6b5"
+ version = "v1.6.1"
+
+[[projects]]
+ name = "github.com/beego/i18n"
+ packages = ["."]
+ revision = "e87155e8f0c05bf323d0b13470e1b97af0cb5652"
+
+[[projects]]
+ name = "github.com/davecgh/go-spew"
+ packages = ["spew"]
+ revision = "346938d642f2ec3594ed81d874461961cd0faa76"
+ version = "v1.1.0"
+
+[[projects]]
+ name = "github.com/dghubble/sling"
+ packages = ["."]
+ revision = "eb56e89ac5088bebb12eef3cb4b293300f43608b"
+ version = "v1.1.0"
+
+[[projects]]
+ name = "github.com/dgrijalva/jwt-go"
+ packages = ["."]
+ revision = "d2709f9f1f31ebcda9651b03077758c1f3a0018c"
+ version = "v3.0.0"
+
+[[projects]]
+ name = "github.com/docker/distribution"
+ packages = [
+ ".",
+ "context",
+ "digest",
+ "manifest",
+ "manifest/schema1",
+ "manifest/schema2",
+ "reference",
+ "registry/auth",
+ "registry/auth/token",
+ "registry/client/auth/challenge",
+ "uuid"
+ ]
+ revision = "325b0804fef3a66309d962357aac3c2ce3f4d329"
+ version = "v2.6.0"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/docker/go"
+ packages = ["canonical/json"]
+ revision = "d30aec9fd63c35133f8f79c3412ad91a3b08be06"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/docker/libtrust"
+ packages = ["."]
+ revision = "aabc10ec26b754e797f9028f4589c5b7bd90dc20"
+
+[[projects]]
+ name = "github.com/docker/notary"
+ packages = [
+ ".",
+ "client",
+ "client/changelist",
+ "cryptoservice",
+ "storage",
+ "trustmanager",
+ "trustmanager/yubikey",
+ "trustpinning",
+ "tuf",
+ "tuf/data",
+ "tuf/signed",
+ "tuf/utils",
+ "tuf/validation"
+ ]
+ revision = "c04e3e6d05881045def11167c51d4a8baa34899a"
+
+[[projects]]
+ name = "github.com/garyburd/redigo"
+ packages = [
+ "internal",
+ "redis"
+ ]
+ revision = "47dc60e71eed504e3ef8e77ee3c6fe720f3be57f"
+ version = "v1.3.0"
+
+[[projects]]
+ name = "github.com/go-sql-driver/mysql"
+ packages = ["."]
+ revision = "a732e14c62dde3285440047bba97581bc472ae18"
+ version = "v1.2"
+
+[[projects]]
+ name = "github.com/golang/protobuf"
+ packages = ["proto"]
+ revision = "130e6b02ab059e7b717a096f397c5b60111cae74"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/google/go-querystring"
+ packages = ["query"]
+ revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a"
+
+[[projects]]
+ name = "github.com/gorilla/context"
+ packages = ["."]
+ revision = "aed02d124ae4a0e94fea4541c8effd05bf0c8296"
+
+[[projects]]
+ name = "github.com/gorilla/handlers"
+ packages = ["."]
+ revision = "13d73096a474cac93275c679c7b8a2dc17ddba82"
+
+[[projects]]
+ name = "github.com/gorilla/mux"
+ packages = ["."]
+ revision = "780415097119f6f61c55475fe59b66f3c3e9ea53"
+
+[[projects]]
+ name = "github.com/lib/pq"
+ packages = [
+ ".",
+ "oid"
+ ]
+ revision = "dd1fe2071026ce53f36a39112e645b4d4f5793a4"
+
+[[projects]]
+ name = "github.com/mattn/go-sqlite3"
+ packages = ["."]
+ revision = "3fb7a0e792edd47bf0cf1e919dfc14e2be412e15"
+
+[[projects]]
+ name = "github.com/miekg/pkcs11"
+ packages = ["."]
+ revision = "7283ca79f35edb89bc1b4ecae7f86a3680ce737f"
+
+[[projects]]
+ name = "github.com/opencontainers/go-digest"
+ packages = ["."]
+ revision = "aa2ec055abd10d26d539eb630a92241b781ce4bc"
+ version = "v1.0.0-rc0"
+
+[[projects]]
+ name = "github.com/pmezard/go-difflib"
+ packages = ["difflib"]
+ revision = "792786c7400a136282c1664665ae0a8db921c6c2"
+ version = "v1.0.0"
+
+[[projects]]
+ name = "github.com/stretchr/testify"
+ packages = [
+ "assert",
+ "require"
+ ]
+ revision = "4d4bfba8f1d1027c4fdbe371823030df51419987"
+
+[[projects]]
+ name = "golang.org/x/crypto"
+ packages = ["pbkdf2"]
+ revision = "5f961cd492ac9d43fc33a8ef646bae79d113fd97"
+
+[[projects]]
+ name = "golang.org/x/net"
+ packages = [
+ "context",
+ "context/ctxhttp"
+ ]
+ revision = "075e191f18186a8ff2becaf64478e30f4545cdad"
+
+[[projects]]
+ name = "golang.org/x/oauth2"
+ packages = [
+ ".",
+ "internal"
+ ]
+ revision = "bb50c06baba3d0c76f9d125c0719093e315b5b44"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/sys"
+ packages = ["unix"]
+ revision = "571f7bbbe08da2a8955aed9d4db316e78630e9a3"
+
+[[projects]]
+ name = "google.golang.org/appengine"
+ packages = [
+ "internal",
+ "internal/base",
+ "internal/datastore",
+ "internal/log",
+ "internal/remote_api",
+ "internal/urlfetch",
+ "urlfetch"
+ ]
+ revision = "24e4144ec923c2374f6b06610c0df16a9222c3d9"
+
+[[projects]]
+ name = "gopkg.in/asn1-ber.v1"
+ packages = ["."]
+ revision = "4e86f4367175e39f69d9358a5f17b4dda270378d"
+ version = "v1.1"
+
+[[projects]]
+ name = "gopkg.in/ldap.v2"
+ packages = ["."]
+ revision = "8168ee085ee43257585e50c6441aadf54ecb2c9f"
+ version = "v2.5.0"
+
+[solve-meta]
+ analyzer-name = "dep"
+ analyzer-version = 1
+ inputs-digest = "9c0f8cd26043afa12693fed0005998c7194c4ea77c8dae19c4363ebadfd600ef"
+ solver-name = "gps-cdcl"
+ solver-version = 1
diff --git a/src/Gopkg.toml b/src/Gopkg.toml
new file mode 100644
index 0000000000..836d90926c
--- /dev/null
+++ b/src/Gopkg.toml
@@ -0,0 +1,53 @@
+# Gopkg.toml example
+#
+# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
+# 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"
+
+
+[[constraint]]
+ name = "github.com/astaxie/beego"
+ version = "1.6.1"
+
+[[constraint]]
+ name = "github.com/dghubble/sling"
+ version = "1.1.0"
+
+[[constraint]]
+ name = "github.com/dgrijalva/jwt-go"
+ version = "3.0.0"
+
+[[constraint]]
+ name = "github.com/docker/distribution"
+ version = "2.6.0"
+
+[[constraint]]
+ branch = "master"
+ name = "github.com/docker/libtrust"
+
+[[constraint]]
+ name = "github.com/go-sql-driver/mysql"
+ version = "1.2.0"
+
+[[constraint]]
+ name = "github.com/opencontainers/go-digest"
+ version = "1.0.0-rc0"
+
+[[constraint]]
+ name = "gopkg.in/ldap.v2"
+ version = "2.5.0"
diff --git a/src/vendor/github.com/Sirupsen/logrus/.gitignore b/src/vendor/github.com/Sirupsen/logrus/.gitignore
new file mode 100644
index 0000000000..66be63a005
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/.gitignore
@@ -0,0 +1 @@
+logrus
diff --git a/src/vendor/github.com/Sirupsen/logrus/.travis.yml b/src/vendor/github.com/Sirupsen/logrus/.travis.yml
new file mode 100644
index 0000000000..dee4eb2cc7
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/.travis.yml
@@ -0,0 +1,10 @@
+language: go
+go:
+ - 1.3
+ - 1.4
+ - 1.5
+ - 1.6
+ - tip
+install:
+ - go get -t ./...
+script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./...
diff --git a/src/vendor/github.com/Sirupsen/logrus/alt_exit_test.go b/src/vendor/github.com/Sirupsen/logrus/alt_exit_test.go
new file mode 100644
index 0000000000..022b778303
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/alt_exit_test.go
@@ -0,0 +1,74 @@
+package logrus
+
+import (
+ "io/ioutil"
+ "os/exec"
+ "testing"
+ "time"
+)
+
+func TestRegister(t *testing.T) {
+ current := len(handlers)
+ RegisterExitHandler(func() {})
+ if len(handlers) != current+1 {
+ t.Fatalf("can't add handler")
+ }
+}
+
+func TestHandler(t *testing.T) {
+ gofile := "/tmp/testprog.go"
+ if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil {
+ t.Fatalf("can't create go file")
+ }
+
+ outfile := "/tmp/testprog.out"
+ arg := time.Now().UTC().String()
+ err := exec.Command("go", "run", gofile, outfile, arg).Run()
+ if err == nil {
+ t.Fatalf("completed normally, should have failed")
+ }
+
+ data, err := ioutil.ReadFile(outfile)
+ if err != nil {
+ t.Fatalf("can't read output file %s", outfile)
+ }
+
+ if string(data) != arg {
+ t.Fatalf("bad data")
+ }
+}
+
+var testprog = []byte(`
+// Test program for atexit, gets output file and data as arguments and writes
+// data to output file in atexit handler.
+package main
+
+import (
+ "github.com/Sirupsen/logrus"
+ "flag"
+ "fmt"
+ "io/ioutil"
+)
+
+var outfile = ""
+var data = ""
+
+func handler() {
+ ioutil.WriteFile(outfile, []byte(data), 0666)
+}
+
+func badHandler() {
+ n := 0
+ fmt.Println(1/n)
+}
+
+func main() {
+ flag.Parse()
+ outfile = flag.Arg(0)
+ data = flag.Arg(1)
+
+ logrus.RegisterExitHandler(handler)
+ logrus.RegisterExitHandler(badHandler)
+ logrus.Fatal("Bye bye")
+}
+`)
diff --git a/src/vendor/github.com/Sirupsen/logrus/entry_test.go b/src/vendor/github.com/Sirupsen/logrus/entry_test.go
new file mode 100644
index 0000000000..99c3b41d5f
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/entry_test.go
@@ -0,0 +1,77 @@
+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)
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go b/src/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
new file mode 100644
index 0000000000..a1623ec003
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
@@ -0,0 +1,50 @@
+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!")
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go b/src/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
new file mode 100644
index 0000000000..3187f6d3e1
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
@@ -0,0 +1,30 @@
+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!")
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go b/src/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go
new file mode 100644
index 0000000000..c6d290c77f
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go
@@ -0,0 +1,98 @@
+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)))
+ }
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/hook_test.go b/src/vendor/github.com/Sirupsen/logrus/hook_test.go
new file mode 100644
index 0000000000..13f34cb6f8
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/hook_test.go
@@ -0,0 +1,122 @@
+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)
+ })
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md
new file mode 100644
index 0000000000..066704b370
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md
@@ -0,0 +1,39 @@
+# Syslog Hooks for Logrus
+
+## 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)
+ }
+}
+```
\ No newline at end of file
diff --git a/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
new file mode 100644
index 0000000000..a36e20032e
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
@@ -0,0 +1,54 @@
+// +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
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go
new file mode 100644
index 0000000000..42762dc10d
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go
@@ -0,0 +1,26 @@
+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!")
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/hooks/test/test.go b/src/vendor/github.com/Sirupsen/logrus/hooks/test/test.go
new file mode 100644
index 0000000000..068812535d
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/hooks/test/test.go
@@ -0,0 +1,67 @@
+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)
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go b/src/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go
new file mode 100644
index 0000000000..d69455ba04
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go
@@ -0,0 +1,39 @@
+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))
+
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/json_formatter_test.go b/src/vendor/github.com/Sirupsen/logrus/json_formatter_test.go
new file mode 100644
index 0000000000..1d70873254
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/json_formatter_test.go
@@ -0,0 +1,120 @@
+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")
+ }
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/logrus_test.go b/src/vendor/github.com/Sirupsen/logrus/logrus_test.go
new file mode 100644
index 0000000000..bfc478055e
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/logrus_test.go
@@ -0,0 +1,361 @@
+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)
+}
diff --git a/src/vendor/github.com/Sirupsen/logrus/text_formatter_test.go b/src/vendor/github.com/Sirupsen/logrus/text_formatter_test.go
new file mode 100644
index 0000000000..e25a44f67b
--- /dev/null
+++ b/src/vendor/github.com/Sirupsen/logrus/text_formatter_test.go
@@ -0,0 +1,61 @@
+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.
diff --git a/src/vendor/github.com/Unknwon/goconfig/.gitignore b/src/vendor/github.com/Unknwon/goconfig/.gitignore
new file mode 100644
index 0000000000..c81d5b3747
--- /dev/null
+++ b/src/vendor/github.com/Unknwon/goconfig/.gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+*.iml
+.idea
\ No newline at end of file
diff --git a/src/vendor/github.com/Unknwon/goconfig/goconfig_test.go b/src/vendor/github.com/Unknwon/goconfig/goconfig_test.go
new file mode 100644
index 0000000000..2dc3592679
--- /dev/null
+++ b/src/vendor/github.com/Unknwon/goconfig/goconfig_test.go
@@ -0,0 +1,398 @@
+// Copyright 2013 Unknwon
+//
+// 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 goconfig
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "testing"
+
+ . "github.com/smartystreets/goconvey/convey"
+)
+
+func TestLoadConfigFile(t *testing.T) {
+ Convey("Load a single configuration file that does exist", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ Convey("Test GetSectionList", func() {
+ So(c.GetSectionList(), ShouldResemble,
+ []string{"DEFAULT", "Demo", "What's this?", "url", "parent",
+ "parent.child", "parent.child.child", "auto increment"})
+ })
+
+ Convey("Get value that does exist", func() {
+ v, err := c.GetValue("Demo", "key2")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, "test data")
+ })
+
+ Convey("Get value that does not exist", func() {
+ _, err := c.GetValue("Demo", "key4")
+ So(err, ShouldNotBeNil)
+ })
+
+ Convey("Get value that has empty value", func() {
+ _, err := c.GetValue("What's this?", "empty_value")
+ So(err, ShouldBeNil)
+ })
+
+ Convey("Get value that section does not exist", func() {
+ _, err := c.GetValue("Demo404", "key4")
+ So(err, ShouldNotBeNil)
+ })
+
+ Convey("Get value use parent-child feature", func() {
+ v, err := c.GetValue("parent.child", "sex")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, "male")
+ })
+
+ Convey("Get value use recursive feature", func() {
+ v, err := c.GetValue("", "search")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, "http://www.google.com")
+
+ v, err = c.GetValue("url", "google_url")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, "http://www.google.fake")
+ })
+
+ Convey("Set value that does exist", func() {
+ So(c.SetValue("Demo", "key2", "hello man!"), ShouldBeFalse)
+ v, err := c.GetValue("Demo", "key2")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, "hello man!")
+ })
+
+ Convey("Set value that does not exist", func() {
+ So(c.SetValue("Demo", "key4", "hello girl!"), ShouldBeTrue)
+ v, err := c.GetValue("Demo", "key4")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, "hello girl!")
+ So(c.SetValue("", "gowalker", "https://gowalker.org"), ShouldBeTrue)
+ })
+
+ Convey("Test GetKeyList", func() {
+ So(c.GetKeyList("Demo"), ShouldResemble,
+ []string{"key1", "key2", "key3", "quote", "key:1",
+ "key:2=key:1", "中国", "chinese-var", "array_key"})
+ })
+
+ Convey("Delete a key", func() {
+ So(c.DeleteKey("", "key404"), ShouldBeFalse)
+ So(c.DeleteKey("Demo", "key404"), ShouldBeFalse)
+ So(c.DeleteKey("Demo", "中国"), ShouldBeTrue)
+ _, err := c.GetValue("Demo", "中国")
+ So(err, ShouldNotBeNil)
+ So(c.DeleteKey("404", "key"), ShouldBeFalse)
+ })
+
+ Convey("Delete all the keys", func() {
+ for _, key := range c.GetKeyList("Demo") {
+ So(c.DeleteKey("Demo", key), ShouldBeTrue)
+ }
+ So(c.GetKeyList("Demo"), ShouldResemble, []string{})
+ So(len(c.GetKeyList("Demo")), ShouldEqual, 0)
+ })
+
+ Convey("Delete section that does not exist", func() {
+ So(c.DeleteSection(""), ShouldBeTrue)
+ So(c.DeleteSection("404"), ShouldBeFalse)
+ })
+
+ Convey("Get section that exists", func() {
+ _, err = c.GetSection("")
+ So(err, ShouldBeNil)
+ })
+
+ Convey("Get section that does not exist", func() {
+ _, err = c.GetSection("404")
+ So(err, ShouldNotBeNil)
+ })
+
+ Convey("Set section comments", func() {
+ So(c.SetSectionComments("", "default section comments"), ShouldBeTrue)
+ })
+
+ Convey("Get section comments", func() {
+ So(c.GetSectionComments(""), ShouldEqual, "")
+ })
+
+ Convey("Set key comments", func() {
+ So(c.SetKeyComments("", "search", "search comments"), ShouldBeTrue)
+ So(c.SetKeyComments("404", "search", ""), ShouldBeTrue)
+ })
+
+ Convey("Get key comments", func() {
+ So(c.GetKeyComments("", "google"), ShouldEqual, "; Google")
+ })
+
+ Convey("Delete all the sections", func() {
+ for _, sec := range c.GetSectionList() {
+ So(c.DeleteSection(sec), ShouldBeTrue)
+ }
+ So(c.GetSectionList(), ShouldResemble, []string{})
+ So(len(c.GetSectionList()), ShouldEqual, 0)
+ })
+ })
+
+ Convey("Load a single configuration file that does not exist", t, func() {
+ _, err := LoadConfigFile("testdata/conf404.ini")
+ So(err, ShouldNotBeNil)
+ })
+
+ Convey("Load multiple configuration files", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini", "testdata/conf2.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ Convey("Get value that does not exist in 1st file", func() {
+ v, err := c.GetValue("new section", "key1")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, "conf.ini does not have this key")
+ })
+
+ Convey("Get value that overwrited in 2nd file", func() {
+ v, err := c.GetValue("Demo", "key2")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, "rewrite this key of conf.ini")
+ })
+ })
+}
+
+func TestGetKeyList(t *testing.T) {
+ Convey("Get key list", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ Convey("Get ket list that does exist", func() {
+ So(c.GetKeyList("Demo"), ShouldResemble,
+ []string{"key1", "key2", "key3", "quote", "key:1",
+ "key:2=key:1", "中国", "chinese-var", "array_key"})
+ So(c.GetKeyList(""), ShouldResemble, []string{"google", "search"})
+ })
+
+ Convey("Get key list that not exist", func() {
+ So(c.GetKeyList("404"), ShouldBeNil)
+ })
+ })
+}
+
+func TestSaveConfigFile(t *testing.T) {
+ Convey("Save a ConfigFile to file system", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini", "testdata/conf2.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ c.SetValue("", "", "empty")
+
+ So(SaveConfigFile(c, "testdata/conf_test.ini"), ShouldBeNil)
+ })
+}
+
+func TestSaveConfigData(t *testing.T) {
+ Convey("Save a ConfigFile to file system", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini", "testdata/conf2.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ c.SetValue("", "", "empty")
+ var dst bytes.Buffer
+ So(SaveConfigData(c, &dst), ShouldBeNil)
+ So(dst.Len(), ShouldNotEqual, 0)
+ })
+}
+
+func TestReload(t *testing.T) {
+ Convey("Reload a configuration file", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini", "testdata/conf2.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ So(c.Reload(), ShouldBeNil)
+ })
+}
+
+func TestReloadData(t *testing.T) {
+ Convey("Reload a configuration file", t, func() {
+ data, err := ioutil.ReadFile("testdata/conf.ini")
+ So(err, ShouldBeNil)
+ c, err := LoadFromReader(bytes.NewBuffer(data))
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ So(c.ReloadData(bytes.NewBuffer(data)), ShouldBeNil)
+ })
+}
+
+func TestAppendFiles(t *testing.T) {
+ Convey("Reload a configuration file", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ So(c.AppendFiles("testdata/conf2.ini"), ShouldBeNil)
+ })
+}
+
+func TestTypes(t *testing.T) {
+ Convey("Return with types", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ Convey("Return bool", func() {
+ v, err := c.Bool("parent.child", "married")
+ So(err, ShouldBeNil)
+ So(v, ShouldBeTrue)
+
+ _, err = c.Bool("parent.child", "died")
+ So(err, ShouldNotBeNil)
+ })
+
+ Convey("Return float64", func() {
+ v, err := c.Float64("parent", "money")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, 1.25)
+
+ _, err = c.Float64("parent", "balance")
+ So(err, ShouldNotBeNil)
+ })
+
+ Convey("Return int", func() {
+ v, err := c.Int("parent", "age")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, 32)
+
+ _, err = c.Int("parent", "children")
+ So(err, ShouldNotBeNil)
+ })
+
+ Convey("Return int64", func() {
+ v, err := c.Int64("parent", "age")
+ So(err, ShouldBeNil)
+ So(v, ShouldEqual, 32)
+
+ _, err = c.Int64("parent", "children")
+ So(err, ShouldNotBeNil)
+ })
+ })
+}
+
+func TestMust(t *testing.T) {
+ Convey("Must return with type", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ Convey("Return string", func() {
+ So(c.MustValue("parent.child", "name"), ShouldEqual, "john")
+ So(c.MustValue("parent.child", "died"), ShouldEqual, "")
+ So(c.MustValue("parent.child", "died", "no"), ShouldEqual, "no")
+ })
+
+ Convey("Return string and bool", func() {
+ val, ok := c.MustValueSet("parent.child", "died")
+ So(val, ShouldEqual, "")
+ So(ok, ShouldBeFalse)
+ val, ok = c.MustValueSet("parent.child", "died", "no")
+ So(val, ShouldEqual, "no")
+ So(ok, ShouldBeTrue)
+ })
+
+ Convey("Return bool", func() {
+ So(c.MustBool("parent.child", "married"), ShouldBeTrue)
+ So(c.MustBool("parent.child", "died"), ShouldBeFalse)
+ So(c.MustBool("parent.child", "died", true), ShouldBeTrue)
+ })
+
+ Convey("Return float64", func() {
+ So(c.MustFloat64("parent", "money"), ShouldEqual, 1.25)
+ So(c.MustFloat64("parent", "balance"), ShouldEqual, 0.0)
+ So(c.MustFloat64("parent", "balance", 1.25), ShouldEqual, 1.25)
+ })
+
+ Convey("Return int", func() {
+ So(c.MustInt("parent", "age"), ShouldEqual, 32)
+ So(c.MustInt("parent", "children"), ShouldEqual, 0)
+ So(c.MustInt("parent", "children", 3), ShouldEqual, 3)
+ })
+
+ Convey("Return int64", func() {
+ So(c.MustInt64("parent", "age"), ShouldEqual, 32)
+ So(c.MustInt64("parent", "children"), ShouldEqual, 0)
+ So(c.MustInt64("parent", "children", 3), ShouldEqual, 3)
+ })
+ })
+}
+
+func TestRange(t *testing.T) {
+ Convey("Must return with range", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ So(c.MustValueRange("What's this?", "name", "joe", []string{"hello"}), ShouldEqual, "joe")
+ So(c.MustValueRange("What's this?", "name404", "joe", []string{"hello"}), ShouldEqual, "joe")
+ So(c.MustValueRange("What's this?", "name", "joe", []string{"hello", "try one more value ^-^"}),
+ ShouldEqual, "try one more value ^-^")
+ })
+}
+
+func TestArray(t *testing.T) {
+ Convey("Must return with string array", t, func() {
+ c, err := LoadConfigFile("testdata/conf.ini")
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+
+ So(fmt.Sprintf("%s", c.MustValueArray("Demo", "array_key", ",")), ShouldEqual, "[1 2 3 4 5]")
+ So(fmt.Sprintf("%s", c.MustValueArray("Demo", "array_key404", ",")), ShouldEqual, "[]")
+ })
+}
+
+func TestLoadFromData(t *testing.T) {
+ Convey("Load config file from data", t, func() {
+ c, err := LoadFromData([]byte(""))
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+ })
+}
+
+func TestLoadFromReader(t *testing.T) {
+ Convey("Load config file from reader", t, func() {
+ c, err := LoadFromReader(bytes.NewBuffer([]byte("")))
+ So(err, ShouldBeNil)
+ So(c, ShouldNotBeNil)
+ })
+}
+
+func Benchmark_GetValue(b *testing.B) {
+ c, _ := LoadConfigFile("testdata/conf.ini")
+ c.BlockMode = false
+ for i := 0; i < b.N; i++ {
+ c.GetValue("parent", "money")
+ }
+}
+
+func Benchmark_SetValue(b *testing.B) {
+ c, _ := LoadConfigFile("testdata/conf.ini")
+ for i := 0; i < b.N; i++ {
+ c.SetValue("parent", "money", "10")
+ }
+}
diff --git a/src/vendor/github.com/Unknwon/goconfig/testdata/conf.ini b/src/vendor/github.com/Unknwon/goconfig/testdata/conf.ini
new file mode 100644
index 0000000000..65ab1da7c3
--- /dev/null
+++ b/src/vendor/github.com/Unknwon/goconfig/testdata/conf.ini
@@ -0,0 +1,46 @@
+; Google
+google = www.google.com
+search = http://%(google)s
+
+; Here are Comments
+; Second line
+[Demo]
+# This symbol can also make this line to be comments
+key1 = Let's us goconfig!!!
+key2 = test data
+key3 = this is based on key2:%(key2)s
+quote = "special case for quote
+"key:1" = This is the value of "key:1"
+"key:2=key:1" = this is based on "key:2=key:1" => %(key:1)s
+中国 = China
+chinese-var = hello %(中国)s!
+array_key = 1,2,3,4,5
+
+[What's this?]
+; Not Enough Comments!!
+name = try one more value ^-^
+empty_value =
+
+[url]
+google_fake = www.google.fake
+google_url = http://%(google_fake)s
+
+[parent]
+name = john
+relation = father
+sex = male
+age = 32
+money = 1.25
+
+[parent.child]
+age = 3
+married = true
+
+[parent.child.child]
+
+; Auto increment by setting key to "-"
+[auto increment]
+- = hello
+- = go
+- = config
+
diff --git a/src/vendor/github.com/Unknwon/goconfig/testdata/conf2.ini b/src/vendor/github.com/Unknwon/goconfig/testdata/conf2.ini
new file mode 100644
index 0000000000..d5635bb6b8
--- /dev/null
+++ b/src/vendor/github.com/Unknwon/goconfig/testdata/conf2.ini
@@ -0,0 +1,37 @@
+; Google
+google = www.google.com
+search = http://%(google)s
+
+; Here are Comments
+; Second line
+[Demo]
+# This symbol can also make this line to be comments
+key1 = Let's us goconfig!!!
+key2 = rewrite this key of conf.ini
+key3 = this is based on key2:%(key2)s
+"key:1" = This is the value of "key:1"
+"""key:2"""="""this is based on "key:1" => `%(key:1)s`"""
+
+[What's this?]
+; Not Enough Comments!!
+name = try one more value ^-^
+
+[parent]
+name = john
+relation = father
+sex = male
+age = 32
+
+[parent.child]
+age = 3
+
+[parent.child.child]
+
+; Auto increment by setting key to "-"
+[auto increment]
+- = hello
+- = go
+- = config
+
+[new section]
+key1 = conf.ini does not have this key
\ No newline at end of file
diff --git a/src/vendor/github.com/Unknwon/goconfig/testdata/conf_test.ini b/src/vendor/github.com/Unknwon/goconfig/testdata/conf_test.ini
new file mode 100644
index 0000000000..769d656196
--- /dev/null
+++ b/src/vendor/github.com/Unknwon/goconfig/testdata/conf_test.ini
@@ -0,0 +1,50 @@
+; Google
+google = www.google.com
+search = http://%(google)s
+
+; Here are Comments
+; Second line
+[Demo]
+# This symbol can also make this line to be comments
+key1 = Let's us goconfig!!!
+key2 = rewrite this key of conf.ini
+key3 = this is based on key2:%(key2)s
+quote = "special case for quote
+`key:1` = This is the value of "key:1"
+`key:2=key:1` = this is based on "key:2=key:1" => %(key:1)s
+中国 = China
+chinese-var = hello %(中国)s!
+array_key = 1,2,3,4,5
+`key:2` = """this is based on "key:1" => `%(key:1)s`"""
+
+[What's this?]
+; Not Enough Comments!!
+name = try one more value ^-^
+empty_value =
+
+[url]
+google_fake = www.google.fake
+google_url = http://%(google_fake)s
+
+[parent]
+name = john
+relation = father
+sex = male
+age = 32
+money = 1.25
+
+[parent.child]
+age = 3
+married = true
+
+[parent.child.child]
+
+; Auto increment by setting key to "-"
+[auto increment]
+- = hello
+- = go
+- = config
+
+[new section]
+key1 = conf.ini does not have this key
+
diff --git a/src/vendor/github.com/agl/ed25519/ed25519_test.go b/src/vendor/github.com/agl/ed25519/ed25519_test.go
new file mode 100644
index 0000000000..0b4651eb6b
--- /dev/null
+++ b/src/vendor/github.com/agl/ed25519/ed25519_test.go
@@ -0,0 +1,160 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ed25519
+
+import (
+ "bufio"
+ "bytes"
+ "compress/gzip"
+ "crypto/rand"
+ "encoding/hex"
+ "io"
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/agl/ed25519/edwards25519"
+)
+
+type zeroReader struct{}
+
+func (zeroReader) Read(buf []byte) (int, error) {
+ for i := range buf {
+ buf[i] = 0
+ }
+ return len(buf), nil
+}
+
+func TestUnmarshalMarshal(t *testing.T) {
+ pub, _, _ := GenerateKey(rand.Reader)
+
+ var A edwards25519.ExtendedGroupElement
+ if !A.FromBytes(pub) {
+ t.Fatalf("ExtendedGroupElement.FromBytes failed")
+ }
+
+ var pub2 [32]byte
+ A.ToBytes(&pub2)
+
+ if *pub != pub2 {
+ t.Errorf("FromBytes(%v)->ToBytes does not round-trip, got %x\n", *pub, pub2)
+ }
+}
+
+func TestSignVerify(t *testing.T) {
+ var zero zeroReader
+ public, private, _ := GenerateKey(zero)
+
+ message := []byte("test message")
+ sig := Sign(private, message)
+ if !Verify(public, message, sig) {
+ t.Errorf("valid signature rejected")
+ }
+
+ wrongMessage := []byte("wrong message")
+ if Verify(public, wrongMessage, sig) {
+ t.Errorf("signature of different message accepted")
+ }
+}
+
+func TestGolden(t *testing.T) {
+ // sign.input.gz is a selection of test cases from
+ // http://ed25519.cr.yp.to/python/sign.input
+ testDataZ, err := os.Open("testdata/sign.input.gz")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer testDataZ.Close()
+ testData, err := gzip.NewReader(testDataZ)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer testData.Close()
+
+ in := bufio.NewReaderSize(testData, 1<<12)
+ lineNo := 0
+ for {
+ lineNo++
+ lineBytes, isPrefix, err := in.ReadLine()
+ if isPrefix {
+ t.Fatal("bufio buffer too small")
+ }
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ t.Fatalf("error reading test data: %s", err)
+ }
+
+ line := string(lineBytes)
+ parts := strings.Split(line, ":")
+ if len(parts) != 5 {
+ t.Fatalf("bad number of parts on line %d", lineNo)
+ }
+
+ privBytes, _ := hex.DecodeString(parts[0])
+ pubKeyBytes, _ := hex.DecodeString(parts[1])
+ msg, _ := hex.DecodeString(parts[2])
+ sig, _ := hex.DecodeString(parts[3])
+ // The signatures in the test vectors also include the message
+ // at the end, but we just want R and S.
+ sig = sig[:SignatureSize]
+
+ if l := len(pubKeyBytes); l != PublicKeySize {
+ t.Fatalf("bad public key length on line %d: got %d bytes", lineNo, l)
+ }
+
+ var priv [PrivateKeySize]byte
+ copy(priv[:], privBytes)
+ copy(priv[32:], pubKeyBytes)
+
+ sig2 := Sign(&priv, msg)
+ if !bytes.Equal(sig, sig2[:]) {
+ t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2)
+ }
+
+ var pubKey [PublicKeySize]byte
+ copy(pubKey[:], pubKeyBytes)
+ if !Verify(&pubKey, msg, sig2) {
+ t.Errorf("signature failed to verify on line %d", lineNo)
+ }
+ }
+}
+
+func BenchmarkKeyGeneration(b *testing.B) {
+ var zero zeroReader
+ for i := 0; i < b.N; i++ {
+ if _, _, err := GenerateKey(zero); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkSigning(b *testing.B) {
+ var zero zeroReader
+ _, priv, err := GenerateKey(zero)
+ if err != nil {
+ b.Fatal(err)
+ }
+ message := []byte("Hello, world!")
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Sign(priv, message)
+ }
+}
+
+func BenchmarkVerification(b *testing.B) {
+ var zero zeroReader
+ pub, priv, err := GenerateKey(zero)
+ if err != nil {
+ b.Fatal(err)
+ }
+ message := []byte("Hello, world!")
+ signature := Sign(priv, message)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Verify(pub, message, signature)
+ }
+}
diff --git a/src/vendor/github.com/agl/ed25519/extra25519/extra25519.go b/src/vendor/github.com/agl/ed25519/extra25519/extra25519.go
new file mode 100644
index 0000000000..b897ba5381
--- /dev/null
+++ b/src/vendor/github.com/agl/ed25519/extra25519/extra25519.go
@@ -0,0 +1,340 @@
+// Copyright 2013 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 extra25519
+
+import (
+ "crypto/sha512"
+
+ "github.com/agl/ed25519/edwards25519"
+)
+
+// PrivateKeyToCurve25519 converts an ed25519 private key into a corresponding
+// curve25519 private key such that the resulting curve25519 public key will
+// equal the result from PublicKeyToCurve25519.
+func PrivateKeyToCurve25519(curve25519Private *[32]byte, privateKey *[64]byte) {
+ h := sha512.New()
+ h.Write(privateKey[:32])
+ digest := h.Sum(nil)
+
+ digest[0] &= 248
+ digest[31] &= 127
+ digest[31] |= 64
+
+ copy(curve25519Private[:], digest)
+}
+
+func edwardsToMontgomeryX(outX, y *edwards25519.FieldElement) {
+ // We only need the x-coordinate of the curve25519 point, which I'll
+ // call u. The isomorphism is u=(y+1)/(1-y), since y=Y/Z, this gives
+ // u=(Y+Z)/(Z-Y). We know that Z=1, thus u=(Y+1)/(1-Y).
+ var oneMinusY edwards25519.FieldElement
+ edwards25519.FeOne(&oneMinusY)
+ edwards25519.FeSub(&oneMinusY, &oneMinusY, y)
+ edwards25519.FeInvert(&oneMinusY, &oneMinusY)
+
+ edwards25519.FeOne(outX)
+ edwards25519.FeAdd(outX, outX, y)
+
+ edwards25519.FeMul(outX, outX, &oneMinusY)
+}
+
+// PublicKeyToCurve25519 converts an Ed25519 public key into the curve25519
+// public key that would be generated from the same private key.
+func PublicKeyToCurve25519(curve25519Public *[32]byte, publicKey *[32]byte) bool {
+ var A edwards25519.ExtendedGroupElement
+ if !A.FromBytes(publicKey) {
+ return false
+ }
+
+ // A.Z = 1 as a postcondition of FromBytes.
+ var x edwards25519.FieldElement
+ edwardsToMontgomeryX(&x, &A.Y)
+ edwards25519.FeToBytes(curve25519Public, &x)
+ return true
+}
+
+// sqrtMinusAPlus2 is sqrt(-(486662+2))
+var sqrtMinusAPlus2 = edwards25519.FieldElement{
+ -12222970, -8312128, -11511410, 9067497, -15300785, -241793, 25456130, 14121551, -12187136, 3972024,
+}
+
+// sqrtMinusHalf is sqrt(-1/2)
+var sqrtMinusHalf = edwards25519.FieldElement{
+ -17256545, 3971863, 28865457, -1750208, 27359696, -16640980, 12573105, 1002827, -163343, 11073975,
+}
+
+// halfQMinus1Bytes is (2^255-20)/2 expressed in little endian form.
+var halfQMinus1Bytes = [32]byte{
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f,
+}
+
+// feBytesLess returns one if a <= b and zero otherwise.
+func feBytesLE(a, b *[32]byte) int32 {
+ equalSoFar := int32(-1)
+ greater := int32(0)
+
+ for i := uint(31); i < 32; i-- {
+ x := int32(a[i])
+ y := int32(b[i])
+
+ greater = (^equalSoFar & greater) | (equalSoFar & ((x - y) >> 31))
+ equalSoFar = equalSoFar & (((x ^ y) - 1) >> 31)
+ }
+
+ return int32(^equalSoFar & 1 & greater)
+}
+
+// ScalarBaseMult computes a curve25519 public key from a private key and also
+// a uniform representative for that public key. Note that this function will
+// fail and return false for about half of private keys.
+// See http://elligator.cr.yp.to/elligator-20130828.pdf.
+func ScalarBaseMult(publicKey, representative, privateKey *[32]byte) bool {
+ var maskedPrivateKey [32]byte
+ copy(maskedPrivateKey[:], privateKey[:])
+
+ maskedPrivateKey[0] &= 248
+ maskedPrivateKey[31] &= 127
+ maskedPrivateKey[31] |= 64
+
+ var A edwards25519.ExtendedGroupElement
+ edwards25519.GeScalarMultBase(&A, &maskedPrivateKey)
+
+ var inv1 edwards25519.FieldElement
+ edwards25519.FeSub(&inv1, &A.Z, &A.Y)
+ edwards25519.FeMul(&inv1, &inv1, &A.X)
+ edwards25519.FeInvert(&inv1, &inv1)
+
+ var t0, u edwards25519.FieldElement
+ edwards25519.FeMul(&u, &inv1, &A.X)
+ edwards25519.FeAdd(&t0, &A.Y, &A.Z)
+ edwards25519.FeMul(&u, &u, &t0)
+
+ var v edwards25519.FieldElement
+ edwards25519.FeMul(&v, &t0, &inv1)
+ edwards25519.FeMul(&v, &v, &A.Z)
+ edwards25519.FeMul(&v, &v, &sqrtMinusAPlus2)
+
+ var b edwards25519.FieldElement
+ edwards25519.FeAdd(&b, &u, &edwards25519.A)
+
+ var c, b3, b7, b8 edwards25519.FieldElement
+ edwards25519.FeSquare(&b3, &b) // 2
+ edwards25519.FeMul(&b3, &b3, &b) // 3
+ edwards25519.FeSquare(&c, &b3) // 6
+ edwards25519.FeMul(&b7, &c, &b) // 7
+ edwards25519.FeMul(&b8, &b7, &b) // 8
+ edwards25519.FeMul(&c, &b7, &u)
+ q58(&c, &c)
+
+ var chi edwards25519.FieldElement
+ edwards25519.FeSquare(&chi, &c)
+ edwards25519.FeSquare(&chi, &chi)
+
+ edwards25519.FeSquare(&t0, &u)
+ edwards25519.FeMul(&chi, &chi, &t0)
+
+ edwards25519.FeSquare(&t0, &b7) // 14
+ edwards25519.FeMul(&chi, &chi, &t0)
+ edwards25519.FeNeg(&chi, &chi)
+
+ var chiBytes [32]byte
+ edwards25519.FeToBytes(&chiBytes, &chi)
+ // chi[1] is either 0 or 0xff
+ if chiBytes[1] == 0xff {
+ return false
+ }
+
+ // Calculate r1 = sqrt(-u/(2*(u+A)))
+ var r1 edwards25519.FieldElement
+ edwards25519.FeMul(&r1, &c, &u)
+ edwards25519.FeMul(&r1, &r1, &b3)
+ edwards25519.FeMul(&r1, &r1, &sqrtMinusHalf)
+
+ var maybeSqrtM1 edwards25519.FieldElement
+ edwards25519.FeSquare(&t0, &r1)
+ edwards25519.FeMul(&t0, &t0, &b)
+ edwards25519.FeAdd(&t0, &t0, &t0)
+ edwards25519.FeAdd(&t0, &t0, &u)
+
+ edwards25519.FeOne(&maybeSqrtM1)
+ edwards25519.FeCMove(&maybeSqrtM1, &edwards25519.SqrtM1, edwards25519.FeIsNonZero(&t0))
+ edwards25519.FeMul(&r1, &r1, &maybeSqrtM1)
+
+ // Calculate r = sqrt(-(u+A)/(2u))
+ var r edwards25519.FieldElement
+ edwards25519.FeSquare(&t0, &c) // 2
+ edwards25519.FeMul(&t0, &t0, &c) // 3
+ edwards25519.FeSquare(&t0, &t0) // 6
+ edwards25519.FeMul(&r, &t0, &c) // 7
+
+ edwards25519.FeSquare(&t0, &u) // 2
+ edwards25519.FeMul(&t0, &t0, &u) // 3
+ edwards25519.FeMul(&r, &r, &t0)
+
+ edwards25519.FeSquare(&t0, &b8) // 16
+ edwards25519.FeMul(&t0, &t0, &b8) // 24
+ edwards25519.FeMul(&t0, &t0, &b) // 25
+ edwards25519.FeMul(&r, &r, &t0)
+ edwards25519.FeMul(&r, &r, &sqrtMinusHalf)
+
+ edwards25519.FeSquare(&t0, &r)
+ edwards25519.FeMul(&t0, &t0, &u)
+ edwards25519.FeAdd(&t0, &t0, &t0)
+ edwards25519.FeAdd(&t0, &t0, &b)
+ edwards25519.FeOne(&maybeSqrtM1)
+ edwards25519.FeCMove(&maybeSqrtM1, &edwards25519.SqrtM1, edwards25519.FeIsNonZero(&t0))
+ edwards25519.FeMul(&r, &r, &maybeSqrtM1)
+
+ var vBytes [32]byte
+ edwards25519.FeToBytes(&vBytes, &v)
+ vInSquareRootImage := feBytesLE(&vBytes, &halfQMinus1Bytes)
+ edwards25519.FeCMove(&r, &r1, vInSquareRootImage)
+
+ edwards25519.FeToBytes(publicKey, &u)
+ edwards25519.FeToBytes(representative, &r)
+ return true
+}
+
+// q58 calculates out = z^((p-5)/8).
+func q58(out, z *edwards25519.FieldElement) {
+ var t1, t2, t3 edwards25519.FieldElement
+ var i int
+
+ edwards25519.FeSquare(&t1, z) // 2^1
+ edwards25519.FeMul(&t1, &t1, z) // 2^1 + 2^0
+ edwards25519.FeSquare(&t1, &t1) // 2^2 + 2^1
+ edwards25519.FeSquare(&t2, &t1) // 2^3 + 2^2
+ edwards25519.FeSquare(&t2, &t2) // 2^4 + 2^3
+ edwards25519.FeMul(&t2, &t2, &t1) // 4,3,2,1
+ edwards25519.FeMul(&t1, &t2, z) // 4..0
+ edwards25519.FeSquare(&t2, &t1) // 5..1
+ for i = 1; i < 5; i++ { // 9,8,7,6,5
+ edwards25519.FeSquare(&t2, &t2)
+ }
+ edwards25519.FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0
+ edwards25519.FeSquare(&t2, &t1) // 10..1
+ for i = 1; i < 10; i++ { // 19..10
+ edwards25519.FeSquare(&t2, &t2)
+ }
+ edwards25519.FeMul(&t2, &t2, &t1) // 19..0
+ edwards25519.FeSquare(&t3, &t2) // 20..1
+ for i = 1; i < 20; i++ { // 39..20
+ edwards25519.FeSquare(&t3, &t3)
+ }
+ edwards25519.FeMul(&t2, &t3, &t2) // 39..0
+ edwards25519.FeSquare(&t2, &t2) // 40..1
+ for i = 1; i < 10; i++ { // 49..10
+ edwards25519.FeSquare(&t2, &t2)
+ }
+ edwards25519.FeMul(&t1, &t2, &t1) // 49..0
+ edwards25519.FeSquare(&t2, &t1) // 50..1
+ for i = 1; i < 50; i++ { // 99..50
+ edwards25519.FeSquare(&t2, &t2)
+ }
+ edwards25519.FeMul(&t2, &t2, &t1) // 99..0
+ edwards25519.FeSquare(&t3, &t2) // 100..1
+ for i = 1; i < 100; i++ { // 199..100
+ edwards25519.FeSquare(&t3, &t3)
+ }
+ edwards25519.FeMul(&t2, &t3, &t2) // 199..0
+ edwards25519.FeSquare(&t2, &t2) // 200..1
+ for i = 1; i < 50; i++ { // 249..50
+ edwards25519.FeSquare(&t2, &t2)
+ }
+ edwards25519.FeMul(&t1, &t2, &t1) // 249..0
+ edwards25519.FeSquare(&t1, &t1) // 250..1
+ edwards25519.FeSquare(&t1, &t1) // 251..2
+ edwards25519.FeMul(out, &t1, z) // 251..2,0
+}
+
+// chi calculates out = z^((p-1)/2). The result is either 1, 0, or -1 depending
+// on whether z is a non-zero square, zero, or a non-square.
+func chi(out, z *edwards25519.FieldElement) {
+ var t0, t1, t2, t3 edwards25519.FieldElement
+ var i int
+
+ edwards25519.FeSquare(&t0, z) // 2^1
+ edwards25519.FeMul(&t1, &t0, z) // 2^1 + 2^0
+ edwards25519.FeSquare(&t0, &t1) // 2^2 + 2^1
+ edwards25519.FeSquare(&t2, &t0) // 2^3 + 2^2
+ edwards25519.FeSquare(&t2, &t2) // 4,3
+ edwards25519.FeMul(&t2, &t2, &t0) // 4,3,2,1
+ edwards25519.FeMul(&t1, &t2, z) // 4..0
+ edwards25519.FeSquare(&t2, &t1) // 5..1
+ for i = 1; i < 5; i++ { // 9,8,7,6,5
+ edwards25519.FeSquare(&t2, &t2)
+ }
+ edwards25519.FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0
+ edwards25519.FeSquare(&t2, &t1) // 10..1
+ for i = 1; i < 10; i++ { // 19..10
+ edwards25519.FeSquare(&t2, &t2)
+ }
+ edwards25519.FeMul(&t2, &t2, &t1) // 19..0
+ edwards25519.FeSquare(&t3, &t2) // 20..1
+ for i = 1; i < 20; i++ { // 39..20
+ edwards25519.FeSquare(&t3, &t3)
+ }
+ edwards25519.FeMul(&t2, &t3, &t2) // 39..0
+ edwards25519.FeSquare(&t2, &t2) // 40..1
+ for i = 1; i < 10; i++ { // 49..10
+ edwards25519.FeSquare(&t2, &t2)
+ }
+ edwards25519.FeMul(&t1, &t2, &t1) // 49..0
+ edwards25519.FeSquare(&t2, &t1) // 50..1
+ for i = 1; i < 50; i++ { // 99..50
+ edwards25519.FeSquare(&t2, &t2)
+ }
+ edwards25519.FeMul(&t2, &t2, &t1) // 99..0
+ edwards25519.FeSquare(&t3, &t2) // 100..1
+ for i = 1; i < 100; i++ { // 199..100
+ edwards25519.FeSquare(&t3, &t3)
+ }
+ edwards25519.FeMul(&t2, &t3, &t2) // 199..0
+ edwards25519.FeSquare(&t2, &t2) // 200..1
+ for i = 1; i < 50; i++ { // 249..50
+ edwards25519.FeSquare(&t2, &t2)
+ }
+ edwards25519.FeMul(&t1, &t2, &t1) // 249..0
+ edwards25519.FeSquare(&t1, &t1) // 250..1
+ for i = 1; i < 4; i++ { // 253..4
+ edwards25519.FeSquare(&t1, &t1)
+ }
+ edwards25519.FeMul(out, &t1, &t0) // 253..4,2,1
+}
+
+// RepresentativeToPublicKey converts a uniform representative value for a
+// curve25519 public key, as produced by ScalarBaseMult, to a curve25519 public
+// key.
+func RepresentativeToPublicKey(publicKey, representative *[32]byte) {
+ var rr2, v, e edwards25519.FieldElement
+ edwards25519.FeFromBytes(&rr2, representative)
+
+ edwards25519.FeSquare2(&rr2, &rr2)
+ rr2[0]++
+ edwards25519.FeInvert(&rr2, &rr2)
+ edwards25519.FeMul(&v, &edwards25519.A, &rr2)
+ edwards25519.FeNeg(&v, &v)
+
+ var v2, v3 edwards25519.FieldElement
+ edwards25519.FeSquare(&v2, &v)
+ edwards25519.FeMul(&v3, &v, &v2)
+ edwards25519.FeAdd(&e, &v3, &v)
+ edwards25519.FeMul(&v2, &v2, &edwards25519.A)
+ edwards25519.FeAdd(&e, &v2, &e)
+ chi(&e, &e)
+ var eBytes [32]byte
+ edwards25519.FeToBytes(&eBytes, &e)
+ // eBytes[1] is either 0 (for e = 1) or 0xff (for e = -1)
+ eIsMinus1 := int32(eBytes[1]) & 1
+ var negV edwards25519.FieldElement
+ edwards25519.FeNeg(&negV, &v)
+ edwards25519.FeCMove(&v, &negV, eIsMinus1)
+
+ edwards25519.FeZero(&v2)
+ edwards25519.FeCMove(&v2, &edwards25519.A, eIsMinus1)
+ edwards25519.FeSub(&v, &v, &v2)
+
+ edwards25519.FeToBytes(publicKey, &v)
+}
diff --git a/src/vendor/github.com/agl/ed25519/extra25519/extra25519_test.go b/src/vendor/github.com/agl/ed25519/extra25519/extra25519_test.go
new file mode 100644
index 0000000000..55be8ecab0
--- /dev/null
+++ b/src/vendor/github.com/agl/ed25519/extra25519/extra25519_test.go
@@ -0,0 +1,78 @@
+// Copyright 2013 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 extra25519
+
+import (
+ "bytes"
+ "crypto/rand"
+ "testing"
+
+ "github.com/agl/ed25519"
+ "golang.org/x/crypto/curve25519"
+)
+
+func TestCurve25519Conversion(t *testing.T) {
+ public, private, _ := ed25519.GenerateKey(rand.Reader)
+
+ var curve25519Public, curve25519Public2, curve25519Private [32]byte
+ PrivateKeyToCurve25519(&curve25519Private, private)
+ curve25519.ScalarBaseMult(&curve25519Public, &curve25519Private)
+
+ if !PublicKeyToCurve25519(&curve25519Public2, public) {
+ t.Fatalf("PublicKeyToCurve25519 failed")
+ }
+
+ if !bytes.Equal(curve25519Public[:], curve25519Public2[:]) {
+ t.Errorf("Values didn't match: curve25519 produced %x, conversion produced %x", curve25519Public[:], curve25519Public2[:])
+ }
+}
+
+func TestElligator(t *testing.T) {
+ var publicKey, publicKey2, publicKey3, representative, privateKey [32]byte
+
+ for i := 0; i < 1000; i++ {
+ rand.Reader.Read(privateKey[:])
+
+ if !ScalarBaseMult(&publicKey, &representative, &privateKey) {
+ continue
+ }
+ RepresentativeToPublicKey(&publicKey2, &representative)
+ if !bytes.Equal(publicKey[:], publicKey2[:]) {
+ t.Fatal("The resulting public key doesn't match the initial one.")
+ }
+
+ curve25519.ScalarBaseMult(&publicKey3, &privateKey)
+ if !bytes.Equal(publicKey[:], publicKey3[:]) {
+ t.Fatal("The public key doesn't match the value that curve25519 produced.")
+ }
+ }
+}
+
+func BenchmarkKeyGeneration(b *testing.B) {
+ var publicKey, representative, privateKey [32]byte
+
+ // Find the private key that results in a point that's in the image of the map.
+ for {
+ rand.Reader.Read(privateKey[:])
+ if ScalarBaseMult(&publicKey, &representative, &privateKey) {
+ break
+ }
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ ScalarBaseMult(&publicKey, &representative, &privateKey)
+ }
+}
+
+func BenchmarkMap(b *testing.B) {
+ var publicKey, representative [32]byte
+ rand.Reader.Read(representative[:])
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ RepresentativeToPublicKey(&publicKey, &representative)
+ }
+}
diff --git a/src/vendor/github.com/agl/ed25519/testdata/sign.input.gz b/src/vendor/github.com/agl/ed25519/testdata/sign.input.gz
new file mode 100644
index 0000000000..41030690c0
Binary files /dev/null and b/src/vendor/github.com/agl/ed25519/testdata/sign.input.gz differ
diff --git a/src/vendor/github.com/astaxie/beego/.gitignore b/src/vendor/github.com/astaxie/beego/.gitignore
new file mode 100644
index 0000000000..9806457b99
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/.gitignore
@@ -0,0 +1,5 @@
+.idea
+.DS_Store
+*.swp
+*.swo
+beego.iml
diff --git a/src/vendor/github.com/astaxie/beego/.travis.yml b/src/vendor/github.com/astaxie/beego/.travis.yml
new file mode 100644
index 0000000000..3c821dcd30
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/.travis.yml
@@ -0,0 +1,51 @@
+language: go
+
+go:
+ - tip
+ - 1.6.0
+ - 1.5.3
+ - 1.4.3
+services:
+ - redis-server
+ - mysql
+ - postgresql
+ - memcached
+env:
+ - ORM_DRIVER=sqlite3 ORM_SOURCE=$TRAVIS_BUILD_DIR/orm_test.db
+ - ORM_DRIVER=mysql ORM_SOURCE="root:@/orm_test?charset=utf8"
+ - ORM_DRIVER=postgres ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
+before_install:
+ - git clone git://github.com/ideawu/ssdb.git
+ - cd ssdb
+ - make
+ - cd ..
+install:
+ - go get github.com/lib/pq
+ - go get github.com/go-sql-driver/mysql
+ - go get github.com/mattn/go-sqlite3
+ - go get github.com/bradfitz/gomemcache/memcache
+ - go get github.com/garyburd/redigo/redis
+ - go get github.com/beego/x2j
+ - go get github.com/couchbase/go-couchbase
+ - go get github.com/beego/goyaml2
+ - go get github.com/belogik/goes
+ - go get github.com/siddontang/ledisdb/config
+ - go get github.com/siddontang/ledisdb/ledis
+ - go get golang.org/x/tools/cmd/vet
+ - go get github.com/golang/lint/golint
+ - go get github.com/ssdb/gossdb/ssdb
+before_script:
+ - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
+ - sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi"
+ - sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi"
+ - mkdir -p res/var
+ - ./ssdb/ssdb-server ./ssdb/ssdb.conf -d
+after_script:
+ -killall -w ssdb-server
+ - rm -rf ./res/var/*
+script:
+ - go vet -x ./...
+ - $HOME/gopath/bin/golint ./...
+ - go test -v ./...
+notifications:
+ webhooks: https://hooks.pubu.im/services/z7m9bvybl3rgtg9
diff --git a/src/vendor/github.com/astaxie/beego/cache/cache_test.go b/src/vendor/github.com/astaxie/beego/cache/cache_test.go
new file mode 100644
index 0000000000..9ceb606ad5
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/cache/cache_test.go
@@ -0,0 +1,168 @@
+// Copyright 2014 beego Author. 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 cache
+
+import (
+ "os"
+ "testing"
+ "time"
+)
+
+func TestCache(t *testing.T) {
+ bm, err := NewCache("memory", `{"interval":20}`)
+ if err != nil {
+ t.Error("init err")
+ }
+ timeoutDuration := 10 * time.Second
+ if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+
+ if v := bm.Get("astaxie"); v.(int) != 1 {
+ t.Error("get err")
+ }
+
+ time.Sleep(30 * time.Second)
+
+ if bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+
+ if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+
+ if err = bm.Incr("astaxie"); err != nil {
+ t.Error("Incr Error", err)
+ }
+
+ if v := bm.Get("astaxie"); v.(int) != 2 {
+ t.Error("get err")
+ }
+
+ if err = bm.Decr("astaxie"); err != nil {
+ t.Error("Decr Error", err)
+ }
+
+ if v := bm.Get("astaxie"); v.(int) != 1 {
+ t.Error("get err")
+ }
+ bm.Delete("astaxie")
+ if bm.IsExist("astaxie") {
+ t.Error("delete err")
+ }
+
+ //test GetMulti
+ if err = bm.Put("astaxie", "author", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+ if v := bm.Get("astaxie"); v.(string) != "author" {
+ t.Error("get err")
+ }
+
+ if err = bm.Put("astaxie1", "author1", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie1") {
+ t.Error("check err")
+ }
+
+ vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
+ if len(vv) != 2 {
+ t.Error("GetMulti ERROR")
+ }
+ if vv[0].(string) != "author" {
+ t.Error("GetMulti ERROR")
+ }
+ if vv[1].(string) != "author1" {
+ t.Error("GetMulti ERROR")
+ }
+}
+
+func TestFileCache(t *testing.T) {
+ bm, err := NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`)
+ if err != nil {
+ t.Error("init err")
+ }
+ timeoutDuration := 10 * time.Second
+ if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+
+ if v := bm.Get("astaxie"); v.(int) != 1 {
+ t.Error("get err")
+ }
+
+ if err = bm.Incr("astaxie"); err != nil {
+ t.Error("Incr Error", err)
+ }
+
+ if v := bm.Get("astaxie"); v.(int) != 2 {
+ t.Error("get err")
+ }
+
+ if err = bm.Decr("astaxie"); err != nil {
+ t.Error("Decr Error", err)
+ }
+
+ if v := bm.Get("astaxie"); v.(int) != 1 {
+ t.Error("get err")
+ }
+ bm.Delete("astaxie")
+ if bm.IsExist("astaxie") {
+ t.Error("delete err")
+ }
+
+ //test string
+ if err = bm.Put("astaxie", "author", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+ if v := bm.Get("astaxie"); v.(string) != "author" {
+ t.Error("get err")
+ }
+
+ //test GetMulti
+ if err = bm.Put("astaxie1", "author1", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie1") {
+ t.Error("check err")
+ }
+
+ vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
+ if len(vv) != 2 {
+ t.Error("GetMulti ERROR")
+ }
+ if vv[0].(string) != "author" {
+ t.Error("GetMulti ERROR")
+ }
+ if vv[1].(string) != "author1" {
+ t.Error("GetMulti ERROR")
+ }
+
+ os.RemoveAll("cache")
+}
diff --git a/src/vendor/github.com/astaxie/beego/cache/conv_test.go b/src/vendor/github.com/astaxie/beego/cache/conv_test.go
new file mode 100644
index 0000000000..cf792fa659
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/cache/conv_test.go
@@ -0,0 +1,143 @@
+// Copyright 2014 beego Author. 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 cache
+
+import (
+ "testing"
+)
+
+func TestGetString(t *testing.T) {
+ var t1 = "test1"
+ if "test1" != GetString(t1) {
+ t.Error("get string from string error")
+ }
+ var t2 = []byte("test2")
+ if "test2" != GetString(t2) {
+ t.Error("get string from byte array error")
+ }
+ var t3 = 1
+ if "1" != GetString(t3) {
+ t.Error("get string from int error")
+ }
+ var t4 int64 = 1
+ if "1" != GetString(t4) {
+ t.Error("get string from int64 error")
+ }
+ var t5 = 1.1
+ if "1.1" != GetString(t5) {
+ t.Error("get string from float64 error")
+ }
+
+ if "" != GetString(nil) {
+ t.Error("get string from nil error")
+ }
+}
+
+func TestGetInt(t *testing.T) {
+ var t1 = 1
+ if 1 != GetInt(t1) {
+ t.Error("get int from int error")
+ }
+ var t2 int32 = 32
+ if 32 != GetInt(t2) {
+ t.Error("get int from int32 error")
+ }
+ var t3 int64 = 64
+ if 64 != GetInt(t3) {
+ t.Error("get int from int64 error")
+ }
+ var t4 = "128"
+ if 128 != GetInt(t4) {
+ t.Error("get int from num string error")
+ }
+ if 0 != GetInt(nil) {
+ t.Error("get int from nil error")
+ }
+}
+
+func TestGetInt64(t *testing.T) {
+ var i int64 = 1
+ var t1 = 1
+ if i != GetInt64(t1) {
+ t.Error("get int64 from int error")
+ }
+ var t2 int32 = 1
+ if i != GetInt64(t2) {
+ t.Error("get int64 from int32 error")
+ }
+ var t3 int64 = 1
+ if i != GetInt64(t3) {
+ t.Error("get int64 from int64 error")
+ }
+ var t4 = "1"
+ if i != GetInt64(t4) {
+ t.Error("get int64 from num string error")
+ }
+ if 0 != GetInt64(nil) {
+ t.Error("get int64 from nil")
+ }
+}
+
+func TestGetFloat64(t *testing.T) {
+ var f = 1.11
+ var t1 float32 = 1.11
+ if f != GetFloat64(t1) {
+ t.Error("get float64 from float32 error")
+ }
+ var t2 = 1.11
+ if f != GetFloat64(t2) {
+ t.Error("get float64 from float64 error")
+ }
+ var t3 = "1.11"
+ if f != GetFloat64(t3) {
+ t.Error("get float64 from string error")
+ }
+
+ var f2 float64 = 1
+ var t4 = 1
+ if f2 != GetFloat64(t4) {
+ t.Error("get float64 from int error")
+ }
+
+ if 0 != GetFloat64(nil) {
+ t.Error("get float64 from nil error")
+ }
+}
+
+func TestGetBool(t *testing.T) {
+ var t1 = true
+ if true != GetBool(t1) {
+ t.Error("get bool from bool error")
+ }
+ var t2 = "true"
+ if true != GetBool(t2) {
+ t.Error("get bool from string error")
+ }
+ if false != GetBool(nil) {
+ t.Error("get bool from nil error")
+ }
+}
+
+func byteArrayEquals(a []byte, b []byte) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i, v := range a {
+ if v != b[i] {
+ return false
+ }
+ }
+ return true
+}
diff --git a/src/vendor/github.com/astaxie/beego/cache/memcache/memcache.go b/src/vendor/github.com/astaxie/beego/cache/memcache/memcache.go
new file mode 100644
index 0000000000..3f0fe4111c
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/cache/memcache/memcache.go
@@ -0,0 +1,190 @@
+// Copyright 2014 beego Author. 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 memcache for cache provider
+//
+// depend on github.com/bradfitz/gomemcache/memcache
+//
+// go install github.com/bradfitz/gomemcache/memcache
+//
+// Usage:
+// import(
+// _ "github.com/astaxie/beego/cache/memcache"
+// "github.com/astaxie/beego/cache"
+// )
+//
+// bm, err := cache.NewCache("memcache", `{"conn":"127.0.0.1:11211"}`)
+//
+// more docs http://beego.me/docs/module/cache.md
+package memcache
+
+import (
+ "encoding/json"
+ "errors"
+ "strings"
+
+ "github.com/bradfitz/gomemcache/memcache"
+
+ "time"
+
+ "github.com/astaxie/beego/cache"
+)
+
+// Cache Memcache adapter.
+type Cache struct {
+ conn *memcache.Client
+ conninfo []string
+}
+
+// NewMemCache create new memcache adapter.
+func NewMemCache() cache.Cache {
+ return &Cache{}
+}
+
+// Get get value from memcache.
+func (rc *Cache) Get(key string) interface{} {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ if item, err := rc.conn.Get(key); err == nil {
+ return string(item.Value)
+ }
+ return nil
+}
+
+// GetMulti get value from memcache.
+func (rc *Cache) GetMulti(keys []string) []interface{} {
+ size := len(keys)
+ var rv []interface{}
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ for i := 0; i < size; i++ {
+ rv = append(rv, err)
+ }
+ return rv
+ }
+ }
+ mv, err := rc.conn.GetMulti(keys)
+ if err == nil {
+ for _, v := range mv {
+ rv = append(rv, string(v.Value))
+ }
+ return rv
+ }
+ for i := 0; i < size; i++ {
+ rv = append(rv, err)
+ }
+ return rv
+}
+
+// Put put value to memcache. only support string.
+func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ v, ok := val.(string)
+ if !ok {
+ return errors.New("val must string")
+ }
+ item := memcache.Item{Key: key, Value: []byte(v), Expiration: int32(timeout / time.Second)}
+ return rc.conn.Set(&item)
+}
+
+// Delete delete value in memcache.
+func (rc *Cache) Delete(key string) error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ return rc.conn.Delete(key)
+}
+
+// Incr increase counter.
+func (rc *Cache) Incr(key string) error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ _, err := rc.conn.Increment(key, 1)
+ return err
+}
+
+// Decr decrease counter.
+func (rc *Cache) Decr(key string) error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ _, err := rc.conn.Decrement(key, 1)
+ return err
+}
+
+// IsExist check value exists in memcache.
+func (rc *Cache) IsExist(key string) bool {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return false
+ }
+ }
+ _, err := rc.conn.Get(key)
+ if err != nil {
+ return false
+ }
+ return true
+}
+
+// ClearAll clear all cached in memcache.
+func (rc *Cache) ClearAll() error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ return rc.conn.FlushAll()
+}
+
+// StartAndGC start memcache adapter.
+// config string is like {"conn":"connection info"}.
+// if connecting error, return.
+func (rc *Cache) StartAndGC(config string) error {
+ var cf map[string]string
+ json.Unmarshal([]byte(config), &cf)
+ if _, ok := cf["conn"]; !ok {
+ return errors.New("config has no conn key")
+ }
+ rc.conninfo = strings.Split(cf["conn"], ";")
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// connect to memcache and keep the connection.
+func (rc *Cache) connectInit() error {
+ rc.conn = memcache.New(rc.conninfo...)
+ return nil
+}
+
+func init() {
+ cache.Register("memcache", NewMemCache)
+}
diff --git a/src/vendor/github.com/astaxie/beego/cache/memcache/memcache_test.go b/src/vendor/github.com/astaxie/beego/cache/memcache/memcache_test.go
new file mode 100644
index 0000000000..0c8c57f295
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/cache/memcache/memcache_test.go
@@ -0,0 +1,108 @@
+// Copyright 2014 beego Author. 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 memcache
+
+import (
+ _ "github.com/bradfitz/gomemcache/memcache"
+
+ "strconv"
+ "testing"
+ "time"
+
+ "github.com/astaxie/beego/cache"
+)
+
+func TestMemcacheCache(t *testing.T) {
+ bm, err := cache.NewCache("memcache", `{"conn": "127.0.0.1:11211"}`)
+ if err != nil {
+ t.Error("init err")
+ }
+ timeoutDuration := 10 * time.Second
+ if err = bm.Put("astaxie", "1", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+
+ time.Sleep(11 * time.Second)
+
+ if bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+ if err = bm.Put("astaxie", "1", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+
+ if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 {
+ t.Error("get err")
+ }
+
+ if err = bm.Incr("astaxie"); err != nil {
+ t.Error("Incr Error", err)
+ }
+
+ if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 2 {
+ t.Error("get err")
+ }
+
+ if err = bm.Decr("astaxie"); err != nil {
+ t.Error("Decr Error", err)
+ }
+
+ if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 {
+ t.Error("get err")
+ }
+ bm.Delete("astaxie")
+ if bm.IsExist("astaxie") {
+ t.Error("delete err")
+ }
+
+ //test string
+ if err = bm.Put("astaxie", "author", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+
+ if v := bm.Get("astaxie").(string); v != "author" {
+ t.Error("get err")
+ }
+
+ //test GetMulti
+ if err = bm.Put("astaxie1", "author1", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie1") {
+ t.Error("check err")
+ }
+
+ vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
+ if len(vv) != 2 {
+ t.Error("GetMulti ERROR")
+ }
+ if vv[0].(string) != "author" && vv[0].(string) != "author1" {
+ t.Error("GetMulti ERROR")
+ }
+ if vv[1].(string) != "author1" && vv[1].(string) != "author" {
+ t.Error("GetMulti ERROR")
+ }
+
+ // test clear all
+ if err = bm.ClearAll(); err != nil {
+ t.Error("clear all err")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/cache/redis/redis_test.go b/src/vendor/github.com/astaxie/beego/cache/redis/redis_test.go
new file mode 100644
index 0000000000..47c5acc666
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/cache/redis/redis_test.go
@@ -0,0 +1,107 @@
+// Copyright 2014 beego Author. 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 redis
+
+import (
+ "testing"
+ "time"
+
+ "github.com/garyburd/redigo/redis"
+
+ "github.com/astaxie/beego/cache"
+)
+
+func TestRedisCache(t *testing.T) {
+ bm, err := cache.NewCache("redis", `{"conn": "127.0.0.1:6379"}`)
+ if err != nil {
+ t.Error("init err")
+ }
+ timeoutDuration := 10 * time.Second
+ if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+
+ time.Sleep(11 * time.Second)
+
+ if bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+ if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+
+ if v, _ := redis.Int(bm.Get("astaxie"), err); v != 1 {
+ t.Error("get err")
+ }
+
+ if err = bm.Incr("astaxie"); err != nil {
+ t.Error("Incr Error", err)
+ }
+
+ if v, _ := redis.Int(bm.Get("astaxie"), err); v != 2 {
+ t.Error("get err")
+ }
+
+ if err = bm.Decr("astaxie"); err != nil {
+ t.Error("Decr Error", err)
+ }
+
+ if v, _ := redis.Int(bm.Get("astaxie"), err); v != 1 {
+ t.Error("get err")
+ }
+ bm.Delete("astaxie")
+ if bm.IsExist("astaxie") {
+ t.Error("delete err")
+ }
+
+ //test string
+ if err = bm.Put("astaxie", "author", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie") {
+ t.Error("check err")
+ }
+
+ if v, _ := redis.String(bm.Get("astaxie"), err); v != "author" {
+ t.Error("get err")
+ }
+
+ //test GetMulti
+ if err = bm.Put("astaxie1", "author1", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !bm.IsExist("astaxie1") {
+ t.Error("check err")
+ }
+
+ vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
+ if len(vv) != 2 {
+ t.Error("GetMulti ERROR")
+ }
+ if v, _ := redis.String(vv[0], nil); v != "author" {
+ t.Error("GetMulti ERROR")
+ }
+ if v, _ := redis.String(vv[1], nil); v != "author1" {
+ t.Error("GetMulti ERROR")
+ }
+
+ // test clear all
+ if err = bm.ClearAll(); err != nil {
+ t.Error("clear all err")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb.go b/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb.go
new file mode 100644
index 0000000000..bfee69ce25
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb.go
@@ -0,0 +1,240 @@
+package ssdb
+
+import (
+ "encoding/json"
+ "errors"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/ssdb/gossdb/ssdb"
+
+ "github.com/astaxie/beego/cache"
+)
+
+// Cache SSDB adapter
+type Cache struct {
+ conn *ssdb.Client
+ conninfo []string
+}
+
+//NewSsdbCache create new ssdb adapter.
+func NewSsdbCache() cache.Cache {
+ return &Cache{}
+}
+
+// Get get value from memcache.
+func (rc *Cache) Get(key string) interface{} {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return nil
+ }
+ }
+ value, err := rc.conn.Get(key)
+ if err == nil {
+ return value
+ }
+ return nil
+}
+
+// GetMulti get value from memcache.
+func (rc *Cache) GetMulti(keys []string) []interface{} {
+ size := len(keys)
+ var values []interface{}
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ for i := 0; i < size; i++ {
+ values = append(values, err)
+ }
+ return values
+ }
+ }
+ res, err := rc.conn.Do("multi_get", keys)
+ resSize := len(res)
+ if err == nil {
+ for i := 1; i < resSize; i += 2 {
+ values = append(values, string(res[i+1]))
+ }
+ return values
+ }
+ for i := 0; i < size; i++ {
+ values = append(values, err)
+ }
+ return values
+}
+
+// DelMulti get value from memcache.
+func (rc *Cache) DelMulti(keys []string) error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ _, err := rc.conn.Do("multi_del", keys)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// Put put value to memcache. only support string.
+func (rc *Cache) Put(key string, value interface{}, timeout time.Duration) error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ v, ok := value.(string)
+ if !ok {
+ return errors.New("value must string")
+ }
+ var resp []string
+ var err error
+ ttl := int(timeout / time.Second)
+ if ttl < 0 {
+ resp, err = rc.conn.Do("set", key, v)
+ } else {
+ resp, err = rc.conn.Do("setx", key, v, ttl)
+ }
+ if err != nil {
+ return err
+ }
+ if len(resp) == 2 && resp[0] == "ok" {
+ return nil
+ }
+ return errors.New("bad response")
+}
+
+// Delete delete value in memcache.
+func (rc *Cache) Delete(key string) error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ _, err := rc.conn.Del(key)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// Incr increase counter.
+func (rc *Cache) Incr(key string) error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ _, err := rc.conn.Do("incr", key, 1)
+ return err
+}
+
+// Decr decrease counter.
+func (rc *Cache) Decr(key string) error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ _, err := rc.conn.Do("incr", key, -1)
+ return err
+}
+
+// IsExist check value exists in memcache.
+func (rc *Cache) IsExist(key string) bool {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return false
+ }
+ }
+ resp, err := rc.conn.Do("exists", key)
+ if err != nil {
+ return false
+ }
+ if resp[1] == "1" {
+ return true
+ }
+ return false
+
+}
+
+// ClearAll clear all cached in memcache.
+func (rc *Cache) ClearAll() error {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ keyStart, keyEnd, limit := "", "", 50
+ resp, err := rc.Scan(keyStart, keyEnd, limit)
+ for err == nil {
+ size := len(resp)
+ if size == 1 {
+ return nil
+ }
+ keys := []string{}
+ for i := 1; i < size; i += 2 {
+ keys = append(keys, string(resp[i]))
+ }
+ _, e := rc.conn.Do("multi_del", keys)
+ if e != nil {
+ return e
+ }
+ keyStart = resp[size-2]
+ resp, err = rc.Scan(keyStart, keyEnd, limit)
+ }
+ return err
+}
+
+// Scan key all cached in ssdb.
+func (rc *Cache) Scan(keyStart string, keyEnd string, limit int) ([]string, error) {
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return nil, err
+ }
+ }
+ resp, err := rc.conn.Do("scan", keyStart, keyEnd, limit)
+ if err != nil {
+ return nil, err
+ }
+ return resp, nil
+}
+
+// StartAndGC start memcache adapter.
+// config string is like {"conn":"connection info"}.
+// if connecting error, return.
+func (rc *Cache) StartAndGC(config string) error {
+ var cf map[string]string
+ json.Unmarshal([]byte(config), &cf)
+ if _, ok := cf["conn"]; !ok {
+ return errors.New("config has no conn key")
+ }
+ rc.conninfo = strings.Split(cf["conn"], ";")
+ if rc.conn == nil {
+ if err := rc.connectInit(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// connect to memcache and keep the connection.
+func (rc *Cache) connectInit() error {
+ conninfoArray := strings.Split(rc.conninfo[0], ":")
+ host := conninfoArray[0]
+ port, e := strconv.Atoi(conninfoArray[1])
+ if e != nil {
+ return e
+ }
+ var err error
+ rc.conn, err = ssdb.Connect(host, port)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func init() {
+ cache.Register("ssdb", NewSsdbCache)
+}
diff --git a/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb_test.go b/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb_test.go
new file mode 100644
index 0000000000..e03ba3437f
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb_test.go
@@ -0,0 +1,103 @@
+package ssdb
+
+import (
+ "github.com/astaxie/beego/cache"
+ "strconv"
+ "testing"
+ "time"
+)
+
+func TestSsdbcacheCache(t *testing.T) {
+ ssdb, err := cache.NewCache("ssdb", `{"conn": "127.0.0.1:8888"}`)
+ if err != nil {
+ t.Error("init err")
+ }
+
+ // test put and exist
+ if ssdb.IsExist("ssdb") {
+ t.Error("check err")
+ }
+ timeoutDuration := 10 * time.Second
+ //timeoutDuration := -10*time.Second if timeoutDuration is negtive,it means permanent
+ if err = ssdb.Put("ssdb", "ssdb", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if !ssdb.IsExist("ssdb") {
+ t.Error("check err")
+ }
+
+ // Get test done
+ if err = ssdb.Put("ssdb", "ssdb", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+
+ if v := ssdb.Get("ssdb"); v != "ssdb" {
+ t.Error("get Error")
+ }
+
+ //inc/dec test done
+ if err = ssdb.Put("ssdb", "2", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if err = ssdb.Incr("ssdb"); err != nil {
+ t.Error("incr Error", err)
+ }
+
+ if v, err := strconv.Atoi(ssdb.Get("ssdb").(string)); err != nil || v != 3 {
+ t.Error("get err")
+ }
+
+ if err = ssdb.Decr("ssdb"); err != nil {
+ t.Error("decr error")
+ }
+
+ // test del
+ if err = ssdb.Put("ssdb", "3", timeoutDuration); err != nil {
+ t.Error("set Error", err)
+ }
+ if v, err := strconv.Atoi(ssdb.Get("ssdb").(string)); err != nil || v != 3 {
+ t.Error("get err")
+ }
+ if err := ssdb.Delete("ssdb"); err == nil {
+ if ssdb.IsExist("ssdb") {
+ t.Error("delete err")
+ }
+ }
+
+ //test string
+ if err = ssdb.Put("ssdb", "ssdb", -10*time.Second); err != nil {
+ t.Error("set Error", err)
+ }
+ if !ssdb.IsExist("ssdb") {
+ t.Error("check err")
+ }
+ if v := ssdb.Get("ssdb").(string); v != "ssdb" {
+ t.Error("get err")
+ }
+
+ //test GetMulti done
+ if err = ssdb.Put("ssdb1", "ssdb1", -10*time.Second); err != nil {
+ t.Error("set Error", err)
+ }
+ if !ssdb.IsExist("ssdb1") {
+ t.Error("check err")
+ }
+ vv := ssdb.GetMulti([]string{"ssdb", "ssdb1"})
+ if len(vv) != 2 {
+ t.Error("getmulti error")
+ }
+ if vv[0].(string) != "ssdb" {
+ t.Error("getmulti error")
+ }
+ if vv[1].(string) != "ssdb1" {
+ t.Error("getmulti error")
+ }
+
+ // test clear all done
+ if err = ssdb.ClearAll(); err != nil {
+ t.Error("clear all err")
+ }
+ if ssdb.IsExist("ssdb") || ssdb.IsExist("ssdb1") {
+ t.Error("check err")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/config/ini_test.go b/src/vendor/github.com/astaxie/beego/config/ini_test.go
new file mode 100644
index 0000000000..93fce61ff4
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/config/ini_test.go
@@ -0,0 +1,184 @@
+// Copyright 2014 beego Author. 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 config
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+)
+
+func TestIni(t *testing.T) {
+
+ var (
+ inicontext = `
+;comment one
+#comment two
+appname = beeapi
+httpport = 8080
+mysqlport = 3600
+PI = 3.1415976
+runmode = "dev"
+autorender = false
+copyrequestbody = true
+session= on
+cookieon= off
+newreg = OFF
+needlogin = ON
+enableSession = Y
+enableCookie = N
+flag = 1
+[demo]
+key1="asta"
+key2 = "xie"
+CaseInsensitive = true
+peers = one;two;three
+`
+
+ keyValue = map[string]interface{}{
+ "appname": "beeapi",
+ "httpport": 8080,
+ "mysqlport": int64(3600),
+ "pi": 3.1415976,
+ "runmode": "dev",
+ "autorender": false,
+ "copyrequestbody": true,
+ "session": true,
+ "cookieon": false,
+ "newreg": false,
+ "needlogin": true,
+ "enableSession": true,
+ "enableCookie": false,
+ "flag": true,
+ "demo::key1": "asta",
+ "demo::key2": "xie",
+ "demo::CaseInsensitive": true,
+ "demo::peers": []string{"one", "two", "three"},
+ "null": "",
+ "demo2::key1": "",
+ "error": "",
+ "emptystrings": []string{},
+ }
+ )
+
+ f, err := os.Create("testini.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = f.WriteString(inicontext)
+ if err != nil {
+ f.Close()
+ t.Fatal(err)
+ }
+ f.Close()
+ defer os.Remove("testini.conf")
+ iniconf, err := NewConfig("ini", "testini.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ for k, v := range keyValue {
+ var err error
+ var value interface{}
+ switch v.(type) {
+ case int:
+ value, err = iniconf.Int(k)
+ case int64:
+ value, err = iniconf.Int64(k)
+ case float64:
+ value, err = iniconf.Float(k)
+ case bool:
+ value, err = iniconf.Bool(k)
+ case []string:
+ value = iniconf.Strings(k)
+ case string:
+ value = iniconf.String(k)
+ default:
+ value, err = iniconf.DIY(k)
+ }
+ if err != nil {
+ t.Fatalf("get key %q value fail,err %s", k, err)
+ } else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
+ t.Fatalf("get key %q value, want %v got %v .", k, v, value)
+ }
+
+ }
+ if err = iniconf.Set("name", "astaxie"); err != nil {
+ t.Fatal(err)
+ }
+ if iniconf.String("name") != "astaxie" {
+ t.Fatal("get name error")
+ }
+
+}
+
+func TestIniSave(t *testing.T) {
+
+ const (
+ inicontext = `
+app = app
+;comment one
+#comment two
+# comment three
+appname = beeapi
+httpport = 8080
+# DB Info
+# enable db
+[dbinfo]
+# db type name
+# suport mysql,sqlserver
+name = mysql
+`
+
+ saveResult = `
+app=app
+#comment one
+#comment two
+# comment three
+appname=beeapi
+httpport=8080
+
+# DB Info
+# enable db
+[dbinfo]
+# db type name
+# suport mysql,sqlserver
+name=mysql
+`
+ )
+ cfg, err := NewConfigData("ini", []byte(inicontext))
+ if err != nil {
+ t.Fatal(err)
+ }
+ name := "newIniConfig.ini"
+ if err := cfg.SaveConfigFile(name); err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove(name)
+
+ if data, err := ioutil.ReadFile(name); err != nil {
+ t.Fatal(err)
+ } else {
+ cfgData := string(data)
+ datas := strings.Split(saveResult, "\n")
+ for _, line := range datas {
+ if strings.Contains(cfgData, line+"\n") == false {
+ t.Fatalf("different after save ini config file. need contains %q", line)
+ }
+ }
+
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/config/json_test.go b/src/vendor/github.com/astaxie/beego/config/json_test.go
new file mode 100644
index 0000000000..df663461b7
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/config/json_test.go
@@ -0,0 +1,216 @@
+// Copyright 2014 beego Author. 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 config
+
+import (
+ "fmt"
+ "os"
+ "testing"
+)
+
+func TestJsonStartsWithArray(t *testing.T) {
+
+ const jsoncontextwitharray = `[
+ {
+ "url": "user",
+ "serviceAPI": "http://www.test.com/user"
+ },
+ {
+ "url": "employee",
+ "serviceAPI": "http://www.test.com/employee"
+ }
+]`
+ f, err := os.Create("testjsonWithArray.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = f.WriteString(jsoncontextwitharray)
+ if err != nil {
+ f.Close()
+ t.Fatal(err)
+ }
+ f.Close()
+ defer os.Remove("testjsonWithArray.conf")
+ jsonconf, err := NewConfig("json", "testjsonWithArray.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ rootArray, err := jsonconf.DIY("rootArray")
+ if err != nil {
+ t.Error("array does not exist as element")
+ }
+ rootArrayCasted := rootArray.([]interface{})
+ if rootArrayCasted == nil {
+ t.Error("array from root is nil")
+ } else {
+ elem := rootArrayCasted[0].(map[string]interface{})
+ if elem["url"] != "user" || elem["serviceAPI"] != "http://www.test.com/user" {
+ t.Error("array[0] values are not valid")
+ }
+
+ elem2 := rootArrayCasted[1].(map[string]interface{})
+ if elem2["url"] != "employee" || elem2["serviceAPI"] != "http://www.test.com/employee" {
+ t.Error("array[1] values are not valid")
+ }
+ }
+}
+
+func TestJson(t *testing.T) {
+
+ var (
+ jsoncontext = `{
+"appname": "beeapi",
+"testnames": "foo;bar",
+"httpport": 8080,
+"mysqlport": 3600,
+"PI": 3.1415976,
+"runmode": "dev",
+"autorender": false,
+"copyrequestbody": true,
+"session": "on",
+"cookieon": "off",
+"newreg": "OFF",
+"needlogin": "ON",
+"enableSession": "Y",
+"enableCookie": "N",
+"flag": 1,
+"database": {
+ "host": "host",
+ "port": "port",
+ "database": "database",
+ "username": "username",
+ "password": "password",
+ "conns":{
+ "maxconnection":12,
+ "autoconnect":true,
+ "connectioninfo":"info"
+ }
+ }
+}`
+ keyValue = map[string]interface{}{
+ "appname": "beeapi",
+ "testnames": []string{"foo", "bar"},
+ "httpport": 8080,
+ "mysqlport": int64(3600),
+ "PI": 3.1415976,
+ "runmode": "dev",
+ "autorender": false,
+ "copyrequestbody": true,
+ "session": true,
+ "cookieon": false,
+ "newreg": false,
+ "needlogin": true,
+ "enableSession": true,
+ "enableCookie": false,
+ "flag": true,
+ "database::host": "host",
+ "database::port": "port",
+ "database::database": "database",
+ "database::password": "password",
+ "database::conns::maxconnection": 12,
+ "database::conns::autoconnect": true,
+ "database::conns::connectioninfo": "info",
+ "unknown": "",
+ }
+ )
+
+ f, err := os.Create("testjson.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = f.WriteString(jsoncontext)
+ if err != nil {
+ f.Close()
+ t.Fatal(err)
+ }
+ f.Close()
+ defer os.Remove("testjson.conf")
+ jsonconf, err := NewConfig("json", "testjson.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for k, v := range keyValue {
+ var err error
+ var value interface{}
+ switch v.(type) {
+ case int:
+ value, err = jsonconf.Int(k)
+ case int64:
+ value, err = jsonconf.Int64(k)
+ case float64:
+ value, err = jsonconf.Float(k)
+ case bool:
+ value, err = jsonconf.Bool(k)
+ case []string:
+ value = jsonconf.Strings(k)
+ case string:
+ value = jsonconf.String(k)
+ default:
+ value, err = jsonconf.DIY(k)
+ }
+ if err != nil {
+ t.Fatalf("get key %q value fatal,%v err %s", k, v, err)
+ } else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
+ t.Fatalf("get key %q value, want %v got %v .", k, v, value)
+ }
+
+ }
+ if err = jsonconf.Set("name", "astaxie"); err != nil {
+ t.Fatal(err)
+ }
+ if jsonconf.String("name") != "astaxie" {
+ t.Fatal("get name error")
+ }
+
+ if db, err := jsonconf.DIY("database"); err != nil {
+ t.Fatal(err)
+ } else if m, ok := db.(map[string]interface{}); !ok {
+ t.Log(db)
+ t.Fatal("db not map[string]interface{}")
+ } else {
+ if m["host"].(string) != "host" {
+ t.Fatal("get host err")
+ }
+ }
+
+ if _, err := jsonconf.Int("unknown"); err == nil {
+ t.Error("unknown keys should return an error when expecting an Int")
+ }
+
+ if _, err := jsonconf.Int64("unknown"); err == nil {
+ t.Error("unknown keys should return an error when expecting an Int64")
+ }
+
+ if _, err := jsonconf.Float("unknown"); err == nil {
+ t.Error("unknown keys should return an error when expecting a Float")
+ }
+
+ if _, err := jsonconf.DIY("unknown"); err == nil {
+ t.Error("unknown keys should return an error when expecting an interface{}")
+ }
+
+ if val := jsonconf.String("unknown"); val != "" {
+ t.Error("unknown keys should return an empty string when expecting a String")
+ }
+
+ if _, err := jsonconf.Bool("unknown"); err == nil {
+ t.Error("unknown keys should return an error when expecting a Bool")
+ }
+
+ if !jsonconf.DefaultBool("unknow", true) {
+ t.Error("unknown keys with default value wrong")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/config/xml/xml.go b/src/vendor/github.com/astaxie/beego/config/xml/xml.go
new file mode 100644
index 0000000000..b5291bf4a0
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/config/xml/xml.go
@@ -0,0 +1,236 @@
+// Copyright 2014 beego Author. 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 xml for config provider
+//
+// depend on github.com/beego/x2j
+//
+// go install github.com/beego/x2j
+//
+// Usage:
+// import(
+// _ "github.com/astaxie/beego/config/xml"
+// "github.com/astaxie/beego/config"
+// )
+//
+// cnf, err := config.NewConfig("xml", "config.xml")
+//
+// more docs http://beego.me/docs/module/config.md
+package xml
+
+import (
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/astaxie/beego/config"
+ "github.com/beego/x2j"
+)
+
+// Config is a xml config parser and implements Config interface.
+// xml configurations should be included in tag.
+// only support key/value pair as value as each item.
+type Config struct{}
+
+// Parse returns a ConfigContainer with parsed xml config map.
+func (xc *Config) Parse(filename string) (config.Configer, error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ x := &ConfigContainer{data: make(map[string]interface{})}
+ content, err := ioutil.ReadAll(file)
+ if err != nil {
+ return nil, err
+ }
+
+ d, err := x2j.DocToMap(string(content))
+ if err != nil {
+ return nil, err
+ }
+
+ x.data = d["config"].(map[string]interface{})
+ return x, nil
+}
+
+// ParseData xml data
+func (xc *Config) ParseData(data []byte) (config.Configer, error) {
+ // Save memory data to temporary file
+ tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
+ os.MkdirAll(path.Dir(tmpName), os.ModePerm)
+ if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
+ return nil, err
+ }
+ return xc.Parse(tmpName)
+}
+
+// ConfigContainer A Config represents the xml configuration.
+type ConfigContainer struct {
+ data map[string]interface{}
+ sync.Mutex
+}
+
+// Bool returns the boolean value for a given key.
+func (c *ConfigContainer) Bool(key string) (bool, error) {
+ if v, ok := c.data[key]; ok {
+ return config.ParseBool(v)
+ }
+ return false, fmt.Errorf("not exist key: %q", key)
+}
+
+// DefaultBool return the bool value if has no error
+// otherwise return the defaultval
+func (c *ConfigContainer) DefaultBool(key string, defaultval bool) bool {
+ v, err := c.Bool(key)
+ if err != nil {
+ return defaultval
+ }
+ return v
+}
+
+// Int returns the integer value for a given key.
+func (c *ConfigContainer) Int(key string) (int, error) {
+ return strconv.Atoi(c.data[key].(string))
+}
+
+// DefaultInt returns the integer value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultInt(key string, defaultval int) int {
+ v, err := c.Int(key)
+ if err != nil {
+ return defaultval
+ }
+ return v
+}
+
+// Int64 returns the int64 value for a given key.
+func (c *ConfigContainer) Int64(key string) (int64, error) {
+ return strconv.ParseInt(c.data[key].(string), 10, 64)
+}
+
+// DefaultInt64 returns the int64 value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
+ v, err := c.Int64(key)
+ if err != nil {
+ return defaultval
+ }
+ return v
+
+}
+
+// Float returns the float value for a given key.
+func (c *ConfigContainer) Float(key string) (float64, error) {
+ return strconv.ParseFloat(c.data[key].(string), 64)
+}
+
+// DefaultFloat returns the float64 value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
+ v, err := c.Float(key)
+ if err != nil {
+ return defaultval
+ }
+ return v
+}
+
+// String returns the string value for a given key.
+func (c *ConfigContainer) String(key string) string {
+ if v, ok := c.data[key].(string); ok {
+ return v
+ }
+ return ""
+}
+
+// DefaultString returns the string value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultString(key string, defaultval string) string {
+ v := c.String(key)
+ if v == "" {
+ return defaultval
+ }
+ return v
+}
+
+// Strings returns the []string value for a given key.
+func (c *ConfigContainer) Strings(key string) []string {
+ v := c.String(key)
+ if v == "" {
+ return nil
+ }
+ return strings.Split(v, ";")
+}
+
+// DefaultStrings returns the []string value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []string {
+ v := c.Strings(key)
+ if v == nil {
+ return defaultval
+ }
+ return v
+}
+
+// GetSection returns map for the given section
+func (c *ConfigContainer) GetSection(section string) (map[string]string, error) {
+ if v, ok := c.data[section]; ok {
+ return v.(map[string]string), nil
+ }
+ return nil, errors.New("not exist setction")
+}
+
+// SaveConfigFile save the config into file
+func (c *ConfigContainer) SaveConfigFile(filename string) (err error) {
+ // Write configuration file by filename.
+ f, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ b, err := xml.MarshalIndent(c.data, " ", " ")
+ if err != nil {
+ return err
+ }
+ _, err = f.Write(b)
+ return err
+}
+
+// Set writes a new value for key.
+func (c *ConfigContainer) Set(key, val string) error {
+ c.Lock()
+ defer c.Unlock()
+ c.data[key] = val
+ return nil
+}
+
+// DIY returns the raw value by a given key.
+func (c *ConfigContainer) DIY(key string) (v interface{}, err error) {
+ if v, ok := c.data[key]; ok {
+ return v, nil
+ }
+ return nil, errors.New("not exist key")
+}
+
+func init() {
+ config.Register("xml", &Config{})
+}
diff --git a/src/vendor/github.com/astaxie/beego/config/xml/xml_test.go b/src/vendor/github.com/astaxie/beego/config/xml/xml_test.go
new file mode 100644
index 0000000000..60dcba54a9
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/config/xml/xml_test.go
@@ -0,0 +1,88 @@
+// Copyright 2014 beego Author. 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 xml
+
+import (
+ "os"
+ "testing"
+
+ "github.com/astaxie/beego/config"
+)
+
+//xml parse should incluce in tags
+var xmlcontext = `
+
+beeapi
+8080
+3600
+3.1415976
+dev
+false
+true
+
+`
+
+func TestXML(t *testing.T) {
+ f, err := os.Create("testxml.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = f.WriteString(xmlcontext)
+ if err != nil {
+ f.Close()
+ t.Fatal(err)
+ }
+ f.Close()
+ defer os.Remove("testxml.conf")
+ xmlconf, err := config.NewConfig("xml", "testxml.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if xmlconf.String("appname") != "beeapi" {
+ t.Fatal("appname not equal to beeapi")
+ }
+ if port, err := xmlconf.Int("httpport"); err != nil || port != 8080 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if port, err := xmlconf.Int64("mysqlport"); err != nil || port != 3600 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if pi, err := xmlconf.Float("PI"); err != nil || pi != 3.1415976 {
+ t.Error(pi)
+ t.Fatal(err)
+ }
+ if xmlconf.String("runmode") != "dev" {
+ t.Fatal("runmode not equal to dev")
+ }
+ if v, err := xmlconf.Bool("autorender"); err != nil || v != false {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if v, err := xmlconf.Bool("copyrequestbody"); err != nil || v != true {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if err = xmlconf.Set("name", "astaxie"); err != nil {
+ t.Fatal(err)
+ }
+ if xmlconf.String("name") != "astaxie" {
+ t.Fatal("get name error")
+ }
+ if xmlconf.Strings("emptystrings") != nil {
+ t.Fatal("get emtpy strings error")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/config/yaml/yaml.go b/src/vendor/github.com/astaxie/beego/config/yaml/yaml.go
new file mode 100644
index 0000000000..7e1d0426ef
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/config/yaml/yaml.go
@@ -0,0 +1,270 @@
+// Copyright 2014 beego Author. 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 yaml for config provider
+//
+// depend on github.com/beego/goyaml2
+//
+// go install github.com/beego/goyaml2
+//
+// Usage:
+// import(
+// _ "github.com/astaxie/beego/config/yaml"
+// "github.com/astaxie/beego/config"
+// )
+//
+// cnf, err := config.NewConfig("yaml", "config.yaml")
+//
+// more docs http://beego.me/docs/module/config.md
+package yaml
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/astaxie/beego/config"
+ "github.com/beego/goyaml2"
+)
+
+// Config is a yaml config parser and implements Config interface.
+type Config struct{}
+
+// Parse returns a ConfigContainer with parsed yaml config map.
+func (yaml *Config) Parse(filename string) (y config.Configer, err error) {
+ cnf, err := ReadYmlReader(filename)
+ if err != nil {
+ return
+ }
+ y = &ConfigContainer{
+ data: cnf,
+ }
+ return
+}
+
+// ParseData parse yaml data
+func (yaml *Config) ParseData(data []byte) (config.Configer, error) {
+ // Save memory data to temporary file
+ tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
+ os.MkdirAll(path.Dir(tmpName), os.ModePerm)
+ if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
+ return nil, err
+ }
+ return yaml.Parse(tmpName)
+}
+
+// ReadYmlReader Read yaml file to map.
+// if json like, use json package, unless goyaml2 package.
+func ReadYmlReader(path string) (cnf map[string]interface{}, err error) {
+ f, err := os.Open(path)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+
+ buf, err := ioutil.ReadAll(f)
+ if err != nil || len(buf) < 3 {
+ return
+ }
+
+ if string(buf[0:1]) == "{" {
+ log.Println("Look like a Json, try json umarshal")
+ err = json.Unmarshal(buf, &cnf)
+ if err == nil {
+ log.Println("It is Json Map")
+ return
+ }
+ }
+
+ data, err := goyaml2.Read(bytes.NewBuffer(buf))
+ if err != nil {
+ log.Println("Goyaml2 ERR>", string(buf), err)
+ return
+ }
+
+ if data == nil {
+ log.Println("Goyaml2 output nil? Pls report bug\n" + string(buf))
+ return
+ }
+ cnf, ok := data.(map[string]interface{})
+ if !ok {
+ log.Println("Not a Map? >> ", string(buf), data)
+ cnf = nil
+ }
+ return
+}
+
+// ConfigContainer A Config represents the yaml configuration.
+type ConfigContainer struct {
+ data map[string]interface{}
+ sync.Mutex
+}
+
+// Bool returns the boolean value for a given key.
+func (c *ConfigContainer) Bool(key string) (bool, error) {
+ if v, ok := c.data[key]; ok {
+ return config.ParseBool(v)
+ }
+ return false, fmt.Errorf("not exist key: %q", key)
+}
+
+// DefaultBool return the bool value if has no error
+// otherwise return the defaultval
+func (c *ConfigContainer) DefaultBool(key string, defaultval bool) bool {
+ v, err := c.Bool(key)
+ if err != nil {
+ return defaultval
+ }
+ return v
+}
+
+// Int returns the integer value for a given key.
+func (c *ConfigContainer) Int(key string) (int, error) {
+ if v, ok := c.data[key].(int64); ok {
+ return int(v), nil
+ }
+ return 0, errors.New("not int value")
+}
+
+// DefaultInt returns the integer value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultInt(key string, defaultval int) int {
+ v, err := c.Int(key)
+ if err != nil {
+ return defaultval
+ }
+ return v
+}
+
+// Int64 returns the int64 value for a given key.
+func (c *ConfigContainer) Int64(key string) (int64, error) {
+ if v, ok := c.data[key].(int64); ok {
+ return v, nil
+ }
+ return 0, errors.New("not bool value")
+}
+
+// DefaultInt64 returns the int64 value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
+ v, err := c.Int64(key)
+ if err != nil {
+ return defaultval
+ }
+ return v
+}
+
+// Float returns the float value for a given key.
+func (c *ConfigContainer) Float(key string) (float64, error) {
+ if v, ok := c.data[key].(float64); ok {
+ return v, nil
+ }
+ return 0.0, errors.New("not float64 value")
+}
+
+// DefaultFloat returns the float64 value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
+ v, err := c.Float(key)
+ if err != nil {
+ return defaultval
+ }
+ return v
+}
+
+// String returns the string value for a given key.
+func (c *ConfigContainer) String(key string) string {
+ if v, ok := c.data[key].(string); ok {
+ return v
+ }
+ return ""
+}
+
+// DefaultString returns the string value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultString(key string, defaultval string) string {
+ v := c.String(key)
+ if v == "" {
+ return defaultval
+ }
+ return v
+}
+
+// Strings returns the []string value for a given key.
+func (c *ConfigContainer) Strings(key string) []string {
+ v := c.String(key)
+ if v == "" {
+ return nil
+ }
+ return strings.Split(v, ";")
+}
+
+// DefaultStrings returns the []string value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []string {
+ v := c.Strings(key)
+ if v == nil {
+ return defaultval
+ }
+ return v
+}
+
+// GetSection returns map for the given section
+func (c *ConfigContainer) GetSection(section string) (map[string]string, error) {
+ v, ok := c.data[section]
+ if ok {
+ return v.(map[string]string), nil
+ }
+ return nil, errors.New("not exist setction")
+}
+
+// SaveConfigFile save the config into file
+func (c *ConfigContainer) SaveConfigFile(filename string) (err error) {
+ // Write configuration file by filename.
+ f, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ err = goyaml2.Write(f, c.data)
+ return err
+}
+
+// Set writes a new value for key.
+func (c *ConfigContainer) Set(key, val string) error {
+ c.Lock()
+ defer c.Unlock()
+ c.data[key] = val
+ return nil
+}
+
+// DIY returns the raw value by a given key.
+func (c *ConfigContainer) DIY(key string) (v interface{}, err error) {
+ if v, ok := c.data[key]; ok {
+ return v, nil
+ }
+ return nil, errors.New("not exist key")
+}
+
+func init() {
+ config.Register("yaml", &Config{})
+}
diff --git a/src/vendor/github.com/astaxie/beego/config/yaml/yaml_test.go b/src/vendor/github.com/astaxie/beego/config/yaml/yaml_test.go
new file mode 100644
index 0000000000..80cbb8fe4c
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/config/yaml/yaml_test.go
@@ -0,0 +1,86 @@
+// Copyright 2014 beego Author. 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 yaml
+
+import (
+ "os"
+ "testing"
+
+ "github.com/astaxie/beego/config"
+)
+
+var yamlcontext = `
+"appname": beeapi
+"httpport": 8080
+"mysqlport": 3600
+"PI": 3.1415976
+"runmode": dev
+"autorender": false
+"copyrequestbody": true
+`
+
+func TestYaml(t *testing.T) {
+ f, err := os.Create("testyaml.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = f.WriteString(yamlcontext)
+ if err != nil {
+ f.Close()
+ t.Fatal(err)
+ }
+ f.Close()
+ defer os.Remove("testyaml.conf")
+ yamlconf, err := config.NewConfig("yaml", "testyaml.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if yamlconf.String("appname") != "beeapi" {
+ t.Fatal("appname not equal to beeapi")
+ }
+ if port, err := yamlconf.Int("httpport"); err != nil || port != 8080 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if port, err := yamlconf.Int64("mysqlport"); err != nil || port != 3600 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if pi, err := yamlconf.Float("PI"); err != nil || pi != 3.1415976 {
+ t.Error(pi)
+ t.Fatal(err)
+ }
+ if yamlconf.String("runmode") != "dev" {
+ t.Fatal("runmode not equal to dev")
+ }
+ if v, err := yamlconf.Bool("autorender"); err != nil || v != false {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if v, err := yamlconf.Bool("copyrequestbody"); err != nil || v != true {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if err = yamlconf.Set("name", "astaxie"); err != nil {
+ t.Fatal(err)
+ }
+ if yamlconf.String("name") != "astaxie" {
+ t.Fatal("get name error")
+ }
+
+ if yamlconf.Strings("emptystrings") != nil {
+ t.Fatal("get emtpy strings error")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/config_test.go b/src/vendor/github.com/astaxie/beego/config_test.go
new file mode 100644
index 0000000000..cf4a781d17
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/config_test.go
@@ -0,0 +1,29 @@
+// Copyright 2014 beego Author. 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 beego
+
+import (
+ "testing"
+)
+
+func TestDefaults(t *testing.T) {
+ if BConfig.WebConfig.FlashName != "BEEGO_FLASH" {
+ t.Errorf("FlashName was not set to default.")
+ }
+
+ if BConfig.WebConfig.FlashSeparator != "BEEGOFLASH" {
+ t.Errorf("FlashName was not set to default.")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/context/acceptencoder_test.go b/src/vendor/github.com/astaxie/beego/context/acceptencoder_test.go
new file mode 100644
index 0000000000..3afff67999
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/context/acceptencoder_test.go
@@ -0,0 +1,44 @@
+// Copyright 2015 beego Author. 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 context
+
+import (
+ "net/http"
+ "testing"
+)
+
+func Test_ExtractEncoding(t *testing.T) {
+ if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip,deflate"}}}) != "gzip" {
+ t.Fail()
+ }
+ if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"deflate,gzip"}}}) != "deflate" {
+ t.Fail()
+ }
+ if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=.5,deflate"}}}) != "deflate" {
+ t.Fail()
+ }
+ if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=.5,deflate;q=0.3"}}}) != "gzip" {
+ t.Fail()
+ }
+ if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=0,deflate"}}}) != "deflate" {
+ t.Fail()
+ }
+ if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"deflate;q=0.5,gzip;q=0.5,identity"}}}) != "" {
+ t.Fail()
+ }
+ if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"*"}}}) != "gzip" {
+ t.Fail()
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/context/input_test.go b/src/vendor/github.com/astaxie/beego/context/input_test.go
new file mode 100644
index 0000000000..24f6fd99c0
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/context/input_test.go
@@ -0,0 +1,173 @@
+// Copyright 2014 beego Author. 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 context
+
+import (
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "testing"
+)
+
+func TestParse(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
+ beegoInput := NewInput()
+ beegoInput.Context = NewContext()
+ beegoInput.Context.Reset(httptest.NewRecorder(), r)
+ beegoInput.ParseFormOrMulitForm(1 << 20)
+
+ var id int
+ err := beegoInput.Bind(&id, "id")
+ if id != 123 || err != nil {
+ t.Fatal("id should has int value")
+ }
+ fmt.Println(id)
+
+ var isok bool
+ err = beegoInput.Bind(&isok, "isok")
+ if !isok || err != nil {
+ t.Fatal("isok should be true")
+ }
+ fmt.Println(isok)
+
+ var float float64
+ err = beegoInput.Bind(&float, "ft")
+ if float != 1.2 || err != nil {
+ t.Fatal("float should be equal to 1.2")
+ }
+ fmt.Println(float)
+
+ ol := make([]int, 0, 2)
+ err = beegoInput.Bind(&ol, "ol")
+ if len(ol) != 2 || err != nil || ol[0] != 1 || ol[1] != 2 {
+ t.Fatal("ol should has two elements")
+ }
+ fmt.Println(ol)
+
+ ul := make([]string, 0, 2)
+ err = beegoInput.Bind(&ul, "ul")
+ if len(ul) != 2 || err != nil || ul[0] != "str" || ul[1] != "array" {
+ t.Fatal("ul should has two elements")
+ }
+ fmt.Println(ul)
+
+ type User struct {
+ Name string
+ }
+ user := User{}
+ err = beegoInput.Bind(&user, "user")
+ if err != nil || user.Name != "astaxie" {
+ t.Fatal("user should has name")
+ }
+ fmt.Println(user)
+}
+
+func TestSubDomain(t *testing.T) {
+ r, _ := http.NewRequest("GET", "http://www.example.com/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
+ beegoInput := NewInput()
+ beegoInput.Context = NewContext()
+ beegoInput.Context.Reset(httptest.NewRecorder(), r)
+
+ subdomain := beegoInput.SubDomains()
+ if subdomain != "www" {
+ t.Fatal("Subdomain parse error, got" + subdomain)
+ }
+
+ r, _ = http.NewRequest("GET", "http://localhost/", nil)
+ beegoInput.Context.Request = r
+ if beegoInput.SubDomains() != "" {
+ t.Fatal("Subdomain parse error, should be empty, got " + beegoInput.SubDomains())
+ }
+
+ r, _ = http.NewRequest("GET", "http://aa.bb.example.com/", nil)
+ beegoInput.Context.Request = r
+ if beegoInput.SubDomains() != "aa.bb" {
+ t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+ }
+
+ /* TODO Fix this
+ r, _ = http.NewRequest("GET", "http://127.0.0.1/", nil)
+ beegoInput.Request = r
+ if beegoInput.SubDomains() != "" {
+ t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+ }
+ */
+
+ r, _ = http.NewRequest("GET", "http://example.com/", nil)
+ beegoInput.Context.Request = r
+ if beegoInput.SubDomains() != "" {
+ t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+ }
+
+ r, _ = http.NewRequest("GET", "http://aa.bb.cc.dd.example.com/", nil)
+ beegoInput.Context.Request = r
+ if beegoInput.SubDomains() != "aa.bb.cc.dd" {
+ t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+ }
+}
+
+func TestParams(t *testing.T) {
+ inp := NewInput()
+
+ inp.SetParam("p1", "val1_ver1")
+ inp.SetParam("p2", "val2_ver1")
+ inp.SetParam("p3", "val3_ver1")
+ if l := inp.ParamsLen(); l != 3 {
+ t.Fatalf("Input.ParamsLen wrong value: %d, expected %d", l, 3)
+ }
+
+ if val := inp.Param("p1"); val != "val1_ver1" {
+ t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver1")
+ }
+ if val := inp.Param("p3"); val != "val3_ver1" {
+ t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val3_ver1")
+ }
+ vals := inp.Params()
+ expected := map[string]string{
+ "p1": "val1_ver1",
+ "p2": "val2_ver1",
+ "p3": "val3_ver1",
+ }
+ if !reflect.DeepEqual(vals, expected) {
+ t.Fatalf("Input.Params wrong value: %s, expected %s", vals, expected)
+ }
+
+ // overwriting existing params
+ inp.SetParam("p1", "val1_ver2")
+ inp.SetParam("p2", "val2_ver2")
+ expected = map[string]string{
+ "p1": "val1_ver2",
+ "p2": "val2_ver2",
+ "p3": "val3_ver1",
+ }
+ vals = inp.Params()
+ if !reflect.DeepEqual(vals, expected) {
+ t.Fatalf("Input.Params wrong value: %s, expected %s", vals, expected)
+ }
+
+ if l := inp.ParamsLen(); l != 3 {
+ t.Fatalf("Input.ParamsLen wrong value: %d, expected %d", l, 3)
+ }
+
+ if val := inp.Param("p1"); val != "val1_ver2" {
+ t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver2")
+ }
+
+ if val := inp.Param("p2"); val != "val2_ver2" {
+ t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver2")
+ }
+
+}
diff --git a/src/vendor/github.com/astaxie/beego/controller_test.go b/src/vendor/github.com/astaxie/beego/controller_test.go
new file mode 100644
index 0000000000..51d3a5b7c9
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/controller_test.go
@@ -0,0 +1,77 @@
+// Copyright 2014 beego Author. 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 beego
+
+import (
+ "testing"
+
+ "github.com/astaxie/beego/context"
+)
+
+func TestGetInt(t *testing.T) {
+ i := context.NewInput()
+ i.SetParam("age", "40")
+ ctx := &context.Context{Input: i}
+ ctrlr := Controller{Ctx: ctx}
+ val, _ := ctrlr.GetInt("age")
+ if val != 40 {
+ t.Errorf("TestGetInt expect 40,get %T,%v", val, val)
+ }
+}
+
+func TestGetInt8(t *testing.T) {
+ i := context.NewInput()
+ i.SetParam("age", "40")
+ ctx := &context.Context{Input: i}
+ ctrlr := Controller{Ctx: ctx}
+ val, _ := ctrlr.GetInt8("age")
+ if val != 40 {
+ t.Errorf("TestGetInt8 expect 40,get %T,%v", val, val)
+ }
+ //Output: int8
+}
+
+func TestGetInt16(t *testing.T) {
+ i := context.NewInput()
+ i.SetParam("age", "40")
+ ctx := &context.Context{Input: i}
+ ctrlr := Controller{Ctx: ctx}
+ val, _ := ctrlr.GetInt16("age")
+ if val != 40 {
+ t.Errorf("TestGetInt16 expect 40,get %T,%v", val, val)
+ }
+}
+
+func TestGetInt32(t *testing.T) {
+ i := context.NewInput()
+ i.SetParam("age", "40")
+ ctx := &context.Context{Input: i}
+ ctrlr := Controller{Ctx: ctx}
+ val, _ := ctrlr.GetInt32("age")
+ if val != 40 {
+ t.Errorf("TestGetInt32 expect 40,get %T,%v", val, val)
+ }
+}
+
+func TestGetInt64(t *testing.T) {
+ i := context.NewInput()
+ i.SetParam("age", "40")
+ ctx := &context.Context{Input: i}
+ ctrlr := Controller{Ctx: ctx}
+ val, _ := ctrlr.GetInt64("age")
+ if val != 40 {
+ t.Errorf("TestGeetInt64 expect 40,get %T,%v", val, val)
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/filter_test.go b/src/vendor/github.com/astaxie/beego/filter_test.go
new file mode 100644
index 0000000000..d9928d8d70
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/filter_test.go
@@ -0,0 +1,74 @@
+// Copyright 2014 beego Author. 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 beego
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/astaxie/beego/context"
+ "github.com/astaxie/beego/logs"
+)
+
+func init() {
+ BeeLogger = logs.NewLogger(10000)
+ BeeLogger.SetLogger("console", "")
+}
+
+var FilterUser = func(ctx *context.Context) {
+ ctx.Output.Body([]byte("i am " + ctx.Input.Param(":last") + ctx.Input.Param(":first")))
+}
+
+func TestFilter(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/person/asta/Xie", nil)
+ w := httptest.NewRecorder()
+ handler := NewControllerRegister()
+ handler.InsertFilter("/person/:last/:first", BeforeRouter, FilterUser)
+ handler.Add("/person/:last/:first", &TestController{})
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "i am astaXie" {
+ t.Errorf("user define func can't run")
+ }
+}
+
+var FilterAdminUser = func(ctx *context.Context) {
+ ctx.Output.Body([]byte("i am admin"))
+}
+
+// Filter pattern /admin/:all
+// all url like /admin/ /admin/xie will all get filter
+
+func TestPatternTwo(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/admin/", nil)
+ w := httptest.NewRecorder()
+ handler := NewControllerRegister()
+ handler.InsertFilter("/admin/?:all", BeforeRouter, FilterAdminUser)
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "i am admin" {
+ t.Errorf("filter /admin/ can't run")
+ }
+}
+
+func TestPatternThree(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/admin/astaxie", nil)
+ w := httptest.NewRecorder()
+ handler := NewControllerRegister()
+ handler.InsertFilter("/admin/:all", BeforeRouter, FilterAdminUser)
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "i am admin" {
+ t.Errorf("filter /admin/astaxie can't run")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/flash_test.go b/src/vendor/github.com/astaxie/beego/flash_test.go
new file mode 100644
index 0000000000..640d54de6e
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/flash_test.go
@@ -0,0 +1,54 @@
+// Copyright 2014 beego Author. 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 beego
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+)
+
+type TestFlashController struct {
+ Controller
+}
+
+func (t *TestFlashController) TestWriteFlash() {
+ flash := NewFlash()
+ flash.Notice("TestFlashString")
+ flash.Store(&t.Controller)
+ // we choose to serve json because we don't want to load a template html file
+ t.ServeJSON(true)
+}
+
+func TestFlashHeader(t *testing.T) {
+ // create fake GET request
+ r, _ := http.NewRequest("GET", "/", nil)
+ w := httptest.NewRecorder()
+
+ // setup the handler
+ handler := NewControllerRegister()
+ handler.Add("/", &TestFlashController{}, "get:TestWriteFlash")
+ handler.ServeHTTP(w, r)
+
+ // get the Set-Cookie value
+ sc := w.Header().Get("Set-Cookie")
+ // match for the expected header
+ res := strings.Contains(sc, "BEEGO_FLASH=%00notice%23BEEGOFLASH%23TestFlashString%00")
+ // validate the assertion
+ if res != true {
+ t.Errorf("TestFlashHeader() unable to validate flash message")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/httplib/README.md b/src/vendor/github.com/astaxie/beego/httplib/README.md
new file mode 100644
index 0000000000..6a72cf7cf8
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/httplib/README.md
@@ -0,0 +1,97 @@
+# httplib
+httplib is an libs help you to curl remote url.
+
+# How to use?
+
+## GET
+you can use Get to crawl data.
+
+ import "github.com/astaxie/beego/httplib"
+
+ str, err := httplib.Get("http://beego.me/").String()
+ if err != nil {
+ // error
+ }
+ fmt.Println(str)
+
+## POST
+POST data to remote url
+
+ req := httplib.Post("http://beego.me/")
+ req.Param("username","astaxie")
+ req.Param("password","123456")
+ str, err := req.String()
+ if err != nil {
+ // error
+ }
+ fmt.Println(str)
+
+## Set timeout
+
+The default timeout is `60` seconds, function prototype:
+
+ SetTimeout(connectTimeout, readWriteTimeout time.Duration)
+
+Exmaple:
+
+ // GET
+ httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
+
+ // POST
+ httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
+
+
+## Debug
+
+If you want to debug the request info, set the debug on
+
+ httplib.Get("http://beego.me/").Debug(true)
+
+## Set HTTP Basic Auth
+
+ str, err := Get("http://beego.me/").SetBasicAuth("user", "passwd").String()
+ if err != nil {
+ // error
+ }
+ fmt.Println(str)
+
+## Set HTTPS
+
+If request url is https, You can set the client support TSL:
+
+ httplib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
+
+More info about the `tls.Config` please visit http://golang.org/pkg/crypto/tls/#Config
+
+## Set HTTP Version
+
+some servers need to specify the protocol version of HTTP
+
+ httplib.Get("http://beego.me/").SetProtocolVersion("HTTP/1.1")
+
+## Set Cookie
+
+some http request need setcookie. So set it like this:
+
+ cookie := &http.Cookie{}
+ cookie.Name = "username"
+ cookie.Value = "astaxie"
+ httplib.Get("http://beego.me/").SetCookie(cookie)
+
+## Upload file
+
+httplib support mutil file upload, use `req.PostFile()`
+
+ req := httplib.Post("http://beego.me/")
+ req.Param("username","astaxie")
+ req.PostFile("uploadfile1", "httplib.pdf")
+ str, err := req.String()
+ if err != nil {
+ // error
+ }
+ fmt.Println(str)
+
+
+See godoc for further documentation and examples.
+
+* [godoc.org/github.com/astaxie/beego/httplib](https://godoc.org/github.com/astaxie/beego/httplib)
diff --git a/src/vendor/github.com/astaxie/beego/httplib/httplib.go b/src/vendor/github.com/astaxie/beego/httplib/httplib.go
new file mode 100644
index 0000000000..7698412269
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/httplib/httplib.go
@@ -0,0 +1,551 @@
+// Copyright 2014 beego Author. 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 httplib is used as http.Client
+// Usage:
+//
+// import "github.com/astaxie/beego/httplib"
+//
+// b := httplib.Post("http://beego.me/")
+// b.Param("username","astaxie")
+// b.Param("password","123456")
+// b.PostFile("uploadfile1", "httplib.pdf")
+// b.PostFile("uploadfile2", "httplib.txt")
+// str, err := b.String()
+// if err != nil {
+// t.Fatal(err)
+// }
+// fmt.Println(str)
+//
+// more docs http://beego.me/docs/module/httplib.md
+package httplib
+
+import (
+ "bytes"
+ "compress/gzip"
+ "crypto/tls"
+ "encoding/json"
+ "encoding/xml"
+ "io"
+ "io/ioutil"
+ "log"
+ "mime/multipart"
+ "net"
+ "net/http"
+ "net/http/cookiejar"
+ "net/http/httputil"
+ "net/url"
+ "os"
+ "strings"
+ "sync"
+ "time"
+)
+
+var defaultSetting = BeegoHTTPSettings{
+ UserAgent: "beegoServer",
+ ConnectTimeout: 60 * time.Second,
+ ReadWriteTimeout: 60 * time.Second,
+ Gzip: true,
+ DumpBody: true,
+}
+
+var defaultCookieJar http.CookieJar
+var settingMutex sync.Mutex
+
+// createDefaultCookie creates a global cookiejar to store cookies.
+func createDefaultCookie() {
+ settingMutex.Lock()
+ defer settingMutex.Unlock()
+ defaultCookieJar, _ = cookiejar.New(nil)
+}
+
+// SetDefaultSetting Overwrite default settings
+func SetDefaultSetting(setting BeegoHTTPSettings) {
+ settingMutex.Lock()
+ defer settingMutex.Unlock()
+ defaultSetting = setting
+}
+
+// NewBeegoRequest return *BeegoHttpRequest with specific method
+func NewBeegoRequest(rawurl, method string) *BeegoHTTPRequest {
+ var resp http.Response
+ u, err := url.Parse(rawurl)
+ if err != nil {
+ log.Println("Httplib:", err)
+ }
+ req := http.Request{
+ URL: u,
+ Method: method,
+ Header: make(http.Header),
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ }
+ return &BeegoHTTPRequest{
+ url: rawurl,
+ req: &req,
+ params: map[string][]string{},
+ files: map[string]string{},
+ setting: defaultSetting,
+ resp: &resp,
+ }
+}
+
+// Get returns *BeegoHttpRequest with GET method.
+func Get(url string) *BeegoHTTPRequest {
+ return NewBeegoRequest(url, "GET")
+}
+
+// Post returns *BeegoHttpRequest with POST method.
+func Post(url string) *BeegoHTTPRequest {
+ return NewBeegoRequest(url, "POST")
+}
+
+// Put returns *BeegoHttpRequest with PUT method.
+func Put(url string) *BeegoHTTPRequest {
+ return NewBeegoRequest(url, "PUT")
+}
+
+// Delete returns *BeegoHttpRequest DELETE method.
+func Delete(url string) *BeegoHTTPRequest {
+ return NewBeegoRequest(url, "DELETE")
+}
+
+// Head returns *BeegoHttpRequest with HEAD method.
+func Head(url string) *BeegoHTTPRequest {
+ return NewBeegoRequest(url, "HEAD")
+}
+
+// BeegoHTTPSettings is the http.Client setting
+type BeegoHTTPSettings struct {
+ ShowDebug bool
+ UserAgent string
+ ConnectTimeout time.Duration
+ ReadWriteTimeout time.Duration
+ TLSClientConfig *tls.Config
+ Proxy func(*http.Request) (*url.URL, error)
+ Transport http.RoundTripper
+ EnableCookie bool
+ Gzip bool
+ DumpBody bool
+}
+
+// BeegoHTTPRequest provides more useful methods for requesting one url than http.Request.
+type BeegoHTTPRequest struct {
+ url string
+ req *http.Request
+ params map[string][]string
+ files map[string]string
+ setting BeegoHTTPSettings
+ resp *http.Response
+ body []byte
+ dump []byte
+}
+
+// GetRequest return the request object
+func (b *BeegoHTTPRequest) GetRequest() *http.Request {
+ return b.req
+}
+
+// Setting Change request settings
+func (b *BeegoHTTPRequest) Setting(setting BeegoHTTPSettings) *BeegoHTTPRequest {
+ b.setting = setting
+ return b
+}
+
+// SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password.
+func (b *BeegoHTTPRequest) SetBasicAuth(username, password string) *BeegoHTTPRequest {
+ b.req.SetBasicAuth(username, password)
+ return b
+}
+
+// SetEnableCookie sets enable/disable cookiejar
+func (b *BeegoHTTPRequest) SetEnableCookie(enable bool) *BeegoHTTPRequest {
+ b.setting.EnableCookie = enable
+ return b
+}
+
+// SetUserAgent sets User-Agent header field
+func (b *BeegoHTTPRequest) SetUserAgent(useragent string) *BeegoHTTPRequest {
+ b.setting.UserAgent = useragent
+ return b
+}
+
+// Debug sets show debug or not when executing request.
+func (b *BeegoHTTPRequest) Debug(isdebug bool) *BeegoHTTPRequest {
+ b.setting.ShowDebug = isdebug
+ return b
+}
+
+// DumpBody setting whether need to Dump the Body.
+func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest {
+ b.setting.DumpBody = isdump
+ return b
+}
+
+// DumpRequest return the DumpRequest
+func (b *BeegoHTTPRequest) DumpRequest() []byte {
+ return b.dump
+}
+
+// SetTimeout sets connect time out and read-write time out for BeegoRequest.
+func (b *BeegoHTTPRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHTTPRequest {
+ b.setting.ConnectTimeout = connectTimeout
+ b.setting.ReadWriteTimeout = readWriteTimeout
+ return b
+}
+
+// SetTLSClientConfig sets tls connection configurations if visiting https url.
+func (b *BeegoHTTPRequest) SetTLSClientConfig(config *tls.Config) *BeegoHTTPRequest {
+ b.setting.TLSClientConfig = config
+ return b
+}
+
+// Header add header item string in request.
+func (b *BeegoHTTPRequest) Header(key, value string) *BeegoHTTPRequest {
+ b.req.Header.Set(key, value)
+ return b
+}
+
+// SetHost set the request host
+func (b *BeegoHTTPRequest) SetHost(host string) *BeegoHTTPRequest {
+ b.req.Host = host
+ return b
+}
+
+// SetProtocolVersion Set the protocol version for incoming requests.
+// Client requests always use HTTP/1.1.
+func (b *BeegoHTTPRequest) SetProtocolVersion(vers string) *BeegoHTTPRequest {
+ if len(vers) == 0 {
+ vers = "HTTP/1.1"
+ }
+
+ major, minor, ok := http.ParseHTTPVersion(vers)
+ if ok {
+ b.req.Proto = vers
+ b.req.ProtoMajor = major
+ b.req.ProtoMinor = minor
+ }
+
+ return b
+}
+
+// SetCookie add cookie into request.
+func (b *BeegoHTTPRequest) SetCookie(cookie *http.Cookie) *BeegoHTTPRequest {
+ b.req.Header.Add("Cookie", cookie.String())
+ return b
+}
+
+// SetTransport set the setting transport
+func (b *BeegoHTTPRequest) SetTransport(transport http.RoundTripper) *BeegoHTTPRequest {
+ b.setting.Transport = transport
+ return b
+}
+
+// SetProxy set the http proxy
+// example:
+//
+// func(req *http.Request) (*url.URL, error) {
+// u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
+// return u, nil
+// }
+func (b *BeegoHTTPRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHTTPRequest {
+ b.setting.Proxy = proxy
+ return b
+}
+
+// Param adds query param in to request.
+// params build query string as ?key1=value1&key2=value2...
+func (b *BeegoHTTPRequest) Param(key, value string) *BeegoHTTPRequest {
+ if param, ok := b.params[key]; ok {
+ b.params[key] = append(param, value)
+ } else {
+ b.params[key] = []string{value}
+ }
+ return b
+}
+
+// PostFile add a post file to the request
+func (b *BeegoHTTPRequest) PostFile(formname, filename string) *BeegoHTTPRequest {
+ b.files[formname] = filename
+ return b
+}
+
+// Body adds request raw body.
+// it supports string and []byte.
+func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest {
+ switch t := data.(type) {
+ case string:
+ bf := bytes.NewBufferString(t)
+ b.req.Body = ioutil.NopCloser(bf)
+ b.req.ContentLength = int64(len(t))
+ case []byte:
+ bf := bytes.NewBuffer(t)
+ b.req.Body = ioutil.NopCloser(bf)
+ b.req.ContentLength = int64(len(t))
+ }
+ return b
+}
+
+// JSONBody adds request raw body encoding by JSON.
+func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) {
+ if b.req.Body == nil && obj != nil {
+ byts, err := json.Marshal(obj)
+ if err != nil {
+ return b, err
+ }
+ b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
+ b.req.ContentLength = int64(len(byts))
+ b.req.Header.Set("Content-Type", "application/json")
+ }
+ return b, nil
+}
+
+func (b *BeegoHTTPRequest) buildURL(paramBody string) {
+ // build GET url with query string
+ if b.req.Method == "GET" && len(paramBody) > 0 {
+ if strings.Index(b.url, "?") != -1 {
+ b.url += "&" + paramBody
+ } else {
+ b.url = b.url + "?" + paramBody
+ }
+ return
+ }
+
+ // build POST/PUT/PATCH url and body
+ if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH") && b.req.Body == nil {
+ // with files
+ if len(b.files) > 0 {
+ pr, pw := io.Pipe()
+ bodyWriter := multipart.NewWriter(pw)
+ go func() {
+ for formname, filename := range b.files {
+ fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
+ if err != nil {
+ log.Println("Httplib:", err)
+ }
+ fh, err := os.Open(filename)
+ if err != nil {
+ log.Println("Httplib:", err)
+ }
+ //iocopy
+ _, err = io.Copy(fileWriter, fh)
+ fh.Close()
+ if err != nil {
+ log.Println("Httplib:", err)
+ }
+ }
+ for k, v := range b.params {
+ for _, vv := range v {
+ bodyWriter.WriteField(k, vv)
+ }
+ }
+ bodyWriter.Close()
+ pw.Close()
+ }()
+ b.Header("Content-Type", bodyWriter.FormDataContentType())
+ b.req.Body = ioutil.NopCloser(pr)
+ return
+ }
+
+ // with params
+ if len(paramBody) > 0 {
+ b.Header("Content-Type", "application/x-www-form-urlencoded")
+ b.Body(paramBody)
+ }
+ }
+}
+
+func (b *BeegoHTTPRequest) getResponse() (*http.Response, error) {
+ if b.resp.StatusCode != 0 {
+ return b.resp, nil
+ }
+ resp, err := b.DoRequest()
+ if err != nil {
+ return nil, err
+ }
+ b.resp = resp
+ return resp, nil
+}
+
+// DoRequest will do the client.Do
+func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) {
+ var paramBody string
+ if len(b.params) > 0 {
+ var buf bytes.Buffer
+ for k, v := range b.params {
+ for _, vv := range v {
+ buf.WriteString(url.QueryEscape(k))
+ buf.WriteByte('=')
+ buf.WriteString(url.QueryEscape(vv))
+ buf.WriteByte('&')
+ }
+ }
+ paramBody = buf.String()
+ paramBody = paramBody[0 : len(paramBody)-1]
+ }
+
+ b.buildURL(paramBody)
+ url, err := url.Parse(b.url)
+ if err != nil {
+ return nil, err
+ }
+
+ b.req.URL = url
+
+ trans := b.setting.Transport
+
+ if trans == nil {
+ // create default transport
+ trans = &http.Transport{
+ TLSClientConfig: b.setting.TLSClientConfig,
+ Proxy: b.setting.Proxy,
+ Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
+ }
+ } else {
+ // if b.transport is *http.Transport then set the settings.
+ if t, ok := trans.(*http.Transport); ok {
+ if t.TLSClientConfig == nil {
+ t.TLSClientConfig = b.setting.TLSClientConfig
+ }
+ if t.Proxy == nil {
+ t.Proxy = b.setting.Proxy
+ }
+ if t.Dial == nil {
+ t.Dial = TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout)
+ }
+ }
+ }
+
+ var jar http.CookieJar
+ if b.setting.EnableCookie {
+ if defaultCookieJar == nil {
+ createDefaultCookie()
+ }
+ jar = defaultCookieJar
+ }
+
+ client := &http.Client{
+ Transport: trans,
+ Jar: jar,
+ }
+
+ if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" {
+ b.req.Header.Set("User-Agent", b.setting.UserAgent)
+ }
+
+ if b.setting.ShowDebug {
+ dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
+ if err != nil {
+ log.Println(err.Error())
+ }
+ b.dump = dump
+ }
+ return client.Do(b.req)
+}
+
+// String returns the body string in response.
+// it calls Response inner.
+func (b *BeegoHTTPRequest) String() (string, error) {
+ data, err := b.Bytes()
+ if err != nil {
+ return "", err
+ }
+
+ return string(data), nil
+}
+
+// Bytes returns the body []byte in response.
+// it calls Response inner.
+func (b *BeegoHTTPRequest) Bytes() ([]byte, error) {
+ if b.body != nil {
+ return b.body, nil
+ }
+ resp, err := b.getResponse()
+ if err != nil {
+ return nil, err
+ }
+ if resp.Body == nil {
+ return nil, nil
+ }
+ defer resp.Body.Close()
+ if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" {
+ reader, err := gzip.NewReader(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+ b.body, err = ioutil.ReadAll(reader)
+ } else {
+ b.body, err = ioutil.ReadAll(resp.Body)
+ }
+ return b.body, err
+}
+
+// ToFile saves the body data in response to one file.
+// it calls Response inner.
+func (b *BeegoHTTPRequest) ToFile(filename string) error {
+ f, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ resp, err := b.getResponse()
+ if err != nil {
+ return err
+ }
+ if resp.Body == nil {
+ return nil
+ }
+ defer resp.Body.Close()
+ _, err = io.Copy(f, resp.Body)
+ return err
+}
+
+// ToJSON returns the map that marshals from the body bytes as json in response .
+// it calls Response inner.
+func (b *BeegoHTTPRequest) ToJSON(v interface{}) error {
+ data, err := b.Bytes()
+ if err != nil {
+ return err
+ }
+ return json.Unmarshal(data, v)
+}
+
+// ToXML returns the map that marshals from the body bytes as xml in response .
+// it calls Response inner.
+func (b *BeegoHTTPRequest) ToXML(v interface{}) error {
+ data, err := b.Bytes()
+ if err != nil {
+ return err
+ }
+ return xml.Unmarshal(data, v)
+}
+
+// Response executes request client gets response mannually.
+func (b *BeegoHTTPRequest) Response() (*http.Response, error) {
+ return b.getResponse()
+}
+
+// TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
+func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
+ return func(netw, addr string) (net.Conn, error) {
+ conn, err := net.DialTimeout(netw, addr, cTimeout)
+ if err != nil {
+ return nil, err
+ }
+ err = conn.SetDeadline(time.Now().Add(rwTimeout))
+ return conn, err
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/httplib/httplib_test.go b/src/vendor/github.com/astaxie/beego/httplib/httplib_test.go
new file mode 100644
index 0000000000..058150547b
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/httplib/httplib_test.go
@@ -0,0 +1,218 @@
+// Copyright 2014 beego Author. 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 httplib
+
+import (
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestResponse(t *testing.T) {
+ req := Get("http://httpbin.org/get")
+ resp, err := req.Response()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(resp)
+}
+
+func TestGet(t *testing.T) {
+ req := Get("http://httpbin.org/get")
+ b, err := req.Bytes()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(b)
+
+ s, err := req.String()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(s)
+
+ if string(b) != s {
+ t.Fatal("request data not match")
+ }
+}
+
+func TestSimplePost(t *testing.T) {
+ v := "smallfish"
+ req := Post("http://httpbin.org/post")
+ req.Param("username", v)
+
+ str, err := req.String()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(str)
+
+ n := strings.Index(str, v)
+ if n == -1 {
+ t.Fatal(v + " not found in post")
+ }
+}
+
+//func TestPostFile(t *testing.T) {
+// v := "smallfish"
+// req := Post("http://httpbin.org/post")
+// req.Debug(true)
+// req.Param("username", v)
+// req.PostFile("uploadfile", "httplib_test.go")
+
+// str, err := req.String()
+// if err != nil {
+// t.Fatal(err)
+// }
+// t.Log(str)
+
+// n := strings.Index(str, v)
+// if n == -1 {
+// t.Fatal(v + " not found in post")
+// }
+//}
+
+func TestSimplePut(t *testing.T) {
+ str, err := Put("http://httpbin.org/put").String()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(str)
+}
+
+func TestSimpleDelete(t *testing.T) {
+ str, err := Delete("http://httpbin.org/delete").String()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(str)
+}
+
+func TestWithCookie(t *testing.T) {
+ v := "smallfish"
+ str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(str)
+
+ str, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(str)
+
+ n := strings.Index(str, v)
+ if n == -1 {
+ t.Fatal(v + " not found in cookie")
+ }
+}
+
+func TestWithBasicAuth(t *testing.T) {
+ str, err := Get("http://httpbin.org/basic-auth/user/passwd").SetBasicAuth("user", "passwd").String()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(str)
+ n := strings.Index(str, "authenticated")
+ if n == -1 {
+ t.Fatal("authenticated not found in response")
+ }
+}
+
+func TestWithUserAgent(t *testing.T) {
+ v := "beego"
+ str, err := Get("http://httpbin.org/headers").SetUserAgent(v).String()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(str)
+
+ n := strings.Index(str, v)
+ if n == -1 {
+ t.Fatal(v + " not found in user-agent")
+ }
+}
+
+func TestWithSetting(t *testing.T) {
+ v := "beego"
+ var setting BeegoHTTPSettings
+ setting.EnableCookie = true
+ setting.UserAgent = v
+ setting.Transport = nil
+ setting.ReadWriteTimeout = 5 * time.Second
+ SetDefaultSetting(setting)
+
+ str, err := Get("http://httpbin.org/get").String()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(str)
+
+ n := strings.Index(str, v)
+ if n == -1 {
+ t.Fatal(v + " not found in user-agent")
+ }
+}
+
+func TestToJson(t *testing.T) {
+ req := Get("http://httpbin.org/ip")
+ resp, err := req.Response()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(resp)
+
+ // httpbin will return http remote addr
+ type IP struct {
+ Origin string `json:"origin"`
+ }
+ var ip IP
+ err = req.ToJSON(&ip)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(ip.Origin)
+
+ if n := strings.Count(ip.Origin, "."); n != 3 {
+ t.Fatal("response is not valid ip")
+ }
+}
+
+func TestToFile(t *testing.T) {
+ f := "beego_testfile"
+ req := Get("http://httpbin.org/ip")
+ err := req.ToFile(f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove(f)
+ b, err := ioutil.ReadFile(f)
+ if n := strings.Index(string(b), "origin"); n == -1 {
+ t.Fatal(err)
+ }
+}
+
+func TestHeader(t *testing.T) {
+ req := Get("http://httpbin.org/headers")
+ req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36")
+ str, err := req.String()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(str)
+}
diff --git a/src/vendor/github.com/astaxie/beego/logs/conn_test.go b/src/vendor/github.com/astaxie/beego/logs/conn_test.go
new file mode 100644
index 0000000000..747fb890e0
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/logs/conn_test.go
@@ -0,0 +1,25 @@
+// Copyright 2014 beego Author. 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 logs
+
+import (
+ "testing"
+)
+
+func TestConn(t *testing.T) {
+ log := NewLogger(1000)
+ log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
+ log.Informational("informational")
+}
diff --git a/src/vendor/github.com/astaxie/beego/logs/console_test.go b/src/vendor/github.com/astaxie/beego/logs/console_test.go
new file mode 100644
index 0000000000..04f2bd7e1e
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/logs/console_test.go
@@ -0,0 +1,51 @@
+// Copyright 2014 beego Author. 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 logs
+
+import (
+ "testing"
+)
+
+// Try each log level in decreasing order of priority.
+func testConsoleCalls(bl *BeeLogger) {
+ bl.Emergency("emergency")
+ bl.Alert("alert")
+ bl.Critical("critical")
+ bl.Error("error")
+ bl.Warning("warning")
+ bl.Notice("notice")
+ bl.Informational("informational")
+ bl.Debug("debug")
+}
+
+// Test console logging by visually comparing the lines being output with and
+// without a log level specification.
+func TestConsole(t *testing.T) {
+ log1 := NewLogger(10000)
+ log1.EnableFuncCallDepth(true)
+ log1.SetLogger("console", "")
+ testConsoleCalls(log1)
+
+ log2 := NewLogger(100)
+ log2.SetLogger("console", `{"level":3}`)
+ testConsoleCalls(log2)
+}
+
+// Test console without color
+func TestConsoleNoColor(t *testing.T) {
+ log := NewLogger(100)
+ log.SetLogger("console", `{"color":false}`)
+ testConsoleCalls(log)
+}
diff --git a/src/vendor/github.com/astaxie/beego/logs/es/es.go b/src/vendor/github.com/astaxie/beego/logs/es/es.go
new file mode 100644
index 0000000000..397ca2eff2
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/logs/es/es.go
@@ -0,0 +1,80 @@
+package es
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net"
+ "net/url"
+ "time"
+
+ "github.com/astaxie/beego/logs"
+ "github.com/belogik/goes"
+)
+
+// NewES return a LoggerInterface
+func NewES() logs.Logger {
+ cw := &esLogger{
+ Level: logs.LevelDebug,
+ }
+ return cw
+}
+
+type esLogger struct {
+ *goes.Connection
+ DSN string `json:"dsn"`
+ Level int `json:"level"`
+}
+
+// {"dsn":"http://localhost:9200/","level":1}
+func (el *esLogger) Init(jsonconfig string) error {
+ err := json.Unmarshal([]byte(jsonconfig), el)
+ if err != nil {
+ return err
+ }
+ if el.DSN == "" {
+ return errors.New("empty dsn")
+ } else if u, err := url.Parse(el.DSN); err != nil {
+ return err
+ } else if u.Path == "" {
+ return errors.New("missing prefix")
+ } else if host, port, err := net.SplitHostPort(u.Host); err != nil {
+ return err
+ } else {
+ conn := goes.NewConnection(host, port)
+ el.Connection = conn
+ }
+ return nil
+}
+
+// WriteMsg will write the msg and level into es
+func (el *esLogger) WriteMsg(when time.Time, msg string, level int) error {
+ if level > el.Level {
+ return nil
+ }
+
+ vals := make(map[string]interface{})
+ vals["@timestamp"] = when.Format(time.RFC3339)
+ vals["@msg"] = msg
+ d := goes.Document{
+ Index: fmt.Sprintf("%04d.%02d.%02d", when.Year(), when.Month(), when.Day()),
+ Type: "logs",
+ Fields: vals,
+ }
+ _, err := el.Index(d, nil)
+ return err
+}
+
+// Destroy is a empty method
+func (el *esLogger) Destroy() {
+
+}
+
+// Flush is a empty method
+func (el *esLogger) Flush() {
+
+}
+
+func init() {
+ logs.Register("es", NewES)
+}
diff --git a/src/vendor/github.com/astaxie/beego/logs/file_test.go b/src/vendor/github.com/astaxie/beego/logs/file_test.go
new file mode 100644
index 0000000000..1fa6cdaa40
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/logs/file_test.go
@@ -0,0 +1,173 @@
+// Copyright 2014 beego Author. 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 logs
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strconv"
+ "testing"
+ "time"
+)
+
+func TestFile1(t *testing.T) {
+ log := NewLogger(10000)
+ log.SetLogger("file", `{"filename":"test.log"}`)
+ log.Debug("debug")
+ log.Informational("info")
+ log.Notice("notice")
+ log.Warning("warning")
+ log.Error("error")
+ log.Alert("alert")
+ log.Critical("critical")
+ log.Emergency("emergency")
+ f, err := os.Open("test.log")
+ if err != nil {
+ t.Fatal(err)
+ }
+ b := bufio.NewReader(f)
+ lineNum := 0
+ for {
+ line, _, err := b.ReadLine()
+ if err != nil {
+ break
+ }
+ if len(line) > 0 {
+ lineNum++
+ }
+ }
+ var expected = LevelDebug + 1
+ if lineNum != expected {
+ t.Fatal(lineNum, "not "+strconv.Itoa(expected)+" lines")
+ }
+ os.Remove("test.log")
+}
+
+func TestFile2(t *testing.T) {
+ log := NewLogger(10000)
+ log.SetLogger("file", fmt.Sprintf(`{"filename":"test2.log","level":%d}`, LevelError))
+ log.Debug("debug")
+ log.Info("info")
+ log.Notice("notice")
+ log.Warning("warning")
+ log.Error("error")
+ log.Alert("alert")
+ log.Critical("critical")
+ log.Emergency("emergency")
+ f, err := os.Open("test2.log")
+ if err != nil {
+ t.Fatal(err)
+ }
+ b := bufio.NewReader(f)
+ lineNum := 0
+ for {
+ line, _, err := b.ReadLine()
+ if err != nil {
+ break
+ }
+ if len(line) > 0 {
+ lineNum++
+ }
+ }
+ var expected = LevelError + 1
+ if lineNum != expected {
+ t.Fatal(lineNum, "not "+strconv.Itoa(expected)+" lines")
+ }
+ os.Remove("test2.log")
+}
+
+func TestFileRotate(t *testing.T) {
+ log := NewLogger(10000)
+ log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`)
+ log.Debug("debug")
+ log.Info("info")
+ log.Notice("notice")
+ log.Warning("warning")
+ log.Error("error")
+ log.Alert("alert")
+ log.Critical("critical")
+ log.Emergency("emergency")
+ rotateName := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + ".log"
+ b, err := exists(rotateName)
+ if !b || err != nil {
+ os.Remove("test3.log")
+ t.Fatal("rotate not generated")
+ }
+ os.Remove(rotateName)
+ os.Remove("test3.log")
+}
+
+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 BenchmarkFile(b *testing.B) {
+ log := NewLogger(100000)
+ log.SetLogger("file", `{"filename":"test4.log"}`)
+ for i := 0; i < b.N; i++ {
+ log.Debug("debug")
+ }
+ os.Remove("test4.log")
+}
+
+func BenchmarkFileAsynchronous(b *testing.B) {
+ log := NewLogger(100000)
+ log.SetLogger("file", `{"filename":"test4.log"}`)
+ log.Async()
+ for i := 0; i < b.N; i++ {
+ log.Debug("debug")
+ }
+ os.Remove("test4.log")
+}
+
+func BenchmarkFileCallDepth(b *testing.B) {
+ log := NewLogger(100000)
+ log.SetLogger("file", `{"filename":"test4.log"}`)
+ log.EnableFuncCallDepth(true)
+ log.SetLogFuncCallDepth(2)
+ for i := 0; i < b.N; i++ {
+ log.Debug("debug")
+ }
+ os.Remove("test4.log")
+}
+
+func BenchmarkFileAsynchronousCallDepth(b *testing.B) {
+ log := NewLogger(100000)
+ log.SetLogger("file", `{"filename":"test4.log"}`)
+ log.EnableFuncCallDepth(true)
+ log.SetLogFuncCallDepth(2)
+ log.Async()
+ for i := 0; i < b.N; i++ {
+ log.Debug("debug")
+ }
+ os.Remove("test4.log")
+}
+
+func BenchmarkFileOnGoroutine(b *testing.B) {
+ log := NewLogger(100000)
+ log.SetLogger("file", `{"filename":"test4.log"}`)
+ for i := 0; i < b.N; i++ {
+ go log.Debug("debug")
+ }
+ os.Remove("test4.log")
+}
diff --git a/src/vendor/github.com/astaxie/beego/logs/multifile_test.go b/src/vendor/github.com/astaxie/beego/logs/multifile_test.go
new file mode 100644
index 0000000000..57b960945e
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/logs/multifile_test.go
@@ -0,0 +1,78 @@
+// Copyright 2014 beego Author. 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 logs
+
+import (
+ "bufio"
+ "os"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func TestFiles_1(t *testing.T) {
+ log := NewLogger(10000)
+ log.SetLogger("multifile", `{"filename":"test.log","separate":["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"]}`)
+ log.Debug("debug")
+ log.Informational("info")
+ log.Notice("notice")
+ log.Warning("warning")
+ log.Error("error")
+ log.Alert("alert")
+ log.Critical("critical")
+ log.Emergency("emergency")
+ fns := []string{""}
+ fns = append(fns, levelNames[0:]...)
+ name := "test"
+ suffix := ".log"
+ for _, fn := range fns {
+
+ file := name + suffix
+ if fn != "" {
+ file = name + "." + fn + suffix
+ }
+ f, err := os.Open(file)
+ if err != nil {
+ t.Fatal(err)
+ }
+ b := bufio.NewReader(f)
+ lineNum := 0
+ lastLine := ""
+ for {
+ line, _, err := b.ReadLine()
+ if err != nil {
+ break
+ }
+ if len(line) > 0 {
+ lastLine = string(line)
+ lineNum++
+ }
+ }
+ var expected = 1
+ if fn == "" {
+ expected = LevelDebug + 1
+ }
+ if lineNum != expected {
+ t.Fatal(file, "has", lineNum, "lines not "+strconv.Itoa(expected)+" lines")
+ }
+ if lineNum == 1 {
+ if !strings.Contains(lastLine, fn) {
+ t.Fatal(file + " " + lastLine + " not contains the log msg " + fn)
+ }
+ }
+ os.Remove(file)
+ }
+
+}
diff --git a/src/vendor/github.com/astaxie/beego/logs/smtp_test.go b/src/vendor/github.com/astaxie/beego/logs/smtp_test.go
new file mode 100644
index 0000000000..28e762d236
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/logs/smtp_test.go
@@ -0,0 +1,27 @@
+// Copyright 2014 beego Author. 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 logs
+
+import (
+ "testing"
+ "time"
+)
+
+func TestSmtp(t *testing.T) {
+ log := NewLogger(10000)
+ log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
+ log.Critical("sendmail critical")
+ time.Sleep(time.Second * 30)
+}
diff --git a/src/vendor/github.com/astaxie/beego/migration/ddl.go b/src/vendor/github.com/astaxie/beego/migration/ddl.go
new file mode 100644
index 0000000000..51243337f5
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/migration/ddl.go
@@ -0,0 +1,53 @@
+// Copyright 2014 beego Author. 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 migration
+
+// Table store the tablename and Column
+type Table struct {
+ TableName string
+ Columns []*Column
+}
+
+// Create return the create sql
+func (t *Table) Create() string {
+ return ""
+}
+
+// Drop return the drop sql
+func (t *Table) Drop() string {
+ return ""
+}
+
+// Column define the columns name type and Default
+type Column struct {
+ Name string
+ Type string
+ Default interface{}
+}
+
+// Create return create sql with the provided tbname and columns
+func Create(tbname string, columns ...Column) string {
+ return ""
+}
+
+// Drop return the drop sql with the provided tbname and columns
+func Drop(tbname string, columns ...Column) string {
+ return ""
+}
+
+// TableDDL is still in think
+func TableDDL(tbname string, columns ...Column) string {
+ return ""
+}
diff --git a/src/vendor/github.com/astaxie/beego/migration/migration.go b/src/vendor/github.com/astaxie/beego/migration/migration.go
new file mode 100644
index 0000000000..1591bc50de
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/migration/migration.go
@@ -0,0 +1,278 @@
+// Copyright 2014 beego Author. 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 migration is used for migration
+//
+// The table structure is as follow:
+//
+// CREATE TABLE `migrations` (
+// `id_migration` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
+// `name` varchar(255) DEFAULT NULL COMMENT 'migration name, unique',
+// `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'date migrated or rolled back',
+// `statements` longtext COMMENT 'SQL statements for this migration',
+// `rollback_statements` longtext,
+// `status` enum('update','rollback') DEFAULT NULL COMMENT 'update indicates it is a normal migration while rollback means this migration is rolled back',
+// PRIMARY KEY (`id_migration`)
+// ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+package migration
+
+import (
+ "errors"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/astaxie/beego"
+ "github.com/astaxie/beego/orm"
+)
+
+// const the data format for the bee generate migration datatype
+const (
+ DateFormat = "20060102_150405"
+ DBDateFormat = "2006-01-02 15:04:05"
+)
+
+// Migrationer is an interface for all Migration struct
+type Migrationer interface {
+ Up()
+ Down()
+ Reset()
+ Exec(name, status string) error
+ GetCreated() int64
+}
+
+var (
+ migrationMap map[string]Migrationer
+)
+
+func init() {
+ migrationMap = make(map[string]Migrationer)
+}
+
+// Migration the basic type which will implement the basic type
+type Migration struct {
+ sqls []string
+ Created string
+}
+
+// Up implement in the Inheritance struct for upgrade
+func (m *Migration) Up() {
+
+}
+
+// Down implement in the Inheritance struct for down
+func (m *Migration) Down() {
+
+}
+
+// SQL add sql want to execute
+func (m *Migration) SQL(sql string) {
+ m.sqls = append(m.sqls, sql)
+}
+
+// Reset the sqls
+func (m *Migration) Reset() {
+ m.sqls = make([]string, 0)
+}
+
+// Exec execute the sql already add in the sql
+func (m *Migration) Exec(name, status string) error {
+ o := orm.NewOrm()
+ for _, s := range m.sqls {
+ beego.Info("exec sql:", s)
+ r := o.Raw(s)
+ _, err := r.Exec()
+ if err != nil {
+ return err
+ }
+ }
+ return m.addOrUpdateRecord(name, status)
+}
+
+func (m *Migration) addOrUpdateRecord(name, status string) error {
+ o := orm.NewOrm()
+ if status == "down" {
+ status = "rollback"
+ p, err := o.Raw("update migrations set status = ?, rollback_statements = ?, created_at = ? where name = ?").Prepare()
+ if err != nil {
+ return nil
+ }
+ _, err = p.Exec(status, strings.Join(m.sqls, "; "), time.Now().Format(DBDateFormat), name)
+ return err
+ }
+ status = "update"
+ p, err := o.Raw("insert into migrations(name, created_at, statements, status) values(?,?,?,?)").Prepare()
+ if err != nil {
+ return err
+ }
+ _, err = p.Exec(name, time.Now().Format(DBDateFormat), strings.Join(m.sqls, "; "), status)
+ return err
+}
+
+// GetCreated get the unixtime from the Created
+func (m *Migration) GetCreated() int64 {
+ t, err := time.Parse(DateFormat, m.Created)
+ if err != nil {
+ return 0
+ }
+ return t.Unix()
+}
+
+// Register register the Migration in the map
+func Register(name string, m Migrationer) error {
+ if _, ok := migrationMap[name]; ok {
+ return errors.New("already exist name:" + name)
+ }
+ migrationMap[name] = m
+ return nil
+}
+
+// Upgrade upgrate the migration from lasttime
+func Upgrade(lasttime int64) error {
+ sm := sortMap(migrationMap)
+ i := 0
+ for _, v := range sm {
+ if v.created > lasttime {
+ beego.Info("start upgrade", v.name)
+ v.m.Reset()
+ v.m.Up()
+ err := v.m.Exec(v.name, "up")
+ if err != nil {
+ beego.Error("execute error:", err)
+ time.Sleep(2 * time.Second)
+ return err
+ }
+ beego.Info("end upgrade:", v.name)
+ i++
+ }
+ }
+ beego.Info("total success upgrade:", i, " migration")
+ time.Sleep(2 * time.Second)
+ return nil
+}
+
+// Rollback rollback the migration by the name
+func Rollback(name string) error {
+ if v, ok := migrationMap[name]; ok {
+ beego.Info("start rollback")
+ v.Reset()
+ v.Down()
+ err := v.Exec(name, "down")
+ if err != nil {
+ beego.Error("execute error:", err)
+ time.Sleep(2 * time.Second)
+ return err
+ }
+ beego.Info("end rollback")
+ time.Sleep(2 * time.Second)
+ return nil
+ }
+ beego.Error("not exist the migrationMap name:" + name)
+ time.Sleep(2 * time.Second)
+ return errors.New("not exist the migrationMap name:" + name)
+}
+
+// Reset reset all migration
+// run all migration's down function
+func Reset() error {
+ sm := sortMap(migrationMap)
+ i := 0
+ for j := len(sm) - 1; j >= 0; j-- {
+ v := sm[j]
+ if isRollBack(v.name) {
+ beego.Info("skip the", v.name)
+ time.Sleep(1 * time.Second)
+ continue
+ }
+ beego.Info("start reset:", v.name)
+ v.m.Reset()
+ v.m.Down()
+ err := v.m.Exec(v.name, "down")
+ if err != nil {
+ beego.Error("execute error:", err)
+ time.Sleep(2 * time.Second)
+ return err
+ }
+ i++
+ beego.Info("end reset:", v.name)
+ }
+ beego.Info("total success reset:", i, " migration")
+ time.Sleep(2 * time.Second)
+ return nil
+}
+
+// Refresh first Reset, then Upgrade
+func Refresh() error {
+ err := Reset()
+ if err != nil {
+ beego.Error("execute error:", err)
+ time.Sleep(2 * time.Second)
+ return err
+ }
+ err = Upgrade(0)
+ return err
+}
+
+type dataSlice []data
+
+type data struct {
+ created int64
+ name string
+ m Migrationer
+}
+
+// Len is part of sort.Interface.
+func (d dataSlice) Len() int {
+ return len(d)
+}
+
+// Swap is part of sort.Interface.
+func (d dataSlice) Swap(i, j int) {
+ d[i], d[j] = d[j], d[i]
+}
+
+// Less is part of sort.Interface. We use count as the value to sort by
+func (d dataSlice) Less(i, j int) bool {
+ return d[i].created < d[j].created
+}
+
+func sortMap(m map[string]Migrationer) dataSlice {
+ s := make(dataSlice, 0, len(m))
+ for k, v := range m {
+ d := data{}
+ d.created = v.GetCreated()
+ d.name = k
+ d.m = v
+ s = append(s, d)
+ }
+ sort.Sort(s)
+ return s
+}
+
+func isRollBack(name string) bool {
+ o := orm.NewOrm()
+ var maps []orm.Params
+ num, err := o.Raw("select * from migrations where `name` = ? order by id_migration desc", name).Values(&maps)
+ if err != nil {
+ beego.Info("get name has error", err)
+ return false
+ }
+ if num <= 0 {
+ return false
+ }
+ if maps[0]["status"] == "rollback" {
+ return true
+ }
+ return false
+}
diff --git a/src/vendor/github.com/astaxie/beego/namespace_test.go b/src/vendor/github.com/astaxie/beego/namespace_test.go
new file mode 100644
index 0000000000..a92ae3efd1
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/namespace_test.go
@@ -0,0 +1,171 @@
+// Copyright 2014 beego Author. 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 beego
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "strconv"
+ "testing"
+
+ "github.com/astaxie/beego/context"
+)
+
+func TestNamespaceGet(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/v1/user", nil)
+ w := httptest.NewRecorder()
+
+ ns := NewNamespace("/v1")
+ ns.Get("/user", func(ctx *context.Context) {
+ ctx.Output.Body([]byte("v1_user"))
+ })
+ AddNamespace(ns)
+ BeeApp.Handlers.ServeHTTP(w, r)
+ if w.Body.String() != "v1_user" {
+ t.Errorf("TestNamespaceGet can't run, get the response is " + w.Body.String())
+ }
+}
+
+func TestNamespacePost(t *testing.T) {
+ r, _ := http.NewRequest("POST", "/v1/user/123", nil)
+ w := httptest.NewRecorder()
+
+ ns := NewNamespace("/v1")
+ ns.Post("/user/:id", func(ctx *context.Context) {
+ ctx.Output.Body([]byte(ctx.Input.Param(":id")))
+ })
+ AddNamespace(ns)
+ BeeApp.Handlers.ServeHTTP(w, r)
+ if w.Body.String() != "123" {
+ t.Errorf("TestNamespacePost can't run, get the response is " + w.Body.String())
+ }
+}
+
+func TestNamespaceNest(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/v1/admin/order", nil)
+ w := httptest.NewRecorder()
+
+ ns := NewNamespace("/v1")
+ ns.Namespace(
+ NewNamespace("/admin").
+ Get("/order", func(ctx *context.Context) {
+ ctx.Output.Body([]byte("order"))
+ }),
+ )
+ AddNamespace(ns)
+ BeeApp.Handlers.ServeHTTP(w, r)
+ if w.Body.String() != "order" {
+ t.Errorf("TestNamespaceNest can't run, get the response is " + w.Body.String())
+ }
+}
+
+func TestNamespaceNestParam(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/v1/admin/order/123", nil)
+ w := httptest.NewRecorder()
+
+ ns := NewNamespace("/v1")
+ ns.Namespace(
+ NewNamespace("/admin").
+ Get("/order/:id", func(ctx *context.Context) {
+ ctx.Output.Body([]byte(ctx.Input.Param(":id")))
+ }),
+ )
+ AddNamespace(ns)
+ BeeApp.Handlers.ServeHTTP(w, r)
+ if w.Body.String() != "123" {
+ t.Errorf("TestNamespaceNestParam can't run, get the response is " + w.Body.String())
+ }
+}
+
+func TestNamespaceRouter(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/v1/api/list", nil)
+ w := httptest.NewRecorder()
+
+ ns := NewNamespace("/v1")
+ ns.Router("/api/list", &TestController{}, "*:List")
+ AddNamespace(ns)
+ BeeApp.Handlers.ServeHTTP(w, r)
+ if w.Body.String() != "i am list" {
+ t.Errorf("TestNamespaceRouter can't run, get the response is " + w.Body.String())
+ }
+}
+
+func TestNamespaceAutoFunc(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/v1/test/list", nil)
+ w := httptest.NewRecorder()
+
+ ns := NewNamespace("/v1")
+ ns.AutoRouter(&TestController{})
+ AddNamespace(ns)
+ BeeApp.Handlers.ServeHTTP(w, r)
+ if w.Body.String() != "i am list" {
+ t.Errorf("user define func can't run")
+ }
+}
+
+func TestNamespaceFilter(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/v1/user/123", nil)
+ w := httptest.NewRecorder()
+
+ ns := NewNamespace("/v1")
+ ns.Filter("before", func(ctx *context.Context) {
+ ctx.Output.Body([]byte("this is Filter"))
+ }).
+ Get("/user/:id", func(ctx *context.Context) {
+ ctx.Output.Body([]byte(ctx.Input.Param(":id")))
+ })
+ AddNamespace(ns)
+ BeeApp.Handlers.ServeHTTP(w, r)
+ if w.Body.String() != "this is Filter" {
+ t.Errorf("TestNamespaceFilter can't run, get the response is " + w.Body.String())
+ }
+}
+
+func TestNamespaceCond(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/v2/test/list", nil)
+ w := httptest.NewRecorder()
+
+ ns := NewNamespace("/v2")
+ ns.Cond(func(ctx *context.Context) bool {
+ if ctx.Input.Domain() == "beego.me" {
+ return true
+ }
+ return false
+ }).
+ AutoRouter(&TestController{})
+ AddNamespace(ns)
+ BeeApp.Handlers.ServeHTTP(w, r)
+ if w.Code != 405 {
+ t.Errorf("TestNamespaceCond can't run get the result " + strconv.Itoa(w.Code))
+ }
+}
+
+func TestNamespaceInside(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/v3/shop/order/123", nil)
+ w := httptest.NewRecorder()
+ ns := NewNamespace("/v3",
+ NSAutoRouter(&TestController{}),
+ NSNamespace("/shop",
+ NSGet("/order/:id", func(ctx *context.Context) {
+ ctx.Output.Body([]byte(ctx.Input.Param(":id")))
+ }),
+ ),
+ )
+ AddNamespace(ns)
+ BeeApp.Handlers.ServeHTTP(w, r)
+ if w.Body.String() != "123" {
+ t.Errorf("TestNamespaceInside can't run, get the response is " + w.Body.String())
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/orm/models_test.go b/src/vendor/github.com/astaxie/beego/orm/models_test.go
new file mode 100644
index 0000000000..ffb16ea021
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/orm/models_test.go
@@ -0,0 +1,457 @@
+// Copyright 2014 beego Author. 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 orm
+
+import (
+ "database/sql"
+ "encoding/json"
+ "fmt"
+ "os"
+ "strings"
+ "time"
+
+ _ "github.com/go-sql-driver/mysql"
+ _ "github.com/lib/pq"
+ _ "github.com/mattn/go-sqlite3"
+ // As tidb can't use go get, so disable the tidb testing now
+ // _ "github.com/pingcap/tidb"
+)
+
+// A slice string field.
+type SliceStringField []string
+
+func (e SliceStringField) Value() []string {
+ return []string(e)
+}
+
+func (e *SliceStringField) Set(d []string) {
+ *e = SliceStringField(d)
+}
+
+func (e *SliceStringField) Add(v string) {
+ *e = append(*e, v)
+}
+
+func (e *SliceStringField) String() string {
+ return strings.Join(e.Value(), ",")
+}
+
+func (e *SliceStringField) FieldType() int {
+ return TypeCharField
+}
+
+func (e *SliceStringField) SetRaw(value interface{}) error {
+ switch d := value.(type) {
+ case []string:
+ e.Set(d)
+ case string:
+ if len(d) > 0 {
+ parts := strings.Split(d, ",")
+ v := make([]string, 0, len(parts))
+ for _, p := range parts {
+ v = append(v, strings.TrimSpace(p))
+ }
+ e.Set(v)
+ }
+ default:
+ return fmt.Errorf(" unknown value `%v`", value)
+ }
+ return nil
+}
+
+func (e *SliceStringField) RawValue() interface{} {
+ return e.String()
+}
+
+var _ Fielder = new(SliceStringField)
+
+// A json field.
+type JSONField struct {
+ Name string
+ Data string
+}
+
+func (e *JSONField) String() string {
+ data, _ := json.Marshal(e)
+ return string(data)
+}
+
+func (e *JSONField) FieldType() int {
+ return TypeTextField
+}
+
+func (e *JSONField) SetRaw(value interface{}) error {
+ switch d := value.(type) {
+ case string:
+ return json.Unmarshal([]byte(d), e)
+ default:
+ return fmt.Errorf(" unknown value `%v`", value)
+ }
+}
+
+func (e *JSONField) RawValue() interface{} {
+ return e.String()
+}
+
+var _ Fielder = new(JSONField)
+
+type Data struct {
+ ID int `orm:"column(id)"`
+ Boolean bool
+ Char string `orm:"size(50)"`
+ Text string `orm:"type(text)"`
+ Date time.Time `orm:"type(date)"`
+ DateTime time.Time `orm:"column(datetime)"`
+ Byte byte
+ Rune rune
+ Int int
+ Int8 int8
+ Int16 int16
+ Int32 int32
+ Int64 int64
+ Uint uint
+ Uint8 uint8
+ Uint16 uint16
+ Uint32 uint32
+ Uint64 uint64
+ Float32 float32
+ Float64 float64
+ Decimal float64 `orm:"digits(8);decimals(4)"`
+}
+
+type DataNull struct {
+ ID int `orm:"column(id)"`
+ Boolean bool `orm:"null"`
+ Char string `orm:"null;size(50)"`
+ Text string `orm:"null;type(text)"`
+ Date time.Time `orm:"null;type(date)"`
+ DateTime time.Time `orm:"null;column(datetime)"`
+ Byte byte `orm:"null"`
+ Rune rune `orm:"null"`
+ Int int `orm:"null"`
+ Int8 int8 `orm:"null"`
+ Int16 int16 `orm:"null"`
+ Int32 int32 `orm:"null"`
+ Int64 int64 `orm:"null"`
+ Uint uint `orm:"null"`
+ Uint8 uint8 `orm:"null"`
+ Uint16 uint16 `orm:"null"`
+ Uint32 uint32 `orm:"null"`
+ Uint64 uint64 `orm:"null"`
+ Float32 float32 `orm:"null"`
+ Float64 float64 `orm:"null"`
+ Decimal float64 `orm:"digits(8);decimals(4);null"`
+ NullString sql.NullString `orm:"null"`
+ NullBool sql.NullBool `orm:"null"`
+ NullFloat64 sql.NullFloat64 `orm:"null"`
+ NullInt64 sql.NullInt64 `orm:"null"`
+ BooleanPtr *bool `orm:"null"`
+ CharPtr *string `orm:"null;size(50)"`
+ TextPtr *string `orm:"null;type(text)"`
+ BytePtr *byte `orm:"null"`
+ RunePtr *rune `orm:"null"`
+ IntPtr *int `orm:"null"`
+ Int8Ptr *int8 `orm:"null"`
+ Int16Ptr *int16 `orm:"null"`
+ Int32Ptr *int32 `orm:"null"`
+ Int64Ptr *int64 `orm:"null"`
+ UintPtr *uint `orm:"null"`
+ Uint8Ptr *uint8 `orm:"null"`
+ Uint16Ptr *uint16 `orm:"null"`
+ Uint32Ptr *uint32 `orm:"null"`
+ Uint64Ptr *uint64 `orm:"null"`
+ Float32Ptr *float32 `orm:"null"`
+ Float64Ptr *float64 `orm:"null"`
+ DecimalPtr *float64 `orm:"digits(8);decimals(4);null"`
+}
+
+type String string
+type Boolean bool
+type Byte byte
+type Rune rune
+type Int int
+type Int8 int8
+type Int16 int16
+type Int32 int32
+type Int64 int64
+type Uint uint
+type Uint8 uint8
+type Uint16 uint16
+type Uint32 uint32
+type Uint64 uint64
+type Float32 float64
+type Float64 float64
+
+type DataCustom struct {
+ ID int `orm:"column(id)"`
+ Boolean Boolean
+ Char string `orm:"size(50)"`
+ Text string `orm:"type(text)"`
+ Byte Byte
+ Rune Rune
+ Int Int
+ Int8 Int8
+ Int16 Int16
+ Int32 Int32
+ Int64 Int64
+ Uint Uint
+ Uint8 Uint8
+ Uint16 Uint16
+ Uint32 Uint32
+ Uint64 Uint64
+ Float32 Float32
+ Float64 Float64
+ Decimal Float64 `orm:"digits(8);decimals(4)"`
+}
+
+// only for mysql
+type UserBig struct {
+ ID uint64 `orm:"column(id)"`
+ Name string
+}
+
+type User struct {
+ ID int `orm:"column(id)"`
+ UserName string `orm:"size(30);unique"`
+ Email string `orm:"size(100)"`
+ Password string `orm:"size(100)"`
+ Status int16 `orm:"column(Status)"`
+ IsStaff bool
+ IsActive bool `orm:"default(true)"`
+ Created time.Time `orm:"auto_now_add;type(date)"`
+ Updated time.Time `orm:"auto_now"`
+ Profile *Profile `orm:"null;rel(one);on_delete(set_null)"`
+ Posts []*Post `orm:"reverse(many)" json:"-"`
+ ShouldSkip string `orm:"-"`
+ Nums int
+ Langs SliceStringField `orm:"size(100)"`
+ Extra JSONField `orm:"type(text)"`
+ unexport bool `orm:"-"`
+ unexportBool bool
+}
+
+func (u *User) TableIndex() [][]string {
+ return [][]string{
+ {"Id", "UserName"},
+ {"Id", "Created"},
+ }
+}
+
+func (u *User) TableUnique() [][]string {
+ return [][]string{
+ {"UserName", "Email"},
+ }
+}
+
+func NewUser() *User {
+ obj := new(User)
+ return obj
+}
+
+type Profile struct {
+ ID int `orm:"column(id)"`
+ Age int16
+ Money float64
+ User *User `orm:"reverse(one)" json:"-"`
+ BestPost *Post `orm:"rel(one);null"`
+}
+
+func (u *Profile) TableName() string {
+ return "user_profile"
+}
+
+func NewProfile() *Profile {
+ obj := new(Profile)
+ return obj
+}
+
+type Post struct {
+ ID int `orm:"column(id)"`
+ User *User `orm:"rel(fk)"`
+ Title string `orm:"size(60)"`
+ Content string `orm:"type(text)"`
+ Created time.Time `orm:"auto_now_add"`
+ Updated time.Time `orm:"auto_now"`
+ Tags []*Tag `orm:"rel(m2m);rel_through(github.com/astaxie/beego/orm.PostTags)"`
+}
+
+func (u *Post) TableIndex() [][]string {
+ return [][]string{
+ {"Id", "Created"},
+ }
+}
+
+func NewPost() *Post {
+ obj := new(Post)
+ return obj
+}
+
+type Tag struct {
+ ID int `orm:"column(id)"`
+ Name string `orm:"size(30)"`
+ BestPost *Post `orm:"rel(one);null"`
+ Posts []*Post `orm:"reverse(many)" json:"-"`
+}
+
+func NewTag() *Tag {
+ obj := new(Tag)
+ return obj
+}
+
+type PostTags struct {
+ ID int `orm:"column(id)"`
+ Post *Post `orm:"rel(fk)"`
+ Tag *Tag `orm:"rel(fk)"`
+}
+
+func (m *PostTags) TableName() string {
+ return "prefix_post_tags"
+}
+
+type Comment struct {
+ ID int `orm:"column(id)"`
+ Post *Post `orm:"rel(fk);column(post)"`
+ Content string `orm:"type(text)"`
+ Parent *Comment `orm:"null;rel(fk)"`
+ Created time.Time `orm:"auto_now_add"`
+}
+
+func NewComment() *Comment {
+ obj := new(Comment)
+ return obj
+}
+
+type Group struct {
+ ID int `orm:"column(gid);size(32)"`
+ Name string
+ Permissions []*Permission `orm:"reverse(many)" json:"-"`
+}
+
+type Permission struct {
+ ID int `orm:"column(id)"`
+ Name string
+ Groups []*Group `orm:"rel(m2m);rel_through(github.com/astaxie/beego/orm.GroupPermissions)"`
+}
+
+type GroupPermissions struct {
+ ID int `orm:"column(id)"`
+ Group *Group `orm:"rel(fk)"`
+ Permission *Permission `orm:"rel(fk)"`
+}
+
+type ModelID struct {
+ ID int64
+}
+
+type ModelBase struct {
+ ModelID
+
+ Created time.Time `orm:"auto_now_add;type(datetime)"`
+ Updated time.Time `orm:"auto_now;type(datetime)"`
+}
+
+type InLine struct {
+ // Common Fields
+ ModelBase
+
+ // Other Fields
+ Name string `orm:"unique"`
+ Email string
+}
+
+func NewInLine() *InLine {
+ return new(InLine)
+}
+
+var DBARGS = struct {
+ Driver string
+ Source string
+ Debug string
+}{
+ os.Getenv("ORM_DRIVER"),
+ os.Getenv("ORM_SOURCE"),
+ os.Getenv("ORM_DEBUG"),
+}
+
+var (
+ IsMysql = DBARGS.Driver == "mysql"
+ IsSqlite = DBARGS.Driver == "sqlite3"
+ IsPostgres = DBARGS.Driver == "postgres"
+ IsTidb = DBARGS.Driver == "tidb"
+)
+
+var (
+ dORM Ormer
+ dDbBaser dbBaser
+)
+
+func init() {
+ Debug, _ = StrTo(DBARGS.Debug).Bool()
+
+ if DBARGS.Driver == "" || DBARGS.Source == "" {
+ fmt.Println(`need driver and source!
+
+Default DB Drivers.
+
+ driver: url
+ mysql: https://github.com/go-sql-driver/mysql
+ sqlite3: https://github.com/mattn/go-sqlite3
+postgres: https://github.com/lib/pq
+tidb: https://github.com/pingcap/tidb
+
+usage:
+
+go get -u github.com/astaxie/beego/orm
+go get -u github.com/go-sql-driver/mysql
+go get -u github.com/mattn/go-sqlite3
+go get -u github.com/lib/pq
+go get -u github.com/pingcap/tidb
+
+#### MySQL
+mysql -u root -e 'create database orm_test;'
+export ORM_DRIVER=mysql
+export ORM_SOURCE="root:@/orm_test?charset=utf8"
+go test -v github.com/astaxie/beego/orm
+
+
+#### Sqlite3
+export ORM_DRIVER=sqlite3
+export ORM_SOURCE='file:memory_test?mode=memory'
+go test -v github.com/astaxie/beego/orm
+
+
+#### PostgreSQL
+psql -c 'create database orm_test;' -U postgres
+export ORM_DRIVER=postgres
+export ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
+go test -v github.com/astaxie/beego/orm
+
+#### TiDB
+export ORM_DRIVER=tidb
+export ORM_SOURCE='memory://test/test'
+go test -v github.com/astaxie/beego/orm
+
+`)
+ os.Exit(2)
+ }
+
+ RegisterDataBase("default", DBARGS.Driver, DBARGS.Source, 20)
+
+ alias := getDbAlias("default")
+ if alias.Driver == DRMySQL {
+ alias.Engine = "INNODB"
+ }
+
+}
diff --git a/src/vendor/github.com/astaxie/beego/orm/orm_test.go b/src/vendor/github.com/astaxie/beego/orm/orm_test.go
new file mode 100644
index 0000000000..ec0f0d3abe
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/orm/orm_test.go
@@ -0,0 +1,1954 @@
+// Copyright 2014 beego Author. 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 orm
+
+import (
+ "bytes"
+ "database/sql"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+var _ = os.PathSeparator
+
+var (
+ testDate = formatDate + " -0700"
+ testDateTime = formatDateTime + " -0700"
+)
+
+type argAny []interface{}
+
+// get interface by index from interface slice
+func (a argAny) Get(i int, args ...interface{}) (r interface{}) {
+ if i >= 0 && i < len(a) {
+ r = a[i]
+ }
+ if len(args) > 0 {
+ r = args[0]
+ }
+ return
+}
+
+func ValuesCompare(is bool, a interface{}, args ...interface{}) (ok bool, err error) {
+ if len(args) == 0 {
+ return false, fmt.Errorf("miss args")
+ }
+ b := args[0]
+ arg := argAny(args)
+
+ switch v := a.(type) {
+ case reflect.Kind:
+ ok = reflect.ValueOf(b).Kind() == v
+ case time.Time:
+ if v2, vo := b.(time.Time); vo {
+ if arg.Get(1) != nil {
+ format := ToStr(arg.Get(1))
+ a = v.Format(format)
+ b = v2.Format(format)
+ ok = a == b
+ } else {
+ err = fmt.Errorf("compare datetime miss format")
+ goto wrongArg
+ }
+ }
+ default:
+ ok = ToStr(a) == ToStr(b)
+ }
+ ok = is && ok || !is && !ok
+ if !ok {
+ if is {
+ err = fmt.Errorf("expected: `%v`, get `%v`", b, a)
+ } else {
+ err = fmt.Errorf("expected: `%v`, get `%v`", b, a)
+ }
+ }
+
+wrongArg:
+ if err != nil {
+ return false, err
+ }
+
+ return true, nil
+}
+
+func AssertIs(a interface{}, args ...interface{}) error {
+ if ok, err := ValuesCompare(true, a, args...); ok == false {
+ return err
+ }
+ return nil
+}
+
+func AssertNot(a interface{}, args ...interface{}) error {
+ if ok, err := ValuesCompare(false, a, args...); ok == false {
+ return err
+ }
+ return nil
+}
+
+func getCaller(skip int) string {
+ pc, file, line, _ := runtime.Caller(skip)
+ fun := runtime.FuncForPC(pc)
+ _, fn := filepath.Split(file)
+ data, err := ioutil.ReadFile(file)
+ var codes []string
+ if err == nil {
+ lines := bytes.Split(data, []byte{'\n'})
+ n := 10
+ for i := 0; i < n; i++ {
+ o := line - n
+ if o < 0 {
+ continue
+ }
+ cur := o + i + 1
+ flag := " "
+ if cur == line {
+ flag = ">>"
+ }
+ code := fmt.Sprintf(" %s %5d: %s", flag, cur, strings.Replace(string(lines[o+i]), "\t", " ", -1))
+ if code != "" {
+ codes = append(codes, code)
+ }
+ }
+ }
+ funName := fun.Name()
+ if i := strings.LastIndex(funName, "."); i > -1 {
+ funName = funName[i+1:]
+ }
+ return fmt.Sprintf("%s:%d: \n%s", fn, line, strings.Join(codes, "\n"))
+}
+
+func throwFail(t *testing.T, err error, args ...interface{}) {
+ if err != nil {
+ con := fmt.Sprintf("\t\nError: %s\n%s\n", err.Error(), getCaller(2))
+ if len(args) > 0 {
+ parts := make([]string, 0, len(args))
+ for _, arg := range args {
+ parts = append(parts, fmt.Sprintf("%v", arg))
+ }
+ con += " " + strings.Join(parts, ", ")
+ }
+ t.Error(con)
+ t.Fail()
+ }
+}
+
+func throwFailNow(t *testing.T, err error, args ...interface{}) {
+ if err != nil {
+ con := fmt.Sprintf("\t\nError: %s\n%s\n", err.Error(), getCaller(2))
+ if len(args) > 0 {
+ parts := make([]string, 0, len(args))
+ for _, arg := range args {
+ parts = append(parts, fmt.Sprintf("%v", arg))
+ }
+ con += " " + strings.Join(parts, ", ")
+ }
+ t.Error(con)
+ t.FailNow()
+ }
+}
+
+func TestGetDB(t *testing.T) {
+ if db, err := GetDB(); err != nil {
+ throwFailNow(t, err)
+ } else {
+ err = db.Ping()
+ throwFailNow(t, err)
+ }
+}
+
+func TestSyncDb(t *testing.T) {
+ RegisterModel(new(Data), new(DataNull), new(DataCustom))
+ RegisterModel(new(User))
+ RegisterModel(new(Profile))
+ RegisterModel(new(Post))
+ RegisterModel(new(Tag))
+ RegisterModel(new(Comment))
+ RegisterModel(new(UserBig))
+ RegisterModel(new(PostTags))
+ RegisterModel(new(Group))
+ RegisterModel(new(Permission))
+ RegisterModel(new(GroupPermissions))
+ RegisterModel(new(InLine))
+
+ err := RunSyncdb("default", true, Debug)
+ throwFail(t, err)
+
+ modelCache.clean()
+}
+
+func TestRegisterModels(t *testing.T) {
+ RegisterModel(new(Data), new(DataNull), new(DataCustom))
+ RegisterModel(new(User))
+ RegisterModel(new(Profile))
+ RegisterModel(new(Post))
+ RegisterModel(new(Tag))
+ RegisterModel(new(Comment))
+ RegisterModel(new(UserBig))
+ RegisterModel(new(PostTags))
+ RegisterModel(new(Group))
+ RegisterModel(new(Permission))
+ RegisterModel(new(GroupPermissions))
+ RegisterModel(new(InLine))
+
+ BootStrap()
+
+ dORM = NewOrm()
+ dDbBaser = getDbAlias("default").DbBaser
+}
+
+func TestModelSyntax(t *testing.T) {
+ user := &User{}
+ ind := reflect.ValueOf(user).Elem()
+ fn := getFullName(ind.Type())
+ mi, ok := modelCache.getByFN(fn)
+ throwFail(t, AssertIs(ok, true))
+
+ mi, ok = modelCache.get("user")
+ throwFail(t, AssertIs(ok, true))
+ if ok {
+ throwFail(t, AssertIs(mi.fields.GetByName("ShouldSkip") == nil, true))
+ }
+}
+
+var DataValues = map[string]interface{}{
+ "Boolean": true,
+ "Char": "char",
+ "Text": "text",
+ "Date": time.Now(),
+ "DateTime": time.Now(),
+ "Byte": byte(1<<8 - 1),
+ "Rune": rune(1<<31 - 1),
+ "Int": int(1<<31 - 1),
+ "Int8": int8(1<<7 - 1),
+ "Int16": int16(1<<15 - 1),
+ "Int32": int32(1<<31 - 1),
+ "Int64": int64(1<<63 - 1),
+ "Uint": uint(1<<32 - 1),
+ "Uint8": uint8(1<<8 - 1),
+ "Uint16": uint16(1<<16 - 1),
+ "Uint32": uint32(1<<32 - 1),
+ "Uint64": uint64(1<<63 - 1), // uint64 values with high bit set are not supported
+ "Float32": float32(100.1234),
+ "Float64": float64(100.1234),
+ "Decimal": float64(100.1234),
+}
+
+func TestDataTypes(t *testing.T) {
+ d := Data{}
+ ind := reflect.Indirect(reflect.ValueOf(&d))
+
+ for name, value := range DataValues {
+ e := ind.FieldByName(name)
+ e.Set(reflect.ValueOf(value))
+ }
+
+ id, err := dORM.Insert(&d)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 1))
+
+ d = Data{ID: 1}
+ err = dORM.Read(&d)
+ throwFail(t, err)
+
+ ind = reflect.Indirect(reflect.ValueOf(&d))
+
+ for name, value := range DataValues {
+ e := ind.FieldByName(name)
+ vu := e.Interface()
+ switch name {
+ case "Date":
+ vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDate)
+ value = value.(time.Time).In(DefaultTimeLoc).Format(testDate)
+ case "DateTime":
+ vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+ value = value.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+ }
+ throwFail(t, AssertIs(vu == value, true), value, vu)
+ }
+}
+
+func TestNullDataTypes(t *testing.T) {
+ d := DataNull{}
+
+ if IsPostgres {
+ // can removed when this fixed
+ // https://github.com/lib/pq/pull/125
+ d.DateTime = time.Now()
+ }
+
+ id, err := dORM.Insert(&d)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 1))
+
+ d = DataNull{ID: 1}
+ err = dORM.Read(&d)
+ throwFail(t, err)
+
+ throwFail(t, AssertIs(d.NullBool.Valid, false))
+ throwFail(t, AssertIs(d.NullString.Valid, false))
+ throwFail(t, AssertIs(d.NullInt64.Valid, false))
+ throwFail(t, AssertIs(d.NullFloat64.Valid, false))
+
+ throwFail(t, AssertIs(d.BooleanPtr, nil))
+ throwFail(t, AssertIs(d.CharPtr, nil))
+ throwFail(t, AssertIs(d.TextPtr, nil))
+ throwFail(t, AssertIs(d.BytePtr, nil))
+ throwFail(t, AssertIs(d.RunePtr, nil))
+ throwFail(t, AssertIs(d.IntPtr, nil))
+ throwFail(t, AssertIs(d.Int8Ptr, nil))
+ throwFail(t, AssertIs(d.Int16Ptr, nil))
+ throwFail(t, AssertIs(d.Int32Ptr, nil))
+ throwFail(t, AssertIs(d.Int64Ptr, nil))
+ throwFail(t, AssertIs(d.UintPtr, nil))
+ throwFail(t, AssertIs(d.Uint8Ptr, nil))
+ throwFail(t, AssertIs(d.Uint16Ptr, nil))
+ throwFail(t, AssertIs(d.Uint32Ptr, nil))
+ throwFail(t, AssertIs(d.Uint64Ptr, nil))
+ throwFail(t, AssertIs(d.Float32Ptr, nil))
+ throwFail(t, AssertIs(d.Float64Ptr, nil))
+ throwFail(t, AssertIs(d.DecimalPtr, nil))
+
+ _, err = dORM.Raw(`INSERT INTO data_null (boolean) VALUES (?)`, nil).Exec()
+ throwFail(t, err)
+
+ d = DataNull{ID: 2}
+ err = dORM.Read(&d)
+ throwFail(t, err)
+
+ booleanPtr := true
+ charPtr := string("test")
+ textPtr := string("test")
+ bytePtr := byte('t')
+ runePtr := rune('t')
+ intPtr := int(42)
+ int8Ptr := int8(42)
+ int16Ptr := int16(42)
+ int32Ptr := int32(42)
+ int64Ptr := int64(42)
+ uintPtr := uint(42)
+ uint8Ptr := uint8(42)
+ uint16Ptr := uint16(42)
+ uint32Ptr := uint32(42)
+ uint64Ptr := uint64(42)
+ float32Ptr := float32(42.0)
+ float64Ptr := float64(42.0)
+ decimalPtr := float64(42.0)
+
+ d = DataNull{
+ DateTime: time.Now(),
+ NullString: sql.NullString{String: "test", Valid: true},
+ NullBool: sql.NullBool{Bool: true, Valid: true},
+ NullInt64: sql.NullInt64{Int64: 42, Valid: true},
+ NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
+ BooleanPtr: &booleanPtr,
+ CharPtr: &charPtr,
+ TextPtr: &textPtr,
+ BytePtr: &bytePtr,
+ RunePtr: &runePtr,
+ IntPtr: &intPtr,
+ Int8Ptr: &int8Ptr,
+ Int16Ptr: &int16Ptr,
+ Int32Ptr: &int32Ptr,
+ Int64Ptr: &int64Ptr,
+ UintPtr: &uintPtr,
+ Uint8Ptr: &uint8Ptr,
+ Uint16Ptr: &uint16Ptr,
+ Uint32Ptr: &uint32Ptr,
+ Uint64Ptr: &uint64Ptr,
+ Float32Ptr: &float32Ptr,
+ Float64Ptr: &float64Ptr,
+ DecimalPtr: &decimalPtr,
+ }
+
+ id, err = dORM.Insert(&d)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 3))
+
+ d = DataNull{ID: 3}
+ err = dORM.Read(&d)
+ throwFail(t, err)
+
+ throwFail(t, AssertIs(d.NullBool.Valid, true))
+ throwFail(t, AssertIs(d.NullBool.Bool, true))
+
+ throwFail(t, AssertIs(d.NullString.Valid, true))
+ throwFail(t, AssertIs(d.NullString.String, "test"))
+
+ throwFail(t, AssertIs(d.NullInt64.Valid, true))
+ throwFail(t, AssertIs(d.NullInt64.Int64, 42))
+
+ throwFail(t, AssertIs(d.NullFloat64.Valid, true))
+ throwFail(t, AssertIs(d.NullFloat64.Float64, 42.42))
+
+ throwFail(t, AssertIs(*d.BooleanPtr, booleanPtr))
+ throwFail(t, AssertIs(*d.CharPtr, charPtr))
+ throwFail(t, AssertIs(*d.TextPtr, textPtr))
+ throwFail(t, AssertIs(*d.BytePtr, bytePtr))
+ throwFail(t, AssertIs(*d.RunePtr, runePtr))
+ throwFail(t, AssertIs(*d.IntPtr, intPtr))
+ throwFail(t, AssertIs(*d.Int8Ptr, int8Ptr))
+ throwFail(t, AssertIs(*d.Int16Ptr, int16Ptr))
+ throwFail(t, AssertIs(*d.Int32Ptr, int32Ptr))
+ throwFail(t, AssertIs(*d.Int64Ptr, int64Ptr))
+ throwFail(t, AssertIs(*d.UintPtr, uintPtr))
+ throwFail(t, AssertIs(*d.Uint8Ptr, uint8Ptr))
+ throwFail(t, AssertIs(*d.Uint16Ptr, uint16Ptr))
+ throwFail(t, AssertIs(*d.Uint32Ptr, uint32Ptr))
+ throwFail(t, AssertIs(*d.Uint64Ptr, uint64Ptr))
+ throwFail(t, AssertIs(*d.Float32Ptr, float32Ptr))
+ throwFail(t, AssertIs(*d.Float64Ptr, float64Ptr))
+ throwFail(t, AssertIs(*d.DecimalPtr, decimalPtr))
+}
+
+func TestDataCustomTypes(t *testing.T) {
+ d := DataCustom{}
+ ind := reflect.Indirect(reflect.ValueOf(&d))
+
+ for name, value := range DataValues {
+ e := ind.FieldByName(name)
+ if !e.IsValid() {
+ continue
+ }
+ e.Set(reflect.ValueOf(value).Convert(e.Type()))
+ }
+
+ id, err := dORM.Insert(&d)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 1))
+
+ d = DataCustom{ID: 1}
+ err = dORM.Read(&d)
+ throwFail(t, err)
+
+ ind = reflect.Indirect(reflect.ValueOf(&d))
+
+ for name, value := range DataValues {
+ e := ind.FieldByName(name)
+ if !e.IsValid() {
+ continue
+ }
+ vu := e.Interface()
+ value = reflect.ValueOf(value).Convert(e.Type()).Interface()
+ throwFail(t, AssertIs(vu == value, true), value, vu)
+ }
+}
+
+func TestCRUD(t *testing.T) {
+ profile := NewProfile()
+ profile.Age = 30
+ profile.Money = 1234.12
+ id, err := dORM.Insert(profile)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 1))
+
+ user := NewUser()
+ user.UserName = "slene"
+ user.Email = "vslene@gmail.com"
+ user.Password = "pass"
+ user.Status = 3
+ user.IsStaff = true
+ user.IsActive = true
+
+ id, err = dORM.Insert(user)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 1))
+
+ u := &User{ID: user.ID}
+ err = dORM.Read(u)
+ throwFail(t, err)
+
+ throwFail(t, AssertIs(u.UserName, "slene"))
+ throwFail(t, AssertIs(u.Email, "vslene@gmail.com"))
+ throwFail(t, AssertIs(u.Password, "pass"))
+ throwFail(t, AssertIs(u.Status, 3))
+ throwFail(t, AssertIs(u.IsStaff, true))
+ throwFail(t, AssertIs(u.IsActive, true))
+ throwFail(t, AssertIs(u.Created.In(DefaultTimeLoc), user.Created.In(DefaultTimeLoc), testDate))
+ throwFail(t, AssertIs(u.Updated.In(DefaultTimeLoc), user.Updated.In(DefaultTimeLoc), testDateTime))
+
+ user.UserName = "astaxie"
+ user.Profile = profile
+ num, err := dORM.Update(user)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ u = &User{ID: user.ID}
+ err = dORM.Read(u)
+ throwFailNow(t, err)
+ throwFail(t, AssertIs(u.UserName, "astaxie"))
+ throwFail(t, AssertIs(u.Profile.ID, profile.ID))
+
+ u = &User{UserName: "astaxie", Password: "pass"}
+ err = dORM.Read(u, "UserName")
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(id, 1))
+
+ u.UserName = "QQ"
+ u.Password = "111"
+ num, err = dORM.Update(u, "UserName")
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ u = &User{ID: user.ID}
+ err = dORM.Read(u)
+ throwFailNow(t, err)
+ throwFail(t, AssertIs(u.UserName, "QQ"))
+ throwFail(t, AssertIs(u.Password, "pass"))
+
+ num, err = dORM.Delete(profile)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ u = &User{ID: user.ID}
+ err = dORM.Read(u)
+ throwFail(t, err)
+ throwFail(t, AssertIs(true, u.Profile == nil))
+
+ num, err = dORM.Delete(user)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ u = &User{ID: 100}
+ err = dORM.Read(u)
+ throwFail(t, AssertIs(err, ErrNoRows))
+
+ ub := UserBig{}
+ ub.Name = "name"
+ id, err = dORM.Insert(&ub)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 1))
+
+ ub = UserBig{ID: 1}
+ err = dORM.Read(&ub)
+ throwFail(t, err)
+ throwFail(t, AssertIs(ub.Name, "name"))
+}
+
+func TestInsertTestData(t *testing.T) {
+ var users []*User
+
+ profile := NewProfile()
+ profile.Age = 28
+ profile.Money = 1234.12
+
+ id, err := dORM.Insert(profile)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 2))
+
+ user := NewUser()
+ user.UserName = "slene"
+ user.Email = "vslene@gmail.com"
+ user.Password = "pass"
+ user.Status = 1
+ user.IsStaff = false
+ user.IsActive = true
+ user.Profile = profile
+
+ users = append(users, user)
+
+ id, err = dORM.Insert(user)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 2))
+
+ profile = NewProfile()
+ profile.Age = 30
+ profile.Money = 4321.09
+
+ id, err = dORM.Insert(profile)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 3))
+
+ user = NewUser()
+ user.UserName = "astaxie"
+ user.Email = "astaxie@gmail.com"
+ user.Password = "password"
+ user.Status = 2
+ user.IsStaff = true
+ user.IsActive = false
+ user.Profile = profile
+
+ users = append(users, user)
+
+ id, err = dORM.Insert(user)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 3))
+
+ user = NewUser()
+ user.UserName = "nobody"
+ user.Email = "nobody@gmail.com"
+ user.Password = "nobody"
+ user.Status = 3
+ user.IsStaff = false
+ user.IsActive = false
+
+ users = append(users, user)
+
+ id, err = dORM.Insert(user)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 4))
+
+ tags := []*Tag{
+ {Name: "golang", BestPost: &Post{ID: 2}},
+ {Name: "example"},
+ {Name: "format"},
+ {Name: "c++"},
+ }
+
+ posts := []*Post{
+ {User: users[0], Tags: []*Tag{tags[0]}, Title: "Introduction", Content: `Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go. On the other hand, thinking about the problem from a Go perspective could produce a successful but quite different program. In other words, to write Go well, it's important to understand its properties and idioms. It's also important to know the established conventions for programming in Go, such as naming, formatting, program construction, and so on, so that programs you write will be easy for other Go programmers to understand.
+This document gives tips for writing clear, idiomatic Go code. It augments the language specification, the Tour of Go, and How to Write Go Code, all of which you should read first.`},
+ {User: users[1], Tags: []*Tag{tags[0], tags[1]}, Title: "Examples", Content: `The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the golang.org web site, such as this one (click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and background.`},
+ {User: users[1], Tags: []*Tag{tags[0], tags[2]}, Title: "Formatting", Content: `Formatting issues are the most contentious but the least consequential. People can adapt to different formatting styles but it's better if they don't have to, and less time is devoted to the topic if everyone adheres to the same style. The problem is how to approach this Utopia without a long prescriptive style guide.
+With Go we take an unusual approach and let the machine take care of most formatting issues. The gofmt program (also available as go fmt, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments. If you want to know how to handle some new layout situation, run gofmt; if the answer doesn't seem right, rearrange your program (or file a bug about gofmt), don't work around it.`},
+ {User: users[2], Tags: []*Tag{tags[3]}, Title: "Commentary", Content: `Go provides C-style /* */ block comments and C++-style // line comments. Line comments are the norm; block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code.
+The program—and web server—godoc processes Go source files to extract documentation about the contents of the package. Comments that appear before top-level declarations, with no intervening newlines, are extracted along with the declaration to serve as explanatory text for the item. The nature and style of these comments determines the quality of the documentation godoc produces.`},
+ }
+
+ comments := []*Comment{
+ {Post: posts[0], Content: "a comment"},
+ {Post: posts[1], Content: "yes"},
+ {Post: posts[1]},
+ {Post: posts[1]},
+ {Post: posts[2]},
+ {Post: posts[2]},
+ }
+
+ for _, tag := range tags {
+ id, err := dORM.Insert(tag)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id > 0, true))
+ }
+
+ for _, post := range posts {
+ id, err := dORM.Insert(post)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id > 0, true))
+
+ num := len(post.Tags)
+ if num > 0 {
+ nums, err := dORM.QueryM2M(post, "tags").Add(post.Tags)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(nums, num))
+ }
+ }
+
+ for _, comment := range comments {
+ id, err := dORM.Insert(comment)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id > 0, true))
+ }
+
+ permissions := []*Permission{
+ {Name: "writePosts"},
+ {Name: "readComments"},
+ {Name: "readPosts"},
+ }
+
+ groups := []*Group{
+ {
+ Name: "admins",
+ Permissions: []*Permission{permissions[0], permissions[1], permissions[2]},
+ },
+ {
+ Name: "users",
+ Permissions: []*Permission{permissions[1], permissions[2]},
+ },
+ }
+
+ for _, permission := range permissions {
+ id, err := dORM.Insert(permission)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id > 0, true))
+ }
+
+ for _, group := range groups {
+ _, err := dORM.Insert(group)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id > 0, true))
+
+ num := len(group.Permissions)
+ if num > 0 {
+ nums, err := dORM.QueryM2M(group, "permissions").Add(group.Permissions)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(nums, num))
+ }
+ }
+
+}
+
+func TestCustomField(t *testing.T) {
+ user := User{ID: 2}
+ err := dORM.Read(&user)
+ throwFailNow(t, err)
+
+ user.Langs = append(user.Langs, "zh-CN", "en-US")
+ user.Extra.Name = "beego"
+ user.Extra.Data = "orm"
+ _, err = dORM.Update(&user, "Langs", "Extra")
+ throwFailNow(t, err)
+
+ user = User{ID: 2}
+ err = dORM.Read(&user)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(len(user.Langs), 2))
+ throwFailNow(t, AssertIs(user.Langs[0], "zh-CN"))
+ throwFailNow(t, AssertIs(user.Langs[1], "en-US"))
+
+ throwFailNow(t, AssertIs(user.Extra.Name, "beego"))
+ throwFailNow(t, AssertIs(user.Extra.Data, "orm"))
+}
+
+func TestExpr(t *testing.T) {
+ user := &User{}
+ qs := dORM.QueryTable(user)
+ qs = dORM.QueryTable((*User)(nil))
+ qs = dORM.QueryTable("User")
+ qs = dORM.QueryTable("user")
+ num, err := qs.Filter("UserName", "slene").Filter("user_name", "slene").Filter("profile__Age", 28).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("created", time.Now()).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+
+ // num, err = qs.Filter("created", time.Now().Format(format_Date)).Count()
+ // throwFail(t, err)
+ // throwFail(t, AssertIs(num, 3))
+}
+
+func TestOperators(t *testing.T) {
+ qs := dORM.QueryTable("user")
+ num, err := qs.Filter("user_name", "slene").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("user_name__exact", String("slene")).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("user_name__exact", "slene").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("user_name__iexact", "Slene").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("user_name__contains", "e").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ var shouldNum int
+
+ if IsSqlite || IsTidb {
+ shouldNum = 2
+ } else {
+ shouldNum = 0
+ }
+
+ num, err = qs.Filter("user_name__contains", "E").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, shouldNum))
+
+ num, err = qs.Filter("user_name__icontains", "E").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ num, err = qs.Filter("user_name__icontains", "E").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ num, err = qs.Filter("status__gt", 1).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ num, err = qs.Filter("status__gte", 1).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+
+ num, err = qs.Filter("status__lt", Uint(3)).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ num, err = qs.Filter("status__lte", Int(3)).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+
+ num, err = qs.Filter("user_name__startswith", "s").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ if IsSqlite || IsTidb {
+ shouldNum = 1
+ } else {
+ shouldNum = 0
+ }
+
+ num, err = qs.Filter("user_name__startswith", "S").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, shouldNum))
+
+ num, err = qs.Filter("user_name__istartswith", "S").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("user_name__endswith", "e").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ if IsSqlite || IsTidb {
+ shouldNum = 2
+ } else {
+ shouldNum = 0
+ }
+
+ num, err = qs.Filter("user_name__endswith", "E").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, shouldNum))
+
+ num, err = qs.Filter("user_name__iendswith", "E").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ num, err = qs.Filter("profile__isnull", true).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("status__in", 1, 2).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ num, err = qs.Filter("status__in", []int{1, 2}).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ n1, n2 := 1, 2
+ num, err = qs.Filter("status__in", []*int{&n1}, &n2).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ num, err = qs.Filter("id__between", 2, 3).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ num, err = qs.Filter("id__between", []int{2, 3}).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+}
+
+func TestSetCond(t *testing.T) {
+ cond := NewCondition()
+ cond1 := cond.And("profile__isnull", false).AndNot("status__in", 1).Or("profile__age__gt", 2000)
+
+ qs := dORM.QueryTable("user")
+ num, err := qs.SetCond(cond1).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ cond2 := cond.AndCond(cond1).OrCond(cond.And("user_name", "slene"))
+ num, err = qs.SetCond(cond2).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+}
+
+func TestLimit(t *testing.T) {
+ var posts []*Post
+ qs := dORM.QueryTable("post")
+ num, err := qs.Limit(1).All(&posts)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Limit(-1).All(&posts)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 4))
+
+ num, err = qs.Limit(-1, 2).All(&posts)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ num, err = qs.Limit(0, 2).All(&posts)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+}
+
+func TestOffset(t *testing.T) {
+ var posts []*Post
+ qs := dORM.QueryTable("post")
+ num, err := qs.Limit(1).Offset(2).All(&posts)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Offset(2).All(&posts)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+}
+
+func TestOrderBy(t *testing.T) {
+ qs := dORM.QueryTable("user")
+ num, err := qs.OrderBy("-status").Filter("user_name", "nobody").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.OrderBy("status").Filter("user_name", "slene").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.OrderBy("-profile__age").Filter("user_name", "astaxie").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+}
+
+func TestAll(t *testing.T) {
+ var users []*User
+ qs := dORM.QueryTable("user")
+ num, err := qs.OrderBy("Id").All(&users)
+ throwFail(t, err)
+ throwFailNow(t, AssertIs(num, 3))
+
+ throwFail(t, AssertIs(users[0].UserName, "slene"))
+ throwFail(t, AssertIs(users[1].UserName, "astaxie"))
+ throwFail(t, AssertIs(users[2].UserName, "nobody"))
+
+ var users2 []User
+ qs = dORM.QueryTable("user")
+ num, err = qs.OrderBy("Id").All(&users2)
+ throwFail(t, err)
+ throwFailNow(t, AssertIs(num, 3))
+
+ throwFailNow(t, AssertIs(users2[0].UserName, "slene"))
+ throwFailNow(t, AssertIs(users2[1].UserName, "astaxie"))
+ throwFailNow(t, AssertIs(users2[2].UserName, "nobody"))
+
+ qs = dORM.QueryTable("user")
+ num, err = qs.OrderBy("Id").RelatedSel().All(&users2, "UserName")
+ throwFail(t, err)
+ throwFailNow(t, AssertIs(num, 3))
+ throwFailNow(t, AssertIs(len(users2), 3))
+ throwFailNow(t, AssertIs(users2[0].UserName, "slene"))
+ throwFailNow(t, AssertIs(users2[1].UserName, "astaxie"))
+ throwFailNow(t, AssertIs(users2[2].UserName, "nobody"))
+ throwFailNow(t, AssertIs(users2[0].ID, 0))
+ throwFailNow(t, AssertIs(users2[1].ID, 0))
+ throwFailNow(t, AssertIs(users2[2].ID, 0))
+ throwFailNow(t, AssertIs(users2[0].Profile == nil, false))
+ throwFailNow(t, AssertIs(users2[1].Profile == nil, false))
+ throwFailNow(t, AssertIs(users2[2].Profile == nil, true))
+
+ qs = dORM.QueryTable("user")
+ num, err = qs.Filter("user_name", "nothing").All(&users)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 0))
+
+ var users3 []*User
+ qs = dORM.QueryTable("user")
+ num, err = qs.Filter("user_name", "nothing").All(&users3)
+ throwFailNow(t, AssertIs(users3 == nil, false))
+}
+
+func TestOne(t *testing.T) {
+ var user User
+ qs := dORM.QueryTable("user")
+ err := qs.One(&user)
+ throwFail(t, AssertIs(err, ErrMultiRows))
+
+ user = User{}
+ err = qs.OrderBy("Id").Limit(1).One(&user)
+ throwFailNow(t, err)
+ throwFail(t, AssertIs(user.UserName, "slene"))
+
+ err = qs.Filter("user_name", "nothing").One(&user)
+ throwFail(t, AssertIs(err, ErrNoRows))
+
+}
+
+func TestValues(t *testing.T) {
+ var maps []Params
+ qs := dORM.QueryTable("user")
+
+ num, err := qs.OrderBy("Id").Values(&maps)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+ if num == 3 {
+ throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
+ throwFail(t, AssertIs(maps[2]["Profile"], nil))
+ }
+
+ num, err = qs.OrderBy("Id").Values(&maps, "UserName", "Profile__Age")
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+ if num == 3 {
+ throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
+ throwFail(t, AssertIs(maps[0]["Profile__Age"], 28))
+ throwFail(t, AssertIs(maps[2]["Profile__Age"], nil))
+ }
+
+ num, err = qs.Filter("UserName", "slene").Values(&maps)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+}
+
+func TestValuesList(t *testing.T) {
+ var list []ParamsList
+ qs := dORM.QueryTable("user")
+
+ num, err := qs.OrderBy("Id").ValuesList(&list)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+ if num == 3 {
+ throwFail(t, AssertIs(list[0][1], "slene"))
+ throwFail(t, AssertIs(list[2][9], nil))
+ }
+
+ num, err = qs.OrderBy("Id").ValuesList(&list, "UserName", "Profile__Age")
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+ if num == 3 {
+ throwFail(t, AssertIs(list[0][0], "slene"))
+ throwFail(t, AssertIs(list[0][1], 28))
+ throwFail(t, AssertIs(list[2][1], nil))
+ }
+}
+
+func TestValuesFlat(t *testing.T) {
+ var list ParamsList
+ qs := dORM.QueryTable("user")
+
+ num, err := qs.OrderBy("id").ValuesFlat(&list, "UserName")
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+ if num == 3 {
+ throwFail(t, AssertIs(list[0], "slene"))
+ throwFail(t, AssertIs(list[1], "astaxie"))
+ throwFail(t, AssertIs(list[2], "nobody"))
+ }
+}
+
+func TestRelatedSel(t *testing.T) {
+ if IsTidb {
+ // Skip it. TiDB does not support relation now.
+ return
+ }
+ qs := dORM.QueryTable("user")
+ num, err := qs.Filter("profile__age", 28).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("profile__age__gt", 28).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("profile__user__profile__age__gt", 28).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ var user User
+ err = qs.Filter("user_name", "slene").RelatedSel("profile").One(&user)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+ throwFail(t, AssertNot(user.Profile, nil))
+ if user.Profile != nil {
+ throwFail(t, AssertIs(user.Profile.Age, 28))
+ }
+
+ err = qs.Filter("user_name", "slene").RelatedSel().One(&user)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+ throwFail(t, AssertNot(user.Profile, nil))
+ if user.Profile != nil {
+ throwFail(t, AssertIs(user.Profile.Age, 28))
+ }
+
+ err = qs.Filter("user_name", "nobody").RelatedSel("profile").One(&user)
+ throwFail(t, AssertIs(num, 1))
+ throwFail(t, AssertIs(user.Profile, nil))
+
+ qs = dORM.QueryTable("user_profile")
+ num, err = qs.Filter("user__username", "slene").Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ var posts []*Post
+ qs = dORM.QueryTable("post")
+ num, err = qs.RelatedSel().All(&posts)
+ throwFail(t, err)
+ throwFailNow(t, AssertIs(num, 4))
+
+ throwFailNow(t, AssertIs(posts[0].User.UserName, "slene"))
+ throwFailNow(t, AssertIs(posts[1].User.UserName, "astaxie"))
+ throwFailNow(t, AssertIs(posts[2].User.UserName, "astaxie"))
+ throwFailNow(t, AssertIs(posts[3].User.UserName, "nobody"))
+}
+
+func TestReverseQuery(t *testing.T) {
+ var profile Profile
+ err := dORM.QueryTable("user_profile").Filter("User", 3).One(&profile)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(profile.Age, 30))
+
+ profile = Profile{}
+ err = dORM.QueryTable("user_profile").Filter("User__UserName", "astaxie").One(&profile)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(profile.Age, 30))
+
+ var user User
+ err = dORM.QueryTable("user").Filter("Posts__Title", "Examples").One(&user)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(user.UserName, "astaxie"))
+
+ user = User{}
+ err = dORM.QueryTable("user").Filter("Posts__User__UserName", "astaxie").Limit(1).One(&user)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(user.UserName, "astaxie"))
+
+ user = User{}
+ err = dORM.QueryTable("user").Filter("Posts__User__UserName", "astaxie").RelatedSel().Limit(1).One(&user)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(user.UserName, "astaxie"))
+ throwFailNow(t, AssertIs(user.Profile == nil, false))
+ throwFailNow(t, AssertIs(user.Profile.Age, 30))
+
+ var posts []*Post
+ num, err := dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").All(&posts)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 3))
+ throwFailNow(t, AssertIs(posts[0].Title, "Introduction"))
+
+ posts = []*Post{}
+ num, err = dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").Filter("User__UserName", "slene").All(&posts)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(posts[0].Title, "Introduction"))
+
+ posts = []*Post{}
+ num, err = dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").
+ Filter("User__UserName", "slene").RelatedSel().All(&posts)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(posts[0].User == nil, false))
+ throwFailNow(t, AssertIs(posts[0].User.UserName, "slene"))
+
+ var tags []*Tag
+ num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").All(&tags)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(tags[0].Name, "golang"))
+
+ tags = []*Tag{}
+ num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").
+ Filter("BestPost__User__UserName", "astaxie").All(&tags)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(tags[0].Name, "golang"))
+
+ tags = []*Tag{}
+ num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").
+ Filter("BestPost__User__UserName", "astaxie").RelatedSel().All(&tags)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(tags[0].Name, "golang"))
+ throwFailNow(t, AssertIs(tags[0].BestPost == nil, false))
+ throwFailNow(t, AssertIs(tags[0].BestPost.Title, "Examples"))
+ throwFailNow(t, AssertIs(tags[0].BestPost.User == nil, false))
+ throwFailNow(t, AssertIs(tags[0].BestPost.User.UserName, "astaxie"))
+}
+
+func TestLoadRelated(t *testing.T) {
+ // load reverse foreign key
+ user := User{ID: 3}
+
+ err := dORM.Read(&user)
+ throwFailNow(t, err)
+
+ num, err := dORM.LoadRelated(&user, "Posts")
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 2))
+ throwFailNow(t, AssertIs(len(user.Posts), 2))
+ throwFailNow(t, AssertIs(user.Posts[0].User.ID, 3))
+
+ num, err = dORM.LoadRelated(&user, "Posts", true)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(len(user.Posts), 2))
+ throwFailNow(t, AssertIs(user.Posts[0].User.UserName, "astaxie"))
+
+ num, err = dORM.LoadRelated(&user, "Posts", true, 1)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(len(user.Posts), 1))
+
+ num, err = dORM.LoadRelated(&user, "Posts", true, 0, 0, "-Id")
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(len(user.Posts), 2))
+ throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting"))
+
+ num, err = dORM.LoadRelated(&user, "Posts", true, 1, 1, "Id")
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(len(user.Posts), 1))
+ throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting"))
+
+ // load reverse one to one
+ profile := Profile{ID: 3}
+ profile.BestPost = &Post{ID: 2}
+ num, err = dORM.Update(&profile, "BestPost")
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+
+ err = dORM.Read(&profile)
+ throwFailNow(t, err)
+
+ num, err = dORM.LoadRelated(&profile, "User")
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(profile.User == nil, false))
+ throwFailNow(t, AssertIs(profile.User.UserName, "astaxie"))
+
+ num, err = dORM.LoadRelated(&profile, "User", true)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(profile.User == nil, false))
+ throwFailNow(t, AssertIs(profile.User.UserName, "astaxie"))
+ throwFailNow(t, AssertIs(profile.User.Profile.Age, profile.Age))
+
+ // load rel one to one
+ err = dORM.Read(&user)
+ throwFailNow(t, err)
+
+ num, err = dORM.LoadRelated(&user, "Profile")
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(user.Profile == nil, false))
+ throwFailNow(t, AssertIs(user.Profile.Age, 30))
+
+ num, err = dORM.LoadRelated(&user, "Profile", true)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(user.Profile == nil, false))
+ throwFailNow(t, AssertIs(user.Profile.Age, 30))
+ throwFailNow(t, AssertIs(user.Profile.BestPost == nil, false))
+ throwFailNow(t, AssertIs(user.Profile.BestPost.Title, "Examples"))
+
+ post := Post{ID: 2}
+
+ // load rel foreign key
+ err = dORM.Read(&post)
+ throwFailNow(t, err)
+
+ num, err = dORM.LoadRelated(&post, "User")
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(post.User == nil, false))
+ throwFailNow(t, AssertIs(post.User.UserName, "astaxie"))
+
+ num, err = dORM.LoadRelated(&post, "User", true)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(post.User == nil, false))
+ throwFailNow(t, AssertIs(post.User.UserName, "astaxie"))
+ throwFailNow(t, AssertIs(post.User.Profile == nil, false))
+ throwFailNow(t, AssertIs(post.User.Profile.Age, 30))
+
+ // load rel m2m
+ post = Post{ID: 2}
+
+ err = dORM.Read(&post)
+ throwFailNow(t, err)
+
+ num, err = dORM.LoadRelated(&post, "Tags")
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 2))
+ throwFailNow(t, AssertIs(len(post.Tags), 2))
+ throwFailNow(t, AssertIs(post.Tags[0].Name, "golang"))
+
+ num, err = dORM.LoadRelated(&post, "Tags", true)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 2))
+ throwFailNow(t, AssertIs(len(post.Tags), 2))
+ throwFailNow(t, AssertIs(post.Tags[0].Name, "golang"))
+ throwFailNow(t, AssertIs(post.Tags[0].BestPost == nil, false))
+ throwFailNow(t, AssertIs(post.Tags[0].BestPost.User.UserName, "astaxie"))
+
+ // load reverse m2m
+ tag := Tag{ID: 1}
+
+ err = dORM.Read(&tag)
+ throwFailNow(t, err)
+
+ num, err = dORM.LoadRelated(&tag, "Posts")
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 3))
+ throwFailNow(t, AssertIs(tag.Posts[0].Title, "Introduction"))
+ throwFailNow(t, AssertIs(tag.Posts[0].User.ID, 2))
+ throwFailNow(t, AssertIs(tag.Posts[0].User.Profile == nil, true))
+
+ num, err = dORM.LoadRelated(&tag, "Posts", true)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 3))
+ throwFailNow(t, AssertIs(tag.Posts[0].Title, "Introduction"))
+ throwFailNow(t, AssertIs(tag.Posts[0].User.ID, 2))
+ throwFailNow(t, AssertIs(tag.Posts[0].User.UserName, "slene"))
+}
+
+func TestQueryM2M(t *testing.T) {
+ post := Post{ID: 4}
+ m2m := dORM.QueryM2M(&post, "Tags")
+
+ tag1 := []*Tag{{Name: "TestTag1"}, {Name: "TestTag2"}}
+ tag2 := &Tag{Name: "TestTag3"}
+ tag3 := []interface{}{&Tag{Name: "TestTag4"}}
+
+ tags := []interface{}{tag1[0], tag1[1], tag2, tag3[0]}
+
+ for _, tag := range tags {
+ _, err := dORM.Insert(tag)
+ throwFailNow(t, err)
+ }
+
+ num, err := m2m.Add(tag1)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 2))
+
+ num, err = m2m.Add(tag2)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+
+ num, err = m2m.Add(tag3)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+
+ num, err = m2m.Count()
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 5))
+
+ num, err = m2m.Remove(tag3)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+
+ num, err = m2m.Count()
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 4))
+
+ exist := m2m.Exist(tag2)
+ throwFailNow(t, AssertIs(exist, true))
+
+ num, err = m2m.Remove(tag2)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+
+ exist = m2m.Exist(tag2)
+ throwFailNow(t, AssertIs(exist, false))
+
+ num, err = m2m.Count()
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 3))
+
+ num, err = m2m.Clear()
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 3))
+
+ num, err = m2m.Count()
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 0))
+
+ tag := Tag{Name: "test"}
+ _, err = dORM.Insert(&tag)
+ throwFailNow(t, err)
+
+ m2m = dORM.QueryM2M(&tag, "Posts")
+
+ post1 := []*Post{{Title: "TestPost1"}, {Title: "TestPost2"}}
+ post2 := &Post{Title: "TestPost3"}
+ post3 := []interface{}{&Post{Title: "TestPost4"}}
+
+ posts := []interface{}{post1[0], post1[1], post2, post3[0]}
+
+ for _, post := range posts {
+ p := post.(*Post)
+ p.User = &User{ID: 1}
+ _, err := dORM.Insert(post)
+ throwFailNow(t, err)
+ }
+
+ num, err = m2m.Add(post1)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 2))
+
+ num, err = m2m.Add(post2)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+
+ num, err = m2m.Add(post3)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+
+ num, err = m2m.Count()
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 4))
+
+ num, err = m2m.Remove(post3)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+
+ num, err = m2m.Count()
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 3))
+
+ exist = m2m.Exist(post2)
+ throwFailNow(t, AssertIs(exist, true))
+
+ num, err = m2m.Remove(post2)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+
+ exist = m2m.Exist(post2)
+ throwFailNow(t, AssertIs(exist, false))
+
+ num, err = m2m.Count()
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 2))
+
+ num, err = m2m.Clear()
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 2))
+
+ num, err = m2m.Count()
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 0))
+
+ num, err = dORM.Delete(&tag)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+}
+
+func TestQueryRelate(t *testing.T) {
+ // post := &Post{Id: 2}
+
+ // qs := dORM.QueryRelate(post, "Tags")
+ // num, err := qs.Count()
+ // throwFailNow(t, err)
+ // throwFailNow(t, AssertIs(num, 2))
+
+ // var tags []*Tag
+ // num, err = qs.All(&tags)
+ // throwFailNow(t, err)
+ // throwFailNow(t, AssertIs(num, 2))
+ // throwFailNow(t, AssertIs(tags[0].Name, "golang"))
+
+ // num, err = dORM.QueryTable("Tag").Filter("Posts__Post", 2).Count()
+ // throwFailNow(t, err)
+ // throwFailNow(t, AssertIs(num, 2))
+}
+
+func TestPkManyRelated(t *testing.T) {
+ permission := &Permission{Name: "readPosts"}
+ err := dORM.Read(permission, "Name")
+ throwFailNow(t, err)
+
+ var groups []*Group
+ qs := dORM.QueryTable("Group")
+ num, err := qs.Filter("Permissions__Permission", permission.ID).All(&groups)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 2))
+}
+
+func TestPrepareInsert(t *testing.T) {
+ qs := dORM.QueryTable("user")
+ i, err := qs.PrepareInsert()
+ throwFailNow(t, err)
+
+ var user User
+ user.UserName = "testing1"
+ num, err := i.Insert(&user)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num > 0, true))
+
+ user.UserName = "testing2"
+ num, err = i.Insert(&user)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num > 0, true))
+
+ num, err = qs.Filter("user_name__in", "testing1", "testing2").Delete()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 2))
+
+ err = i.Close()
+ throwFail(t, err)
+ err = i.Close()
+ throwFail(t, AssertIs(err, ErrStmtClosed))
+}
+
+func TestRawExec(t *testing.T) {
+ Q := dDbBaser.TableQuote()
+
+ query := fmt.Sprintf("UPDATE %suser%s SET %suser_name%s = ? WHERE %suser_name%s = ?", Q, Q, Q, Q, Q, Q)
+ res, err := dORM.Raw(query, "testing", "slene").Exec()
+ throwFail(t, err)
+ num, err := res.RowsAffected()
+ throwFail(t, AssertIs(num, 1), err)
+
+ res, err = dORM.Raw(query, "slene", "testing").Exec()
+ throwFail(t, err)
+ num, err = res.RowsAffected()
+ throwFail(t, AssertIs(num, 1), err)
+}
+
+func TestRawQueryRow(t *testing.T) {
+ var (
+ Boolean bool
+ Char string
+ Text string
+ Date time.Time
+ DateTime time.Time
+ Byte byte
+ Rune rune
+ Int int
+ Int8 int
+ Int16 int16
+ Int32 int32
+ Int64 int64
+ Uint uint
+ Uint8 uint8
+ Uint16 uint16
+ Uint32 uint32
+ Uint64 uint64
+ Float32 float32
+ Float64 float64
+ Decimal float64
+ )
+
+ dataValues := make(map[string]interface{}, len(DataValues))
+
+ for k, v := range DataValues {
+ dataValues[strings.ToLower(k)] = v
+ }
+
+ Q := dDbBaser.TableQuote()
+
+ cols := []string{
+ "id", "boolean", "char", "text", "date", "datetime", "byte", "rune", "int", "int8", "int16", "int32",
+ "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "decimal",
+ }
+ sep := fmt.Sprintf("%s, %s", Q, Q)
+ query := fmt.Sprintf("SELECT %s%s%s FROM data WHERE id = ?", Q, strings.Join(cols, sep), Q)
+ var id int
+ values := []interface{}{
+ &id, &Boolean, &Char, &Text, &Date, &DateTime, &Byte, &Rune, &Int, &Int8, &Int16, &Int32,
+ &Int64, &Uint, &Uint8, &Uint16, &Uint32, &Uint64, &Float32, &Float64, &Decimal,
+ }
+ err := dORM.Raw(query, 1).QueryRow(values...)
+ throwFailNow(t, err)
+ for i, col := range cols {
+ vu := values[i]
+ v := reflect.ValueOf(vu).Elem().Interface()
+ switch col {
+ case "id":
+ throwFail(t, AssertIs(id, 1))
+ case "date":
+ v = v.(time.Time).In(DefaultTimeLoc)
+ value := dataValues[col].(time.Time).In(DefaultTimeLoc)
+ throwFail(t, AssertIs(v, value, testDate))
+ case "datetime":
+ v = v.(time.Time).In(DefaultTimeLoc)
+ value := dataValues[col].(time.Time).In(DefaultTimeLoc)
+ throwFail(t, AssertIs(v, value, testDateTime))
+ default:
+ throwFail(t, AssertIs(v, dataValues[col]))
+ }
+ }
+
+ var (
+ uid int
+ status *int
+ pid *int
+ )
+
+ cols = []string{
+ "id", "Status", "profile_id",
+ }
+ query = fmt.Sprintf("SELECT %s%s%s FROM %suser%s WHERE id = ?", Q, strings.Join(cols, sep), Q, Q, Q)
+ err = dORM.Raw(query, 4).QueryRow(&uid, &status, &pid)
+ throwFail(t, err)
+ throwFail(t, AssertIs(uid, 4))
+ throwFail(t, AssertIs(*status, 3))
+ throwFail(t, AssertIs(pid, nil))
+}
+
+func TestQueryRows(t *testing.T) {
+ Q := dDbBaser.TableQuote()
+
+ var datas []*Data
+
+ query := fmt.Sprintf("SELECT * FROM %sdata%s", Q, Q)
+ num, err := dORM.Raw(query).QueryRows(&datas)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(len(datas), 1))
+
+ ind := reflect.Indirect(reflect.ValueOf(datas[0]))
+
+ for name, value := range DataValues {
+ e := ind.FieldByName(name)
+ vu := e.Interface()
+ switch name {
+ case "Date":
+ vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDate)
+ value = value.(time.Time).In(DefaultTimeLoc).Format(testDate)
+ case "DateTime":
+ vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+ value = value.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+ }
+ throwFail(t, AssertIs(vu == value, true), value, vu)
+ }
+
+ var datas2 []Data
+
+ query = fmt.Sprintf("SELECT * FROM %sdata%s", Q, Q)
+ num, err = dORM.Raw(query).QueryRows(&datas2)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 1))
+ throwFailNow(t, AssertIs(len(datas2), 1))
+
+ ind = reflect.Indirect(reflect.ValueOf(datas2[0]))
+
+ for name, value := range DataValues {
+ e := ind.FieldByName(name)
+ vu := e.Interface()
+ switch name {
+ case "Date":
+ vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDate)
+ value = value.(time.Time).In(DefaultTimeLoc).Format(testDate)
+ case "DateTime":
+ vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+ value = value.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+ }
+ throwFail(t, AssertIs(vu == value, true), value, vu)
+ }
+
+ var ids []int
+ var usernames []string
+ query = fmt.Sprintf("SELECT %sid%s, %suser_name%s FROM %suser%s ORDER BY %sid%s ASC", Q, Q, Q, Q, Q, Q, Q, Q)
+ num, err = dORM.Raw(query).QueryRows(&ids, &usernames)
+ throwFailNow(t, err)
+ throwFailNow(t, AssertIs(num, 3))
+ throwFailNow(t, AssertIs(len(ids), 3))
+ throwFailNow(t, AssertIs(ids[0], 2))
+ throwFailNow(t, AssertIs(usernames[0], "slene"))
+ throwFailNow(t, AssertIs(ids[1], 3))
+ throwFailNow(t, AssertIs(usernames[1], "astaxie"))
+ throwFailNow(t, AssertIs(ids[2], 4))
+ throwFailNow(t, AssertIs(usernames[2], "nobody"))
+}
+
+func TestRawValues(t *testing.T) {
+ Q := dDbBaser.TableQuote()
+
+ var maps []Params
+ query := fmt.Sprintf("SELECT %suser_name%s FROM %suser%s WHERE %sStatus%s = ?", Q, Q, Q, Q, Q, Q)
+ num, err := dORM.Raw(query, 1).Values(&maps)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+ if num == 1 {
+ throwFail(t, AssertIs(maps[0]["user_name"], "slene"))
+ }
+
+ var lists []ParamsList
+ num, err = dORM.Raw(query, 1).ValuesList(&lists)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+ if num == 1 {
+ throwFail(t, AssertIs(lists[0][0], "slene"))
+ }
+
+ query = fmt.Sprintf("SELECT %sprofile_id%s FROM %suser%s ORDER BY %sid%s ASC", Q, Q, Q, Q, Q, Q)
+ var list ParamsList
+ num, err = dORM.Raw(query).ValuesFlat(&list)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+ if num == 3 {
+ throwFail(t, AssertIs(list[0], "2"))
+ throwFail(t, AssertIs(list[1], "3"))
+ throwFail(t, AssertIs(list[2], nil))
+ }
+}
+
+func TestRawPrepare(t *testing.T) {
+ switch {
+ case IsMysql || IsSqlite:
+
+ pre, err := dORM.Raw("INSERT INTO tag (name) VALUES (?)").Prepare()
+ throwFail(t, err)
+ if pre != nil {
+ r, err := pre.Exec("name1")
+ throwFail(t, err)
+
+ tid, err := r.LastInsertId()
+ throwFail(t, err)
+ throwFail(t, AssertIs(tid > 0, true))
+
+ r, err = pre.Exec("name2")
+ throwFail(t, err)
+
+ id, err := r.LastInsertId()
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, tid+1))
+
+ r, err = pre.Exec("name3")
+ throwFail(t, err)
+
+ id, err = r.LastInsertId()
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, tid+2))
+
+ err = pre.Close()
+ throwFail(t, err)
+
+ res, err := dORM.Raw("DELETE FROM tag WHERE name IN (?, ?, ?)", []string{"name1", "name2", "name3"}).Exec()
+ throwFail(t, err)
+
+ num, err := res.RowsAffected()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+ }
+
+ case IsPostgres:
+
+ pre, err := dORM.Raw(`INSERT INTO "tag" ("name") VALUES (?) RETURNING "id"`).Prepare()
+ throwFail(t, err)
+ if pre != nil {
+ _, err := pre.Exec("name1")
+ throwFail(t, err)
+
+ _, err = pre.Exec("name2")
+ throwFail(t, err)
+
+ _, err = pre.Exec("name3")
+ throwFail(t, err)
+
+ err = pre.Close()
+ throwFail(t, err)
+
+ res, err := dORM.Raw(`DELETE FROM "tag" WHERE "name" IN (?, ?, ?)`, []string{"name1", "name2", "name3"}).Exec()
+ throwFail(t, err)
+
+ if err == nil {
+ num, err := res.RowsAffected()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+ }
+ }
+ }
+}
+
+func TestUpdate(t *testing.T) {
+ qs := dORM.QueryTable("user")
+ num, err := qs.Filter("user_name", "slene").Filter("is_staff", false).Update(Params{
+ "is_staff": true,
+ "is_active": true,
+ })
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ // with join
+ num, err = qs.Filter("user_name", "slene").Filter("profile__age", 28).Filter("is_staff", true).Update(Params{
+ "is_staff": false,
+ })
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("user_name", "slene").Update(Params{
+ "Nums": ColValue(ColAdd, 100),
+ })
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("user_name", "slene").Update(Params{
+ "Nums": ColValue(ColMinus, 50),
+ })
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("user_name", "slene").Update(Params{
+ "Nums": ColValue(ColMultiply, 3),
+ })
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ num, err = qs.Filter("user_name", "slene").Update(Params{
+ "Nums": ColValue(ColExcept, 5),
+ })
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ user := User{UserName: "slene"}
+ err = dORM.Read(&user, "UserName")
+ throwFail(t, err)
+ throwFail(t, AssertIs(user.Nums, 30))
+}
+
+func TestDelete(t *testing.T) {
+ qs := dORM.QueryTable("user_profile")
+ num, err := qs.Filter("user__user_name", "slene").Delete()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ qs = dORM.QueryTable("user")
+ num, err = qs.Filter("user_name", "slene").Filter("profile__isnull", true).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ qs = dORM.QueryTable("comment")
+ num, err = qs.Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 6))
+
+ qs = dORM.QueryTable("post")
+ num, err = qs.Filter("Id", 3).Delete()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ qs = dORM.QueryTable("comment")
+ num, err = qs.Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 4))
+
+ qs = dORM.QueryTable("comment")
+ num, err = qs.Filter("Post__User", 3).Delete()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 3))
+
+ qs = dORM.QueryTable("comment")
+ num, err = qs.Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+}
+
+func TestTransaction(t *testing.T) {
+ // this test worked when database support transaction
+
+ o := NewOrm()
+ err := o.Begin()
+ throwFail(t, err)
+
+ var names = []string{"1", "2", "3"}
+
+ var tag Tag
+ tag.Name = names[0]
+ id, err := o.Insert(&tag)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id > 0, true))
+
+ num, err := o.QueryTable("tag").Filter("name", "golang").Update(Params{"name": names[1]})
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+ switch {
+ case IsMysql || IsSqlite:
+ res, err := o.Raw("INSERT INTO tag (name) VALUES (?)", names[2]).Exec()
+ throwFail(t, err)
+ if err == nil {
+ id, err = res.LastInsertId()
+ throwFail(t, err)
+ throwFail(t, AssertIs(id > 0, true))
+ }
+ }
+
+ err = o.Rollback()
+ throwFail(t, err)
+
+ num, err = o.QueryTable("tag").Filter("name__in", names).Count()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 0))
+
+ err = o.Begin()
+ throwFail(t, err)
+
+ tag.Name = "commit"
+ id, err = o.Insert(&tag)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id > 0, true))
+
+ o.Commit()
+ throwFail(t, err)
+
+ num, err = o.QueryTable("tag").Filter("name", "commit").Delete()
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, 1))
+
+}
+
+func TestReadOrCreate(t *testing.T) {
+ u := &User{
+ UserName: "Kyle",
+ Email: "kylemcc@gmail.com",
+ Password: "other_pass",
+ Status: 7,
+ IsStaff: false,
+ IsActive: true,
+ }
+
+ created, pk, err := dORM.ReadOrCreate(u, "UserName")
+ throwFail(t, err)
+ throwFail(t, AssertIs(created, true))
+ throwFail(t, AssertIs(u.UserName, "Kyle"))
+ throwFail(t, AssertIs(u.Email, "kylemcc@gmail.com"))
+ throwFail(t, AssertIs(u.Password, "other_pass"))
+ throwFail(t, AssertIs(u.Status, 7))
+ throwFail(t, AssertIs(u.IsStaff, false))
+ throwFail(t, AssertIs(u.IsActive, true))
+ throwFail(t, AssertIs(u.Created.In(DefaultTimeLoc), u.Created.In(DefaultTimeLoc), testDate))
+ throwFail(t, AssertIs(u.Updated.In(DefaultTimeLoc), u.Updated.In(DefaultTimeLoc), testDateTime))
+
+ nu := &User{UserName: u.UserName, Email: "someotheremail@gmail.com"}
+ created, pk, err = dORM.ReadOrCreate(nu, "UserName")
+ throwFail(t, err)
+ throwFail(t, AssertIs(created, false))
+ throwFail(t, AssertIs(nu.ID, u.ID))
+ throwFail(t, AssertIs(pk, u.ID))
+ throwFail(t, AssertIs(nu.UserName, u.UserName))
+ throwFail(t, AssertIs(nu.Email, u.Email)) // should contain the value in the table, not the one specified above
+ throwFail(t, AssertIs(nu.Password, u.Password))
+ throwFail(t, AssertIs(nu.Status, u.Status))
+ throwFail(t, AssertIs(nu.IsStaff, u.IsStaff))
+ throwFail(t, AssertIs(nu.IsActive, u.IsActive))
+
+ dORM.Delete(u)
+}
+
+func TestInLine(t *testing.T) {
+ name := "inline"
+ email := "hello@go.com"
+ inline := NewInLine()
+ inline.Name = name
+ inline.Email = email
+
+ id, err := dORM.Insert(inline)
+ throwFail(t, err)
+ throwFail(t, AssertIs(id, 1))
+
+ il := NewInLine()
+ il.ID = 1
+ err = dORM.Read(il)
+ throwFail(t, err)
+
+ throwFail(t, AssertIs(il.Name, name))
+ throwFail(t, AssertIs(il.Email, email))
+ throwFail(t, AssertIs(il.Created.In(DefaultTimeLoc), inline.Created.In(DefaultTimeLoc), testDate))
+ throwFail(t, AssertIs(il.Updated.In(DefaultTimeLoc), inline.Updated.In(DefaultTimeLoc), testDateTime))
+}
diff --git a/src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go b/src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go
new file mode 100644
index 0000000000..8af08088d4
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go
@@ -0,0 +1,180 @@
+// Copyright 2014 beego Author. 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 apiauth provides handlers to enable apiauth support.
+//
+// Simple Usage:
+// import(
+// "github.com/astaxie/beego"
+// "github.com/astaxie/beego/plugins/apiauth"
+// )
+//
+// func main(){
+// // apiauth every request
+// beego.InsertFilter("*", beego.BeforeRouter,apiauth.APIBaiscAuth("appid","appkey"))
+// beego.Run()
+// }
+//
+// Advanced Usage:
+//
+// func getAppSecret(appid string) string {
+// // get appsecret by appid
+// // maybe store in configure, maybe in database
+// }
+//
+// beego.InsertFilter("*", beego.BeforeRouter,apiauth.APISecretAuth(getAppSecret, 360))
+//
+// Infomation:
+//
+// In the request user should include these params in the query
+//
+// 1. appid
+//
+// appid is assigned to the application
+//
+// 2. signature
+//
+// get the signature use apiauth.Signature()
+//
+// when you send to server remember use url.QueryEscape()
+//
+// 3. timestamp:
+//
+// send the request time, the format is yyyy-mm-dd HH:ii:ss
+//
+package apiauth
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
+ "fmt"
+ "net/url"
+ "sort"
+ "time"
+
+ "github.com/astaxie/beego"
+ "github.com/astaxie/beego/context"
+)
+
+// AppIDToAppSecret is used to get appsecret throw appid
+type AppIDToAppSecret func(string) string
+
+// APIBaiscAuth use the basic appid/appkey as the AppIdToAppSecret
+func APIBaiscAuth(appid, appkey string) beego.FilterFunc {
+ ft := func(aid string) string {
+ if aid == appid {
+ return appkey
+ }
+ return ""
+ }
+ return APISecretAuth(ft, 300)
+}
+
+// APISecretAuth use AppIdToAppSecret verify and
+func APISecretAuth(f AppIDToAppSecret, timeout int) beego.FilterFunc {
+ return func(ctx *context.Context) {
+ if ctx.Input.Query("appid") == "" {
+ ctx.ResponseWriter.WriteHeader(403)
+ ctx.WriteString("miss query param: appid")
+ return
+ }
+ appsecret := f(ctx.Input.Query("appid"))
+ if appsecret == "" {
+ ctx.ResponseWriter.WriteHeader(403)
+ ctx.WriteString("not exist this appid")
+ return
+ }
+ if ctx.Input.Query("signature") == "" {
+ ctx.ResponseWriter.WriteHeader(403)
+ ctx.WriteString("miss query param: signature")
+ return
+ }
+ if ctx.Input.Query("timestamp") == "" {
+ ctx.ResponseWriter.WriteHeader(403)
+ ctx.WriteString("miss query param: timestamp")
+ return
+ }
+ u, err := time.Parse("2006-01-02 15:04:05", ctx.Input.Query("timestamp"))
+ if err != nil {
+ ctx.ResponseWriter.WriteHeader(403)
+ ctx.WriteString("timestamp format is error, should 2006-01-02 15:04:05")
+ return
+ }
+ t := time.Now()
+ if t.Sub(u).Seconds() > float64(timeout) {
+ ctx.ResponseWriter.WriteHeader(403)
+ ctx.WriteString("timeout! the request time is long ago, please try again")
+ return
+ }
+ if ctx.Input.Query("signature") !=
+ Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.URI()) {
+ ctx.ResponseWriter.WriteHeader(403)
+ ctx.WriteString("auth failed")
+ }
+ }
+}
+
+// Signature used to generate signature with the appsecret/method/params/RequestURI
+func Signature(appsecret, method string, params url.Values, RequestURI string) (result string) {
+ var query string
+ pa := make(map[string]string)
+ for k, v := range params {
+ pa[k] = v[0]
+ }
+ vs := mapSorter(pa)
+ vs.Sort()
+ for i := 0; i < vs.Len(); i++ {
+ if vs.Keys[i] == "signature" {
+ continue
+ }
+ if vs.Keys[i] != "" && vs.Vals[i] != "" {
+ query = fmt.Sprintf("%v%v%v", query, vs.Keys[i], vs.Vals[i])
+ }
+ }
+ stringToSign := fmt.Sprintf("%v\n%v\n%v\n", method, query, RequestURI)
+
+ sha256 := sha256.New
+ hash := hmac.New(sha256, []byte(appsecret))
+ hash.Write([]byte(stringToSign))
+ return base64.StdEncoding.EncodeToString(hash.Sum(nil))
+}
+
+type valSorter struct {
+ Keys []string
+ Vals []string
+}
+
+func mapSorter(m map[string]string) *valSorter {
+ vs := &valSorter{
+ Keys: make([]string, 0, len(m)),
+ Vals: make([]string, 0, len(m)),
+ }
+ for k, v := range m {
+ vs.Keys = append(vs.Keys, k)
+ vs.Vals = append(vs.Vals, v)
+ }
+ return vs
+}
+
+func (vs *valSorter) Sort() {
+ sort.Sort(vs)
+}
+
+func (vs *valSorter) Len() int { return len(vs.Keys) }
+func (vs *valSorter) Less(i, j int) bool { return vs.Keys[i] < vs.Keys[j] }
+func (vs *valSorter) Swap(i, j int) {
+ vs.Vals[i], vs.Vals[j] = vs.Vals[j], vs.Vals[i]
+ vs.Keys[i], vs.Keys[j] = vs.Keys[j], vs.Keys[i]
+}
diff --git a/src/vendor/github.com/astaxie/beego/plugins/auth/basic.go b/src/vendor/github.com/astaxie/beego/plugins/auth/basic.go
new file mode 100644
index 0000000000..c478044abb
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/plugins/auth/basic.go
@@ -0,0 +1,107 @@
+// Copyright 2014 beego Author. 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 auth provides handlers to enable basic auth support.
+// Simple Usage:
+// import(
+// "github.com/astaxie/beego"
+// "github.com/astaxie/beego/plugins/auth"
+// )
+//
+// func main(){
+// // authenticate every request
+// beego.InsertFilter("*", beego.BeforeRouter,auth.Basic("username","secretpassword"))
+// beego.Run()
+// }
+//
+//
+// Advanced Usage:
+//
+// func SecretAuth(username, password string) bool {
+// return username == "astaxie" && password == "helloBeego"
+// }
+// authPlugin := auth.NewBasicAuthenticator(SecretAuth, "Authorization Required")
+// beego.InsertFilter("*", beego.BeforeRouter,authPlugin)
+package auth
+
+import (
+ "encoding/base64"
+ "net/http"
+ "strings"
+
+ "github.com/astaxie/beego"
+ "github.com/astaxie/beego/context"
+)
+
+var defaultRealm = "Authorization Required"
+
+// Basic is the http basic auth
+func Basic(username string, password string) beego.FilterFunc {
+ secrets := func(user, pass string) bool {
+ return user == username && pass == password
+ }
+ return NewBasicAuthenticator(secrets, defaultRealm)
+}
+
+// NewBasicAuthenticator return the BasicAuth
+func NewBasicAuthenticator(secrets SecretProvider, Realm string) beego.FilterFunc {
+ return func(ctx *context.Context) {
+ a := &BasicAuth{Secrets: secrets, Realm: Realm}
+ if username := a.CheckAuth(ctx.Request); username == "" {
+ a.RequireAuth(ctx.ResponseWriter, ctx.Request)
+ }
+ }
+}
+
+// SecretProvider is the SecretProvider function
+type SecretProvider func(user, pass string) bool
+
+// BasicAuth store the SecretProvider and Realm
+type BasicAuth struct {
+ Secrets SecretProvider
+ Realm string
+}
+
+// CheckAuth Checks the username/password combination from the request. Returns
+// either an empty string (authentication failed) or the name of the
+// authenticated user.
+// Supports MD5 and SHA1 password entries
+func (a *BasicAuth) CheckAuth(r *http.Request) string {
+ s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
+ if len(s) != 2 || s[0] != "Basic" {
+ return ""
+ }
+
+ b, err := base64.StdEncoding.DecodeString(s[1])
+ if err != nil {
+ return ""
+ }
+ pair := strings.SplitN(string(b), ":", 2)
+ if len(pair) != 2 {
+ return ""
+ }
+
+ if a.Secrets(pair[0], pair[1]) {
+ return pair[0]
+ }
+ return ""
+}
+
+// RequireAuth http.Handler for BasicAuth which initiates the authentication process
+// (or requires reauthentication).
+func (a *BasicAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("WWW-Authenticate", `Basic realm="`+a.Realm+`"`)
+ w.WriteHeader(401)
+ w.Write([]byte("401 Unauthorized\n"))
+}
diff --git a/src/vendor/github.com/astaxie/beego/plugins/cors/cors.go b/src/vendor/github.com/astaxie/beego/plugins/cors/cors.go
new file mode 100644
index 0000000000..45c327ab46
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/plugins/cors/cors.go
@@ -0,0 +1,228 @@
+// Copyright 2014 beego Author. 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 cors provides handlers to enable CORS support.
+// Usage
+// import (
+// "github.com/astaxie/beego"
+// "github.com/astaxie/beego/plugins/cors"
+// )
+//
+// func main() {
+// // CORS for https://foo.* origins, allowing:
+// // - PUT and PATCH methods
+// // - Origin header
+// // - Credentials share
+// beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
+// AllowOrigins: []string{"https://*.foo.com"},
+// AllowMethods: []string{"PUT", "PATCH"},
+// AllowHeaders: []string{"Origin"},
+// ExposeHeaders: []string{"Content-Length"},
+// AllowCredentials: true,
+// }))
+// beego.Run()
+// }
+package cors
+
+import (
+ "net/http"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/astaxie/beego"
+ "github.com/astaxie/beego/context"
+)
+
+const (
+ headerAllowOrigin = "Access-Control-Allow-Origin"
+ headerAllowCredentials = "Access-Control-Allow-Credentials"
+ headerAllowHeaders = "Access-Control-Allow-Headers"
+ headerAllowMethods = "Access-Control-Allow-Methods"
+ headerExposeHeaders = "Access-Control-Expose-Headers"
+ headerMaxAge = "Access-Control-Max-Age"
+
+ headerOrigin = "Origin"
+ headerRequestMethod = "Access-Control-Request-Method"
+ headerRequestHeaders = "Access-Control-Request-Headers"
+)
+
+var (
+ defaultAllowHeaders = []string{"Origin", "Accept", "Content-Type", "Authorization"}
+ // Regex patterns are generated from AllowOrigins. These are used and generated internally.
+ allowOriginPatterns = []string{}
+)
+
+// Options represents Access Control options.
+type Options struct {
+ // If set, all origins are allowed.
+ AllowAllOrigins bool
+ // A list of allowed origins. Wild cards and FQDNs are supported.
+ AllowOrigins []string
+ // If set, allows to share auth credentials such as cookies.
+ AllowCredentials bool
+ // A list of allowed HTTP methods.
+ AllowMethods []string
+ // A list of allowed HTTP headers.
+ AllowHeaders []string
+ // A list of exposed HTTP headers.
+ ExposeHeaders []string
+ // Max age of the CORS headers.
+ MaxAge time.Duration
+}
+
+// Header converts options into CORS headers.
+func (o *Options) Header(origin string) (headers map[string]string) {
+ headers = make(map[string]string)
+ // if origin is not allowed, don't extend the headers
+ // with CORS headers.
+ if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
+ return
+ }
+
+ // add allow origin
+ if o.AllowAllOrigins {
+ headers[headerAllowOrigin] = "*"
+ } else {
+ headers[headerAllowOrigin] = origin
+ }
+
+ // add allow credentials
+ headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
+
+ // add allow methods
+ if len(o.AllowMethods) > 0 {
+ headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
+ }
+
+ // add allow headers
+ if len(o.AllowHeaders) > 0 {
+ headers[headerAllowHeaders] = strings.Join(o.AllowHeaders, ",")
+ }
+
+ // add exposed header
+ if len(o.ExposeHeaders) > 0 {
+ headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
+ }
+ // add a max age header
+ if o.MaxAge > time.Duration(0) {
+ headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
+ }
+ return
+}
+
+// PreflightHeader converts options into CORS headers for a preflight response.
+func (o *Options) PreflightHeader(origin, rMethod, rHeaders string) (headers map[string]string) {
+ headers = make(map[string]string)
+ if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
+ return
+ }
+ // verify if requested method is allowed
+ for _, method := range o.AllowMethods {
+ if method == rMethod {
+ headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
+ break
+ }
+ }
+
+ // verify if requested headers are allowed
+ var allowed []string
+ for _, rHeader := range strings.Split(rHeaders, ",") {
+ rHeader = strings.TrimSpace(rHeader)
+ lookupLoop:
+ for _, allowedHeader := range o.AllowHeaders {
+ if strings.ToLower(rHeader) == strings.ToLower(allowedHeader) {
+ allowed = append(allowed, rHeader)
+ break lookupLoop
+ }
+ }
+ }
+
+ headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
+ // add allow origin
+ if o.AllowAllOrigins {
+ headers[headerAllowOrigin] = "*"
+ } else {
+ headers[headerAllowOrigin] = origin
+ }
+
+ // add allowed headers
+ if len(allowed) > 0 {
+ headers[headerAllowHeaders] = strings.Join(allowed, ",")
+ }
+
+ // add exposed headers
+ if len(o.ExposeHeaders) > 0 {
+ headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
+ }
+ // add a max age header
+ if o.MaxAge > time.Duration(0) {
+ headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
+ }
+ return
+}
+
+// IsOriginAllowed looks up if the origin matches one of the patterns
+// generated from Options.AllowOrigins patterns.
+func (o *Options) IsOriginAllowed(origin string) (allowed bool) {
+ for _, pattern := range allowOriginPatterns {
+ allowed, _ = regexp.MatchString(pattern, origin)
+ if allowed {
+ return
+ }
+ }
+ return
+}
+
+// Allow enables CORS for requests those match the provided options.
+func Allow(opts *Options) beego.FilterFunc {
+ // Allow default headers if nothing is specified.
+ if len(opts.AllowHeaders) == 0 {
+ opts.AllowHeaders = defaultAllowHeaders
+ }
+
+ for _, origin := range opts.AllowOrigins {
+ pattern := regexp.QuoteMeta(origin)
+ pattern = strings.Replace(pattern, "\\*", ".*", -1)
+ pattern = strings.Replace(pattern, "\\?", ".", -1)
+ allowOriginPatterns = append(allowOriginPatterns, "^"+pattern+"$")
+ }
+
+ return func(ctx *context.Context) {
+ var (
+ origin = ctx.Input.Header(headerOrigin)
+ requestedMethod = ctx.Input.Header(headerRequestMethod)
+ requestedHeaders = ctx.Input.Header(headerRequestHeaders)
+ // additional headers to be added
+ // to the response.
+ headers map[string]string
+ )
+
+ if ctx.Input.Method() == "OPTIONS" &&
+ (requestedMethod != "" || requestedHeaders != "") {
+ headers = opts.PreflightHeader(origin, requestedMethod, requestedHeaders)
+ for key, value := range headers {
+ ctx.Output.Header(key, value)
+ }
+ ctx.ResponseWriter.WriteHeader(http.StatusOK)
+ return
+ }
+ headers = opts.Header(origin)
+
+ for key, value := range headers {
+ ctx.Output.Header(key, value)
+ }
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/plugins/cors/cors_test.go b/src/vendor/github.com/astaxie/beego/plugins/cors/cors_test.go
new file mode 100644
index 0000000000..3403914353
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/plugins/cors/cors_test.go
@@ -0,0 +1,253 @@
+// Copyright 2014 beego Author. 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 cors
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/astaxie/beego"
+ "github.com/astaxie/beego/context"
+)
+
+// HTTPHeaderGuardRecorder is httptest.ResponseRecorder with own http.Header
+type HTTPHeaderGuardRecorder struct {
+ *httptest.ResponseRecorder
+ savedHeaderMap http.Header
+}
+
+// NewRecorder return HttpHeaderGuardRecorder
+func NewRecorder() *HTTPHeaderGuardRecorder {
+ return &HTTPHeaderGuardRecorder{httptest.NewRecorder(), nil}
+}
+
+func (gr *HTTPHeaderGuardRecorder) WriteHeader(code int) {
+ gr.ResponseRecorder.WriteHeader(code)
+ gr.savedHeaderMap = gr.ResponseRecorder.Header()
+}
+
+func (gr *HTTPHeaderGuardRecorder) Header() http.Header {
+ if gr.savedHeaderMap != nil {
+ // headers were written. clone so we don't get updates
+ clone := make(http.Header)
+ for k, v := range gr.savedHeaderMap {
+ clone[k] = v
+ }
+ return clone
+ }
+ return gr.ResponseRecorder.Header()
+}
+
+func Test_AllowAll(t *testing.T) {
+ recorder := httptest.NewRecorder()
+ handler := beego.NewControllerRegister()
+ handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+ AllowAllOrigins: true,
+ }))
+ handler.Any("/foo", func(ctx *context.Context) {
+ ctx.Output.SetStatus(500)
+ })
+ r, _ := http.NewRequest("PUT", "/foo", nil)
+ handler.ServeHTTP(recorder, r)
+
+ if recorder.HeaderMap.Get(headerAllowOrigin) != "*" {
+ t.Errorf("Allow-Origin header should be *")
+ }
+}
+
+func Test_AllowRegexMatch(t *testing.T) {
+ recorder := httptest.NewRecorder()
+ handler := beego.NewControllerRegister()
+ handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+ AllowOrigins: []string{"https://aaa.com", "https://*.foo.com"},
+ }))
+ handler.Any("/foo", func(ctx *context.Context) {
+ ctx.Output.SetStatus(500)
+ })
+ origin := "https://bar.foo.com"
+ r, _ := http.NewRequest("PUT", "/foo", nil)
+ r.Header.Add("Origin", origin)
+ handler.ServeHTTP(recorder, r)
+
+ headerValue := recorder.HeaderMap.Get(headerAllowOrigin)
+ if headerValue != origin {
+ t.Errorf("Allow-Origin header should be %v, found %v", origin, headerValue)
+ }
+}
+
+func Test_AllowRegexNoMatch(t *testing.T) {
+ recorder := httptest.NewRecorder()
+ handler := beego.NewControllerRegister()
+ handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+ AllowOrigins: []string{"https://*.foo.com"},
+ }))
+ handler.Any("/foo", func(ctx *context.Context) {
+ ctx.Output.SetStatus(500)
+ })
+ origin := "https://ww.foo.com.evil.com"
+ r, _ := http.NewRequest("PUT", "/foo", nil)
+ r.Header.Add("Origin", origin)
+ handler.ServeHTTP(recorder, r)
+
+ headerValue := recorder.HeaderMap.Get(headerAllowOrigin)
+ if headerValue != "" {
+ t.Errorf("Allow-Origin header should not exist, found %v", headerValue)
+ }
+}
+
+func Test_OtherHeaders(t *testing.T) {
+ recorder := httptest.NewRecorder()
+ handler := beego.NewControllerRegister()
+ handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+ AllowAllOrigins: true,
+ AllowCredentials: true,
+ AllowMethods: []string{"PATCH", "GET"},
+ AllowHeaders: []string{"Origin", "X-whatever"},
+ ExposeHeaders: []string{"Content-Length", "Hello"},
+ MaxAge: 5 * time.Minute,
+ }))
+ handler.Any("/foo", func(ctx *context.Context) {
+ ctx.Output.SetStatus(500)
+ })
+ r, _ := http.NewRequest("PUT", "/foo", nil)
+ handler.ServeHTTP(recorder, r)
+
+ credentialsVal := recorder.HeaderMap.Get(headerAllowCredentials)
+ methodsVal := recorder.HeaderMap.Get(headerAllowMethods)
+ headersVal := recorder.HeaderMap.Get(headerAllowHeaders)
+ exposedHeadersVal := recorder.HeaderMap.Get(headerExposeHeaders)
+ maxAgeVal := recorder.HeaderMap.Get(headerMaxAge)
+
+ if credentialsVal != "true" {
+ t.Errorf("Allow-Credentials is expected to be true, found %v", credentialsVal)
+ }
+
+ if methodsVal != "PATCH,GET" {
+ t.Errorf("Allow-Methods is expected to be PATCH,GET; found %v", methodsVal)
+ }
+
+ if headersVal != "Origin,X-whatever" {
+ t.Errorf("Allow-Headers is expected to be Origin,X-whatever; found %v", headersVal)
+ }
+
+ if exposedHeadersVal != "Content-Length,Hello" {
+ t.Errorf("Expose-Headers are expected to be Content-Length,Hello. Found %v", exposedHeadersVal)
+ }
+
+ if maxAgeVal != "300" {
+ t.Errorf("Max-Age is expected to be 300, found %v", maxAgeVal)
+ }
+}
+
+func Test_DefaultAllowHeaders(t *testing.T) {
+ recorder := httptest.NewRecorder()
+ handler := beego.NewControllerRegister()
+ handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+ AllowAllOrigins: true,
+ }))
+ handler.Any("/foo", func(ctx *context.Context) {
+ ctx.Output.SetStatus(500)
+ })
+
+ r, _ := http.NewRequest("PUT", "/foo", nil)
+ handler.ServeHTTP(recorder, r)
+
+ headersVal := recorder.HeaderMap.Get(headerAllowHeaders)
+ if headersVal != "Origin,Accept,Content-Type,Authorization" {
+ t.Errorf("Allow-Headers is expected to be Origin,Accept,Content-Type,Authorization; found %v", headersVal)
+ }
+}
+
+func Test_Preflight(t *testing.T) {
+ recorder := NewRecorder()
+ handler := beego.NewControllerRegister()
+ handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+ AllowAllOrigins: true,
+ AllowMethods: []string{"PUT", "PATCH"},
+ AllowHeaders: []string{"Origin", "X-whatever", "X-CaseSensitive"},
+ }))
+
+ handler.Any("/foo", func(ctx *context.Context) {
+ ctx.Output.SetStatus(200)
+ })
+
+ r, _ := http.NewRequest("OPTIONS", "/foo", nil)
+ r.Header.Add(headerRequestMethod, "PUT")
+ r.Header.Add(headerRequestHeaders, "X-whatever, x-casesensitive")
+ handler.ServeHTTP(recorder, r)
+
+ headers := recorder.Header()
+ methodsVal := headers.Get(headerAllowMethods)
+ headersVal := headers.Get(headerAllowHeaders)
+ originVal := headers.Get(headerAllowOrigin)
+
+ if methodsVal != "PUT,PATCH" {
+ t.Errorf("Allow-Methods is expected to be PUT,PATCH, found %v", methodsVal)
+ }
+
+ if !strings.Contains(headersVal, "X-whatever") {
+ t.Errorf("Allow-Headers is expected to contain X-whatever, found %v", headersVal)
+ }
+
+ if !strings.Contains(headersVal, "x-casesensitive") {
+ t.Errorf("Allow-Headers is expected to contain x-casesensitive, found %v", headersVal)
+ }
+
+ if originVal != "*" {
+ t.Errorf("Allow-Origin is expected to be *, found %v", originVal)
+ }
+
+ if recorder.Code != http.StatusOK {
+ t.Errorf("Status code is expected to be 200, found %d", recorder.Code)
+ }
+}
+
+func Benchmark_WithoutCORS(b *testing.B) {
+ recorder := httptest.NewRecorder()
+ handler := beego.NewControllerRegister()
+ beego.BConfig.RunMode = beego.PROD
+ handler.Any("/foo", func(ctx *context.Context) {
+ ctx.Output.SetStatus(500)
+ })
+ b.ResetTimer()
+ r, _ := http.NewRequest("PUT", "/foo", nil)
+ for i := 0; i < b.N; i++ {
+ handler.ServeHTTP(recorder, r)
+ }
+}
+
+func Benchmark_WithCORS(b *testing.B) {
+ recorder := httptest.NewRecorder()
+ handler := beego.NewControllerRegister()
+ beego.BConfig.RunMode = beego.PROD
+ handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+ AllowAllOrigins: true,
+ AllowCredentials: true,
+ AllowMethods: []string{"PATCH", "GET"},
+ AllowHeaders: []string{"Origin", "X-whatever"},
+ MaxAge: 5 * time.Minute,
+ }))
+ handler.Any("/foo", func(ctx *context.Context) {
+ ctx.Output.SetStatus(500)
+ })
+ b.ResetTimer()
+ r, _ := http.NewRequest("PUT", "/foo", nil)
+ for i := 0; i < b.N; i++ {
+ handler.ServeHTTP(recorder, r)
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/router_test.go b/src/vendor/github.com/astaxie/beego/router_test.go
new file mode 100644
index 0000000000..f26f0c86b9
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/router_test.go
@@ -0,0 +1,613 @@
+// Copyright 2014 beego Author. 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 beego
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "github.com/astaxie/beego/context"
+)
+
+type TestController struct {
+ Controller
+}
+
+func (tc *TestController) Get() {
+ tc.Data["Username"] = "astaxie"
+ tc.Ctx.Output.Body([]byte("ok"))
+}
+
+func (tc *TestController) Post() {
+ tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
+}
+
+func (tc *TestController) Param() {
+ tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
+}
+
+func (tc *TestController) List() {
+ tc.Ctx.Output.Body([]byte("i am list"))
+}
+
+func (tc *TestController) Params() {
+ tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param("0") + tc.Ctx.Input.Param("1") + tc.Ctx.Input.Param("2")))
+}
+
+func (tc *TestController) Myext() {
+ tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext")))
+}
+
+func (tc *TestController) GetURL() {
+ tc.Ctx.Output.Body([]byte(tc.URLFor(".Myext")))
+}
+
+func (tc *TestController) GetParams() {
+ tc.Ctx.WriteString(tc.Ctx.Input.Query(":last") + "+" +
+ tc.Ctx.Input.Query(":first") + "+" + tc.Ctx.Input.Query("learn"))
+}
+
+func (tc *TestController) GetManyRouter() {
+ tc.Ctx.WriteString(tc.Ctx.Input.Query(":id") + tc.Ctx.Input.Query(":page"))
+}
+
+func (tc *TestController) GetEmptyBody() {
+ var res []byte
+ tc.Ctx.Output.Body(res)
+}
+
+type ResStatus struct {
+ Code int
+ Msg string
+}
+
+type JSONController struct {
+ Controller
+}
+
+func (jc *JSONController) Prepare() {
+ jc.Data["json"] = "prepare"
+ jc.ServeJSON(true)
+}
+
+func (jc *JSONController) Get() {
+ jc.Data["Username"] = "astaxie"
+ jc.Ctx.Output.Body([]byte("ok"))
+}
+
+func TestUrlFor(t *testing.T) {
+ handler := NewControllerRegister()
+ handler.Add("/api/list", &TestController{}, "*:List")
+ handler.Add("/person/:last/:first", &TestController{}, "*:Param")
+ if a := handler.URLFor("TestController.List"); a != "/api/list" {
+ Info(a)
+ t.Errorf("TestController.List must equal to /api/list")
+ }
+ if a := handler.URLFor("TestController.Param", ":last", "xie", ":first", "asta"); a != "/person/xie/asta" {
+ t.Errorf("TestController.Param must equal to /person/xie/asta, but get " + a)
+ }
+}
+
+func TestUrlFor3(t *testing.T) {
+ handler := NewControllerRegister()
+ handler.AddAuto(&TestController{})
+ if a := handler.URLFor("TestController.Myext"); a != "/test/myext" && a != "/Test/Myext" {
+ t.Errorf("TestController.Myext must equal to /test/myext, but get " + a)
+ }
+ if a := handler.URLFor("TestController.GetURL"); a != "/test/geturl" && a != "/Test/GetURL" {
+ t.Errorf("TestController.GetURL must equal to /test/geturl, but get " + a)
+ }
+}
+
+func TestUrlFor2(t *testing.T) {
+ handler := NewControllerRegister()
+ handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, "*:List")
+ handler.Add("/v1/:username/edit", &TestController{}, "get:GetURL")
+ handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, "*:Param")
+ handler.Add("/:year:int/:month:int/:title/:entid", &TestController{})
+ if handler.URLFor("TestController.GetURL", ":username", "astaxie") != "/v1/astaxie/edit" {
+ Info(handler.URLFor("TestController.GetURL"))
+ t.Errorf("TestController.List must equal to /v1/astaxie/edit")
+ }
+
+ if handler.URLFor("TestController.List", ":v", "za", ":id", "12", ":page", "123") !=
+ "/v1/za/cms_12_123.html" {
+ Info(handler.URLFor("TestController.List"))
+ t.Errorf("TestController.List must equal to /v1/za/cms_12_123.html")
+ }
+ if handler.URLFor("TestController.Param", ":v", "za", ":id", "12", ":page", "123") !=
+ "/v1/za_cms/ttt_12_123.html" {
+ Info(handler.URLFor("TestController.Param"))
+ t.Errorf("TestController.List must equal to /v1/za_cms/ttt_12_123.html")
+ }
+ if handler.URLFor("TestController.Get", ":year", "1111", ":month", "11",
+ ":title", "aaaa", ":entid", "aaaa") !=
+ "/1111/11/aaaa/aaaa" {
+ Info(handler.URLFor("TestController.Get"))
+ t.Errorf("TestController.Get must equal to /1111/11/aaaa/aaaa")
+ }
+}
+
+func TestUserFunc(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/api/list", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Add("/api/list", &TestController{}, "*:List")
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "i am list" {
+ t.Errorf("user define func can't run")
+ }
+}
+
+func TestPostFunc(t *testing.T) {
+ r, _ := http.NewRequest("POST", "/astaxie", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Add("/:name", &TestController{})
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "astaxie" {
+ t.Errorf("post func should astaxie")
+ }
+}
+
+func TestAutoFunc(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/test/list", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.AddAuto(&TestController{})
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "i am list" {
+ t.Errorf("user define func can't run")
+ }
+}
+
+func TestAutoFunc2(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/Test/List", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.AddAuto(&TestController{})
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "i am list" {
+ t.Errorf("user define func can't run")
+ }
+}
+
+func TestAutoFuncParams(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/test/params/2009/11/12", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.AddAuto(&TestController{})
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "20091112" {
+ t.Errorf("user define func can't run")
+ }
+}
+
+func TestAutoExtFunc(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/test/myext.json", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.AddAuto(&TestController{})
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "json" {
+ t.Errorf("user define func can't run")
+ }
+}
+
+func TestRouteOk(t *testing.T) {
+
+ r, _ := http.NewRequest("GET", "/person/anderson/thomas?learn=kungfu", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Add("/person/:last/:first", &TestController{}, "get:GetParams")
+ handler.ServeHTTP(w, r)
+ body := w.Body.String()
+ if body != "anderson+thomas+kungfu" {
+ t.Errorf("url param set to [%s];", body)
+ }
+}
+
+func TestManyRoute(t *testing.T) {
+
+ r, _ := http.NewRequest("GET", "/beego32-12.html", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{}, "get:GetManyRouter")
+ handler.ServeHTTP(w, r)
+
+ body := w.Body.String()
+
+ if body != "3212" {
+ t.Errorf("url param set to [%s];", body)
+ }
+}
+
+// Test for issue #1669
+func TestEmptyResponse(t *testing.T) {
+
+ r, _ := http.NewRequest("GET", "/beego-empty.html", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Add("/beego-empty.html", &TestController{}, "get:GetEmptyBody")
+ handler.ServeHTTP(w, r)
+
+ if body := w.Body.String(); body != "" {
+ t.Error("want empty body")
+ }
+}
+
+func TestNotFound(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.ServeHTTP(w, r)
+
+ if w.Code != http.StatusNotFound {
+ t.Errorf("Code set to [%v]; want [%v]", w.Code, http.StatusNotFound)
+ }
+}
+
+// TestStatic tests the ability to serve static
+// content from the filesystem
+func TestStatic(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/static/js/jquery.js", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.ServeHTTP(w, r)
+
+ if w.Code != 404 {
+ t.Errorf("handler.Static failed to serve file")
+ }
+}
+
+func TestPrepare(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/json/list", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Add("/json/list", &JSONController{})
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != `"prepare"` {
+ t.Errorf(w.Body.String() + "user define func can't run")
+ }
+}
+
+func TestAutoPrefix(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/admin/test/list", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.AddAutoPrefix("/admin", &TestController{})
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "i am list" {
+ t.Errorf("TestAutoPrefix can't run")
+ }
+}
+
+func TestRouterGet(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/user", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Get("/user", func(ctx *context.Context) {
+ ctx.Output.Body([]byte("Get userlist"))
+ })
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "Get userlist" {
+ t.Errorf("TestRouterGet can't run")
+ }
+}
+
+func TestRouterPost(t *testing.T) {
+ r, _ := http.NewRequest("POST", "/user/123", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Post("/user/:id", func(ctx *context.Context) {
+ ctx.Output.Body([]byte(ctx.Input.Param(":id")))
+ })
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "123" {
+ t.Errorf("TestRouterPost can't run")
+ }
+}
+
+func sayhello(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("sayhello"))
+}
+
+func TestRouterHandler(t *testing.T) {
+ r, _ := http.NewRequest("POST", "/sayhi", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Handler("/sayhi", http.HandlerFunc(sayhello))
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "sayhello" {
+ t.Errorf("TestRouterHandler can't run")
+ }
+}
+
+func TestRouterHandlerAll(t *testing.T) {
+ r, _ := http.NewRequest("POST", "/sayhi/a/b/c", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Handler("/sayhi", http.HandlerFunc(sayhello), true)
+ handler.ServeHTTP(w, r)
+ if w.Body.String() != "sayhello" {
+ t.Errorf("TestRouterHandler can't run")
+ }
+}
+
+//
+// Benchmarks NewApp:
+//
+
+func beegoFilterFunc(ctx *context.Context) {
+ ctx.WriteString("hello")
+}
+
+type AdminController struct {
+ Controller
+}
+
+func (a *AdminController) Get() {
+ a.Ctx.WriteString("hello")
+}
+
+func TestRouterFunc(t *testing.T) {
+ mux := NewControllerRegister()
+ mux.Get("/action", beegoFilterFunc)
+ mux.Post("/action", beegoFilterFunc)
+ rw, r := testRequest("GET", "/action")
+ mux.ServeHTTP(rw, r)
+ if rw.Body.String() != "hello" {
+ t.Errorf("TestRouterFunc can't run")
+ }
+}
+
+func BenchmarkFunc(b *testing.B) {
+ mux := NewControllerRegister()
+ mux.Get("/action", beegoFilterFunc)
+ rw, r := testRequest("GET", "/action")
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ mux.ServeHTTP(rw, r)
+ }
+}
+
+func BenchmarkController(b *testing.B) {
+ mux := NewControllerRegister()
+ mux.Add("/action", &AdminController{})
+ rw, r := testRequest("GET", "/action")
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ mux.ServeHTTP(rw, r)
+ }
+}
+
+func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request) {
+ request, _ := http.NewRequest(method, path, nil)
+ recorder := httptest.NewRecorder()
+
+ return recorder, request
+}
+
+// Execution point: BeforeRouter
+// expectation: only BeforeRouter function is executed, notmatch output as router doesn't handle
+func TestFilterBeforeRouter(t *testing.T) {
+ testName := "TestFilterBeforeRouter"
+ url := "/beforeRouter"
+
+ mux := NewControllerRegister()
+ mux.InsertFilter(url, BeforeRouter, beegoBeforeRouter1)
+
+ mux.Get(url, beegoFilterFunc)
+
+ rw, r := testRequest("GET", url)
+ mux.ServeHTTP(rw, r)
+
+ if strings.Contains(rw.Body.String(), "BeforeRouter1") == false {
+ t.Errorf(testName + " BeforeRouter did not run")
+ }
+ if strings.Contains(rw.Body.String(), "hello") == true {
+ t.Errorf(testName + " BeforeRouter did not return properly")
+ }
+}
+
+// Execution point: BeforeExec
+// expectation: only BeforeExec function is executed, match as router determines route only
+func TestFilterBeforeExec(t *testing.T) {
+ testName := "TestFilterBeforeExec"
+ url := "/beforeExec"
+
+ mux := NewControllerRegister()
+ mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
+ mux.InsertFilter(url, BeforeExec, beegoBeforeExec1)
+
+ mux.Get(url, beegoFilterFunc)
+
+ rw, r := testRequest("GET", url)
+ mux.ServeHTTP(rw, r)
+
+ if strings.Contains(rw.Body.String(), "BeforeExec1") == false {
+ t.Errorf(testName + " BeforeExec did not run")
+ }
+ if strings.Contains(rw.Body.String(), "hello") == true {
+ t.Errorf(testName + " BeforeExec did not return properly")
+ }
+ if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
+ t.Errorf(testName + " BeforeRouter ran in error")
+ }
+}
+
+// Execution point: AfterExec
+// expectation: only AfterExec function is executed, match as router handles
+func TestFilterAfterExec(t *testing.T) {
+ testName := "TestFilterAfterExec"
+ url := "/afterExec"
+
+ mux := NewControllerRegister()
+ mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
+ mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
+ mux.InsertFilter(url, AfterExec, beegoAfterExec1, false)
+
+ mux.Get(url, beegoFilterFunc)
+
+ rw, r := testRequest("GET", url)
+ mux.ServeHTTP(rw, r)
+
+ if strings.Contains(rw.Body.String(), "AfterExec1") == false {
+ t.Errorf(testName + " AfterExec did not run")
+ }
+ if strings.Contains(rw.Body.String(), "hello") == false {
+ t.Errorf(testName + " handler did not run properly")
+ }
+ if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
+ t.Errorf(testName + " BeforeRouter ran in error")
+ }
+ if strings.Contains(rw.Body.String(), "BeforeExec") == true {
+ t.Errorf(testName + " BeforeExec ran in error")
+ }
+}
+
+// Execution point: FinishRouter
+// expectation: only FinishRouter function is executed, match as router handles
+func TestFilterFinishRouter(t *testing.T) {
+ testName := "TestFilterFinishRouter"
+ url := "/finishRouter"
+
+ mux := NewControllerRegister()
+ mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
+ mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
+ mux.InsertFilter(url, AfterExec, beegoFilterNoOutput)
+ mux.InsertFilter(url, FinishRouter, beegoFinishRouter1)
+
+ mux.Get(url, beegoFilterFunc)
+
+ rw, r := testRequest("GET", url)
+ mux.ServeHTTP(rw, r)
+
+ if strings.Contains(rw.Body.String(), "FinishRouter1") == true {
+ t.Errorf(testName + " FinishRouter did not run")
+ }
+ if strings.Contains(rw.Body.String(), "hello") == false {
+ t.Errorf(testName + " handler did not run properly")
+ }
+ if strings.Contains(rw.Body.String(), "AfterExec1") == true {
+ t.Errorf(testName + " AfterExec ran in error")
+ }
+ if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
+ t.Errorf(testName + " BeforeRouter ran in error")
+ }
+ if strings.Contains(rw.Body.String(), "BeforeExec") == true {
+ t.Errorf(testName + " BeforeExec ran in error")
+ }
+}
+
+// Execution point: FinishRouter
+// expectation: only first FinishRouter function is executed, match as router handles
+func TestFilterFinishRouterMultiFirstOnly(t *testing.T) {
+ testName := "TestFilterFinishRouterMultiFirstOnly"
+ url := "/finishRouterMultiFirstOnly"
+
+ mux := NewControllerRegister()
+ mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false)
+ mux.InsertFilter(url, FinishRouter, beegoFinishRouter2)
+
+ mux.Get(url, beegoFilterFunc)
+
+ rw, r := testRequest("GET", url)
+ mux.ServeHTTP(rw, r)
+
+ if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
+ t.Errorf(testName + " FinishRouter1 did not run")
+ }
+ if strings.Contains(rw.Body.String(), "hello") == false {
+ t.Errorf(testName + " handler did not run properly")
+ }
+ // not expected in body
+ if strings.Contains(rw.Body.String(), "FinishRouter2") == true {
+ t.Errorf(testName + " FinishRouter2 did run")
+ }
+}
+
+// Execution point: FinishRouter
+// expectation: both FinishRouter functions execute, match as router handles
+func TestFilterFinishRouterMulti(t *testing.T) {
+ testName := "TestFilterFinishRouterMulti"
+ url := "/finishRouterMulti"
+
+ mux := NewControllerRegister()
+ mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false)
+ mux.InsertFilter(url, FinishRouter, beegoFinishRouter2, false)
+
+ mux.Get(url, beegoFilterFunc)
+
+ rw, r := testRequest("GET", url)
+ mux.ServeHTTP(rw, r)
+
+ if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
+ t.Errorf(testName + " FinishRouter1 did not run")
+ }
+ if strings.Contains(rw.Body.String(), "hello") == false {
+ t.Errorf(testName + " handler did not run properly")
+ }
+ if strings.Contains(rw.Body.String(), "FinishRouter2") == false {
+ t.Errorf(testName + " FinishRouter2 did not run properly")
+ }
+}
+
+func beegoFilterNoOutput(ctx *context.Context) {
+ return
+}
+func beegoBeforeRouter1(ctx *context.Context) {
+ ctx.WriteString("|BeforeRouter1")
+}
+func beegoBeforeRouter2(ctx *context.Context) {
+ ctx.WriteString("|BeforeRouter2")
+}
+func beegoBeforeExec1(ctx *context.Context) {
+ ctx.WriteString("|BeforeExec1")
+}
+func beegoBeforeExec2(ctx *context.Context) {
+ ctx.WriteString("|BeforeExec2")
+}
+func beegoAfterExec1(ctx *context.Context) {
+ ctx.WriteString("|AfterExec1")
+}
+func beegoAfterExec2(ctx *context.Context) {
+ ctx.WriteString("|AfterExec2")
+}
+func beegoFinishRouter1(ctx *context.Context) {
+ ctx.WriteString("|FinishRouter1")
+}
+func beegoFinishRouter2(ctx *context.Context) {
+ ctx.WriteString("|FinishRouter2")
+}
diff --git a/src/vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go b/src/vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go
new file mode 100644
index 0000000000..d5be11d0b1
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go
@@ -0,0 +1,243 @@
+// Copyright 2014 beego Author. 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 couchbase for session provider
+//
+// depend on github.com/couchbaselabs/go-couchbasee
+//
+// go install github.com/couchbaselabs/go-couchbase
+//
+// Usage:
+// import(
+// _ "github.com/astaxie/beego/session/couchbase"
+// "github.com/astaxie/beego/session"
+// )
+//
+// func init() {
+// globalSessions, _ = session.NewManager("couchbase", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"http://host:port/, Pool, Bucket"}``)
+// go globalSessions.GC()
+// }
+//
+// more docs: http://beego.me/docs/module/session.md
+package couchbase
+
+import (
+ "net/http"
+ "strings"
+ "sync"
+
+ couchbase "github.com/couchbase/go-couchbase"
+
+ "github.com/astaxie/beego/session"
+)
+
+var couchbpder = &Provider{}
+
+// SessionStore store each session
+type SessionStore struct {
+ b *couchbase.Bucket
+ sid string
+ lock sync.RWMutex
+ values map[interface{}]interface{}
+ maxlifetime int64
+}
+
+// Provider couchabse provided
+type Provider struct {
+ maxlifetime int64
+ savePath string
+ pool string
+ bucket string
+ b *couchbase.Bucket
+}
+
+// Set value to couchabse session
+func (cs *SessionStore) Set(key, value interface{}) error {
+ cs.lock.Lock()
+ defer cs.lock.Unlock()
+ cs.values[key] = value
+ return nil
+}
+
+// Get value from couchabse session
+func (cs *SessionStore) Get(key interface{}) interface{} {
+ cs.lock.RLock()
+ defer cs.lock.RUnlock()
+ if v, ok := cs.values[key]; ok {
+ return v
+ }
+ return nil
+}
+
+// Delete value in couchbase session by given key
+func (cs *SessionStore) Delete(key interface{}) error {
+ cs.lock.Lock()
+ defer cs.lock.Unlock()
+ delete(cs.values, key)
+ return nil
+}
+
+// Flush Clean all values in couchbase session
+func (cs *SessionStore) Flush() error {
+ cs.lock.Lock()
+ defer cs.lock.Unlock()
+ cs.values = make(map[interface{}]interface{})
+ return nil
+}
+
+// SessionID Get couchbase session store id
+func (cs *SessionStore) SessionID() string {
+ return cs.sid
+}
+
+// SessionRelease Write couchbase session with Gob string
+func (cs *SessionStore) SessionRelease(w http.ResponseWriter) {
+ defer cs.b.Close()
+
+ bo, err := session.EncodeGob(cs.values)
+ if err != nil {
+ return
+ }
+
+ cs.b.Set(cs.sid, int(cs.maxlifetime), bo)
+}
+
+func (cp *Provider) getBucket() *couchbase.Bucket {
+ c, err := couchbase.Connect(cp.savePath)
+ if err != nil {
+ return nil
+ }
+
+ pool, err := c.GetPool(cp.pool)
+ if err != nil {
+ return nil
+ }
+
+ bucket, err := pool.GetBucket(cp.bucket)
+ if err != nil {
+ return nil
+ }
+
+ return bucket
+}
+
+// SessionInit init couchbase session
+// savepath like couchbase server REST/JSON URL
+// e.g. http://host:port/, Pool, Bucket
+func (cp *Provider) SessionInit(maxlifetime int64, savePath string) error {
+ cp.maxlifetime = maxlifetime
+ configs := strings.Split(savePath, ",")
+ if len(configs) > 0 {
+ cp.savePath = configs[0]
+ }
+ if len(configs) > 1 {
+ cp.pool = configs[1]
+ }
+ if len(configs) > 2 {
+ cp.bucket = configs[2]
+ }
+
+ return nil
+}
+
+// SessionRead read couchbase session by sid
+func (cp *Provider) SessionRead(sid string) (session.Store, error) {
+ cp.b = cp.getBucket()
+
+ var doc []byte
+
+ err := cp.b.Get(sid, &doc)
+ var kv map[interface{}]interface{}
+ if doc == nil {
+ kv = make(map[interface{}]interface{})
+ } else {
+ kv, err = session.DecodeGob(doc)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ cs := &SessionStore{b: cp.b, sid: sid, values: kv, maxlifetime: cp.maxlifetime}
+ return cs, nil
+}
+
+// SessionExist Check couchbase session exist.
+// it checkes sid exist or not.
+func (cp *Provider) SessionExist(sid string) bool {
+ cp.b = cp.getBucket()
+ defer cp.b.Close()
+
+ var doc []byte
+
+ if err := cp.b.Get(sid, &doc); err != nil || doc == nil {
+ return false
+ }
+ return true
+}
+
+// SessionRegenerate remove oldsid and use sid to generate new session
+func (cp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+ cp.b = cp.getBucket()
+
+ var doc []byte
+ if err := cp.b.Get(oldsid, &doc); err != nil || doc == nil {
+ cp.b.Set(sid, int(cp.maxlifetime), "")
+ } else {
+ err := cp.b.Delete(oldsid)
+ if err != nil {
+ return nil, err
+ }
+ _, _ = cp.b.Add(sid, int(cp.maxlifetime), doc)
+ }
+
+ err := cp.b.Get(sid, &doc)
+ if err != nil {
+ return nil, err
+ }
+ var kv map[interface{}]interface{}
+ if doc == nil {
+ kv = make(map[interface{}]interface{})
+ } else {
+ kv, err = session.DecodeGob(doc)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ cs := &SessionStore{b: cp.b, sid: sid, values: kv, maxlifetime: cp.maxlifetime}
+ return cs, nil
+}
+
+// SessionDestroy Remove bucket in this couchbase
+func (cp *Provider) SessionDestroy(sid string) error {
+ cp.b = cp.getBucket()
+ defer cp.b.Close()
+
+ cp.b.Delete(sid)
+ return nil
+}
+
+// SessionGC Recycle
+func (cp *Provider) SessionGC() {
+ return
+}
+
+// SessionAll return all active session
+func (cp *Provider) SessionAll() int {
+ return 0
+}
+
+func init() {
+ session.Register("couchbase", couchbpder)
+}
diff --git a/src/vendor/github.com/astaxie/beego/session/ledis/ledis_session.go b/src/vendor/github.com/astaxie/beego/session/ledis/ledis_session.go
new file mode 100644
index 0000000000..68f37b08c8
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/session/ledis/ledis_session.go
@@ -0,0 +1,179 @@
+// Package ledis provide session Provider
+package ledis
+
+import (
+ "net/http"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/astaxie/beego/session"
+ "github.com/siddontang/ledisdb/config"
+ "github.com/siddontang/ledisdb/ledis"
+)
+
+var ledispder = &Provider{}
+var c *ledis.DB
+
+// SessionStore ledis session store
+type SessionStore struct {
+ sid string
+ lock sync.RWMutex
+ values map[interface{}]interface{}
+ maxlifetime int64
+}
+
+// Set value in ledis session
+func (ls *SessionStore) Set(key, value interface{}) error {
+ ls.lock.Lock()
+ defer ls.lock.Unlock()
+ ls.values[key] = value
+ return nil
+}
+
+// Get value in ledis session
+func (ls *SessionStore) Get(key interface{}) interface{} {
+ ls.lock.RLock()
+ defer ls.lock.RUnlock()
+ if v, ok := ls.values[key]; ok {
+ return v
+ }
+ return nil
+}
+
+// Delete value in ledis session
+func (ls *SessionStore) Delete(key interface{}) error {
+ ls.lock.Lock()
+ defer ls.lock.Unlock()
+ delete(ls.values, key)
+ return nil
+}
+
+// Flush clear all values in ledis session
+func (ls *SessionStore) Flush() error {
+ ls.lock.Lock()
+ defer ls.lock.Unlock()
+ ls.values = make(map[interface{}]interface{})
+ return nil
+}
+
+// SessionID get ledis session id
+func (ls *SessionStore) SessionID() string {
+ return ls.sid
+}
+
+// SessionRelease save session values to ledis
+func (ls *SessionStore) SessionRelease(w http.ResponseWriter) {
+ b, err := session.EncodeGob(ls.values)
+ if err != nil {
+ return
+ }
+ c.Set([]byte(ls.sid), b)
+ c.Expire([]byte(ls.sid), ls.maxlifetime)
+}
+
+// Provider ledis session provider
+type Provider struct {
+ maxlifetime int64
+ savePath string
+ db int
+}
+
+// SessionInit init ledis session
+// savepath like ledis server saveDataPath,pool size
+// e.g. 127.0.0.1:6379,100,astaxie
+func (lp *Provider) SessionInit(maxlifetime int64, savePath string) error {
+ var err error
+ lp.maxlifetime = maxlifetime
+ configs := strings.Split(savePath, ",")
+ if len(configs) == 1 {
+ lp.savePath = configs[0]
+ } else if len(configs) == 2 {
+ lp.savePath = configs[0]
+ lp.db, err = strconv.Atoi(configs[1])
+ if err != nil {
+ return err
+ }
+ }
+ cfg := new(config.Config)
+ cfg.DataDir = lp.savePath
+ nowLedis, err := ledis.Open(cfg)
+ c, err = nowLedis.Select(lp.db)
+ if err != nil {
+ println(err)
+ return nil
+ }
+ return nil
+}
+
+// SessionRead read ledis session by sid
+func (lp *Provider) SessionRead(sid string) (session.Store, error) {
+ kvs, err := c.Get([]byte(sid))
+ var kv map[interface{}]interface{}
+ if len(kvs) == 0 {
+ kv = make(map[interface{}]interface{})
+ } else {
+ kv, err = session.DecodeGob(kvs)
+ if err != nil {
+ return nil, err
+ }
+ }
+ ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
+ return ls, nil
+}
+
+// SessionExist check ledis session exist by sid
+func (lp *Provider) SessionExist(sid string) bool {
+ count, _ := c.Exists([]byte(sid))
+ if count == 0 {
+ return false
+ }
+ return true
+}
+
+// SessionRegenerate generate new sid for ledis session
+func (lp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+ count, _ := c.Exists([]byte(sid))
+ if count == 0 {
+ // oldsid doesn't exists, set the new sid directly
+ // ignore error here, since if it return error
+ // the existed value will be 0
+ c.Set([]byte(sid), []byte(""))
+ c.Expire([]byte(sid), lp.maxlifetime)
+ } else {
+ data, _ := c.Get([]byte(oldsid))
+ c.Set([]byte(sid), data)
+ c.Expire([]byte(sid), lp.maxlifetime)
+ }
+ kvs, err := c.Get([]byte(sid))
+ var kv map[interface{}]interface{}
+ if len(kvs) == 0 {
+ kv = make(map[interface{}]interface{})
+ } else {
+ kv, err = session.DecodeGob([]byte(kvs))
+ if err != nil {
+ return nil, err
+ }
+ }
+ ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
+ return ls, nil
+}
+
+// SessionDestroy delete ledis session by id
+func (lp *Provider) SessionDestroy(sid string) error {
+ c.Del([]byte(sid))
+ return nil
+}
+
+// SessionGC Impelment method, no used.
+func (lp *Provider) SessionGC() {
+ return
+}
+
+// SessionAll return all active session
+func (lp *Provider) SessionAll() int {
+ return 0
+}
+func init() {
+ session.Register("ledis", ledispder)
+}
diff --git a/src/vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go b/src/vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go
new file mode 100644
index 0000000000..f1069bc93a
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go
@@ -0,0 +1,232 @@
+// Copyright 2014 beego Author. 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 memcache for session provider
+//
+// depend on github.com/bradfitz/gomemcache/memcache
+//
+// go install github.com/bradfitz/gomemcache/memcache
+//
+// Usage:
+// import(
+// _ "github.com/astaxie/beego/session/memcache"
+// "github.com/astaxie/beego/session"
+// )
+//
+// func init() {
+// globalSessions, _ = session.NewManager("memcache", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:11211"}``)
+// go globalSessions.GC()
+// }
+//
+// more docs: http://beego.me/docs/module/session.md
+package memcache
+
+import (
+ "net/http"
+ "strings"
+ "sync"
+
+ "github.com/astaxie/beego/session"
+
+ "github.com/bradfitz/gomemcache/memcache"
+)
+
+var mempder = &MemProvider{}
+var client *memcache.Client
+
+// SessionStore memcache session store
+type SessionStore struct {
+ sid string
+ lock sync.RWMutex
+ values map[interface{}]interface{}
+ maxlifetime int64
+}
+
+// Set value in memcache session
+func (rs *SessionStore) Set(key, value interface{}) error {
+ rs.lock.Lock()
+ defer rs.lock.Unlock()
+ rs.values[key] = value
+ return nil
+}
+
+// Get value in memcache session
+func (rs *SessionStore) Get(key interface{}) interface{} {
+ rs.lock.RLock()
+ defer rs.lock.RUnlock()
+ if v, ok := rs.values[key]; ok {
+ return v
+ }
+ return nil
+}
+
+// Delete value in memcache session
+func (rs *SessionStore) Delete(key interface{}) error {
+ rs.lock.Lock()
+ defer rs.lock.Unlock()
+ delete(rs.values, key)
+ return nil
+}
+
+// Flush clear all values in memcache session
+func (rs *SessionStore) Flush() error {
+ rs.lock.Lock()
+ defer rs.lock.Unlock()
+ rs.values = make(map[interface{}]interface{})
+ return nil
+}
+
+// SessionID get memcache session id
+func (rs *SessionStore) SessionID() string {
+ return rs.sid
+}
+
+// SessionRelease save session values to memcache
+func (rs *SessionStore) SessionRelease(w http.ResponseWriter) {
+ b, err := session.EncodeGob(rs.values)
+ if err != nil {
+ return
+ }
+ item := memcache.Item{Key: rs.sid, Value: b, Expiration: int32(rs.maxlifetime)}
+ client.Set(&item)
+}
+
+// MemProvider memcache session provider
+type MemProvider struct {
+ maxlifetime int64
+ conninfo []string
+ poolsize int
+ password string
+}
+
+// SessionInit init memcache session
+// savepath like
+// e.g. 127.0.0.1:9090
+func (rp *MemProvider) SessionInit(maxlifetime int64, savePath string) error {
+ rp.maxlifetime = maxlifetime
+ rp.conninfo = strings.Split(savePath, ";")
+ client = memcache.New(rp.conninfo...)
+ return nil
+}
+
+// SessionRead read memcache session by sid
+func (rp *MemProvider) SessionRead(sid string) (session.Store, error) {
+ if client == nil {
+ if err := rp.connectInit(); err != nil {
+ return nil, err
+ }
+ }
+ item, err := client.Get(sid)
+ if err != nil && err == memcache.ErrCacheMiss {
+ rs := &SessionStore{sid: sid, values: make(map[interface{}]interface{}), maxlifetime: rp.maxlifetime}
+ return rs, nil
+ }
+ var kv map[interface{}]interface{}
+ if len(item.Value) == 0 {
+ kv = make(map[interface{}]interface{})
+ } else {
+ kv, err = session.DecodeGob(item.Value)
+ if err != nil {
+ return nil, err
+ }
+ }
+ rs := &SessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime}
+ return rs, nil
+}
+
+// SessionExist check memcache session exist by sid
+func (rp *MemProvider) SessionExist(sid string) bool {
+ if client == nil {
+ if err := rp.connectInit(); err != nil {
+ return false
+ }
+ }
+ if item, err := client.Get(sid); err != nil || len(item.Value) == 0 {
+ return false
+ }
+ return true
+}
+
+// SessionRegenerate generate new sid for memcache session
+func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+ if client == nil {
+ if err := rp.connectInit(); err != nil {
+ return nil, err
+ }
+ }
+ var contain []byte
+ if item, err := client.Get(sid); err != nil || len(item.Value) == 0 {
+ // oldsid doesn't exists, set the new sid directly
+ // ignore error here, since if it return error
+ // the existed value will be 0
+ item.Key = sid
+ item.Value = []byte("")
+ item.Expiration = int32(rp.maxlifetime)
+ client.Set(item)
+ } else {
+ client.Delete(oldsid)
+ item.Key = sid
+ item.Expiration = int32(rp.maxlifetime)
+ client.Set(item)
+ contain = item.Value
+ }
+
+ var kv map[interface{}]interface{}
+ if len(contain) == 0 {
+ kv = make(map[interface{}]interface{})
+ } else {
+ var err error
+ kv, err = session.DecodeGob(contain)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ rs := &SessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime}
+ return rs, nil
+}
+
+// SessionDestroy delete memcache session by id
+func (rp *MemProvider) SessionDestroy(sid string) error {
+ if client == nil {
+ if err := rp.connectInit(); err != nil {
+ return err
+ }
+ }
+
+ err := client.Delete(sid)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (rp *MemProvider) connectInit() error {
+ client = memcache.New(rp.conninfo...)
+ return nil
+}
+
+// SessionGC Impelment method, no used.
+func (rp *MemProvider) SessionGC() {
+ return
+}
+
+// SessionAll return all activeSession
+func (rp *MemProvider) SessionAll() int {
+ return 0
+}
+
+func init() {
+ session.Register("memcache", mempder)
+}
diff --git a/src/vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go b/src/vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go
new file mode 100644
index 0000000000..969d26c97f
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go
@@ -0,0 +1,233 @@
+// Copyright 2014 beego Author. 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 mysql for session provider
+//
+// depends on github.com/go-sql-driver/mysql:
+//
+// go install github.com/go-sql-driver/mysql
+//
+// mysql session support need create table as sql:
+// CREATE TABLE `session` (
+// `session_key` char(64) NOT NULL,
+// `session_data` blob,
+// `session_expiry` int(11) unsigned NOT NULL,
+// PRIMARY KEY (`session_key`)
+// ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+//
+// Usage:
+// import(
+// _ "github.com/astaxie/beego/session/mysql"
+// "github.com/astaxie/beego/session"
+// )
+//
+// func init() {
+// globalSessions, _ = session.NewManager("mysql", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]"}``)
+// go globalSessions.GC()
+// }
+//
+// more docs: http://beego.me/docs/module/session.md
+package mysql
+
+import (
+ "database/sql"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/astaxie/beego/session"
+ // import mysql driver
+ _ "github.com/go-sql-driver/mysql"
+)
+
+var (
+ // TableName store the session in MySQL
+ TableName = "session"
+ mysqlpder = &Provider{}
+)
+
+// SessionStore mysql session store
+type SessionStore struct {
+ c *sql.DB
+ sid string
+ lock sync.RWMutex
+ values map[interface{}]interface{}
+}
+
+// Set value in mysql session.
+// it is temp value in map.
+func (st *SessionStore) Set(key, value interface{}) error {
+ st.lock.Lock()
+ defer st.lock.Unlock()
+ st.values[key] = value
+ return nil
+}
+
+// Get value from mysql session
+func (st *SessionStore) Get(key interface{}) interface{} {
+ st.lock.RLock()
+ defer st.lock.RUnlock()
+ if v, ok := st.values[key]; ok {
+ return v
+ }
+ return nil
+}
+
+// Delete value in mysql session
+func (st *SessionStore) Delete(key interface{}) error {
+ st.lock.Lock()
+ defer st.lock.Unlock()
+ delete(st.values, key)
+ return nil
+}
+
+// Flush clear all values in mysql session
+func (st *SessionStore) Flush() error {
+ st.lock.Lock()
+ defer st.lock.Unlock()
+ st.values = make(map[interface{}]interface{})
+ return nil
+}
+
+// SessionID get session id of this mysql session store
+func (st *SessionStore) SessionID() string {
+ return st.sid
+}
+
+// SessionRelease save mysql session values to database.
+// must call this method to save values to database.
+func (st *SessionStore) SessionRelease(w http.ResponseWriter) {
+ defer st.c.Close()
+ b, err := session.EncodeGob(st.values)
+ if err != nil {
+ return
+ }
+ st.c.Exec("UPDATE "+TableName+" set `session_data`=?, `session_expiry`=? where session_key=?",
+ b, time.Now().Unix(), st.sid)
+
+}
+
+// Provider mysql session provider
+type Provider struct {
+ maxlifetime int64
+ savePath string
+}
+
+// connect to mysql
+func (mp *Provider) connectInit() *sql.DB {
+ db, e := sql.Open("mysql", mp.savePath)
+ if e != nil {
+ return nil
+ }
+ return db
+}
+
+// SessionInit init mysql session.
+// savepath is the connection string of mysql.
+func (mp *Provider) SessionInit(maxlifetime int64, savePath string) error {
+ mp.maxlifetime = maxlifetime
+ mp.savePath = savePath
+ return nil
+}
+
+// SessionRead get mysql session by sid
+func (mp *Provider) SessionRead(sid string) (session.Store, error) {
+ c := mp.connectInit()
+ row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid)
+ var sessiondata []byte
+ err := row.Scan(&sessiondata)
+ if err == sql.ErrNoRows {
+ c.Exec("insert into "+TableName+"(`session_key`,`session_data`,`session_expiry`) values(?,?,?)",
+ sid, "", time.Now().Unix())
+ }
+ var kv map[interface{}]interface{}
+ if len(sessiondata) == 0 {
+ kv = make(map[interface{}]interface{})
+ } else {
+ kv, err = session.DecodeGob(sessiondata)
+ if err != nil {
+ return nil, err
+ }
+ }
+ rs := &SessionStore{c: c, sid: sid, values: kv}
+ return rs, nil
+}
+
+// SessionExist check mysql session exist
+func (mp *Provider) SessionExist(sid string) bool {
+ c := mp.connectInit()
+ defer c.Close()
+ row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid)
+ var sessiondata []byte
+ err := row.Scan(&sessiondata)
+ if err == sql.ErrNoRows {
+ return false
+ }
+ return true
+}
+
+// SessionRegenerate generate new sid for mysql session
+func (mp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+ c := mp.connectInit()
+ row := c.QueryRow("select session_data from "+TableName+" where session_key=?", oldsid)
+ var sessiondata []byte
+ err := row.Scan(&sessiondata)
+ if err == sql.ErrNoRows {
+ c.Exec("insert into "+TableName+"(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", oldsid, "", time.Now().Unix())
+ }
+ c.Exec("update "+TableName+" set `session_key`=? where session_key=?", sid, oldsid)
+ var kv map[interface{}]interface{}
+ if len(sessiondata) == 0 {
+ kv = make(map[interface{}]interface{})
+ } else {
+ kv, err = session.DecodeGob(sessiondata)
+ if err != nil {
+ return nil, err
+ }
+ }
+ rs := &SessionStore{c: c, sid: sid, values: kv}
+ return rs, nil
+}
+
+// SessionDestroy delete mysql session by sid
+func (mp *Provider) SessionDestroy(sid string) error {
+ c := mp.connectInit()
+ c.Exec("DELETE FROM "+TableName+" where session_key=?", sid)
+ c.Close()
+ return nil
+}
+
+// SessionGC delete expired values in mysql session
+func (mp *Provider) SessionGC() {
+ c := mp.connectInit()
+ c.Exec("DELETE from "+TableName+" where session_expiry < ?", time.Now().Unix()-mp.maxlifetime)
+ c.Close()
+ return
+}
+
+// SessionAll count values in mysql session
+func (mp *Provider) SessionAll() int {
+ c := mp.connectInit()
+ defer c.Close()
+ var total int
+ err := c.QueryRow("SELECT count(*) as num from " + TableName).Scan(&total)
+ if err != nil {
+ return 0
+ }
+ return total
+}
+
+func init() {
+ session.Register("mysql", mysqlpder)
+}
diff --git a/src/vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go b/src/vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go
new file mode 100644
index 0000000000..73f9c13a94
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go
@@ -0,0 +1,248 @@
+// Copyright 2014 beego Author. 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 postgres for session provider
+//
+// depends on github.com/lib/pq:
+//
+// go install github.com/lib/pq
+//
+//
+// needs this table in your database:
+//
+// CREATE TABLE session (
+// session_key char(64) NOT NULL,
+// session_data bytea,
+// session_expiry timestamp NOT NULL,
+// CONSTRAINT session_key PRIMARY KEY(session_key)
+// );
+//
+// will be activated with these settings in app.conf:
+//
+// SessionOn = true
+// SessionProvider = postgresql
+// SessionSavePath = "user=a password=b dbname=c sslmode=disable"
+// SessionName = session
+//
+//
+// Usage:
+// import(
+// _ "github.com/astaxie/beego/session/postgresql"
+// "github.com/astaxie/beego/session"
+// )
+//
+// func init() {
+// globalSessions, _ = session.NewManager("postgresql", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"user=pqgotest dbname=pqgotest sslmode=verify-full"}``)
+// go globalSessions.GC()
+// }
+//
+// more docs: http://beego.me/docs/module/session.md
+package postgres
+
+import (
+ "database/sql"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/astaxie/beego/session"
+ // import postgresql Driver
+ _ "github.com/lib/pq"
+)
+
+var postgresqlpder = &Provider{}
+
+// SessionStore postgresql session store
+type SessionStore struct {
+ c *sql.DB
+ sid string
+ lock sync.RWMutex
+ values map[interface{}]interface{}
+}
+
+// Set value in postgresql session.
+// it is temp value in map.
+func (st *SessionStore) Set(key, value interface{}) error {
+ st.lock.Lock()
+ defer st.lock.Unlock()
+ st.values[key] = value
+ return nil
+}
+
+// Get value from postgresql session
+func (st *SessionStore) Get(key interface{}) interface{} {
+ st.lock.RLock()
+ defer st.lock.RUnlock()
+ if v, ok := st.values[key]; ok {
+ return v
+ }
+ return nil
+}
+
+// Delete value in postgresql session
+func (st *SessionStore) Delete(key interface{}) error {
+ st.lock.Lock()
+ defer st.lock.Unlock()
+ delete(st.values, key)
+ return nil
+}
+
+// Flush clear all values in postgresql session
+func (st *SessionStore) Flush() error {
+ st.lock.Lock()
+ defer st.lock.Unlock()
+ st.values = make(map[interface{}]interface{})
+ return nil
+}
+
+// SessionID get session id of this postgresql session store
+func (st *SessionStore) SessionID() string {
+ return st.sid
+}
+
+// SessionRelease save postgresql session values to database.
+// must call this method to save values to database.
+func (st *SessionStore) SessionRelease(w http.ResponseWriter) {
+ defer st.c.Close()
+ b, err := session.EncodeGob(st.values)
+ if err != nil {
+ return
+ }
+ st.c.Exec("UPDATE session set session_data=$1, session_expiry=$2 where session_key=$3",
+ b, time.Now().Format(time.RFC3339), st.sid)
+
+}
+
+// Provider postgresql session provider
+type Provider struct {
+ maxlifetime int64
+ savePath string
+}
+
+// connect to postgresql
+func (mp *Provider) connectInit() *sql.DB {
+ db, e := sql.Open("postgres", mp.savePath)
+ if e != nil {
+ return nil
+ }
+ return db
+}
+
+// SessionInit init postgresql session.
+// savepath is the connection string of postgresql.
+func (mp *Provider) SessionInit(maxlifetime int64, savePath string) error {
+ mp.maxlifetime = maxlifetime
+ mp.savePath = savePath
+ return nil
+}
+
+// SessionRead get postgresql session by sid
+func (mp *Provider) SessionRead(sid string) (session.Store, error) {
+ c := mp.connectInit()
+ row := c.QueryRow("select session_data from session where session_key=$1", sid)
+ var sessiondata []byte
+ err := row.Scan(&sessiondata)
+ if err == sql.ErrNoRows {
+ _, err = c.Exec("insert into session(session_key,session_data,session_expiry) values($1,$2,$3)",
+ sid, "", time.Now().Format(time.RFC3339))
+
+ if err != nil {
+ return nil, err
+ }
+ } else if err != nil {
+ return nil, err
+ }
+
+ var kv map[interface{}]interface{}
+ if len(sessiondata) == 0 {
+ kv = make(map[interface{}]interface{})
+ } else {
+ kv, err = session.DecodeGob(sessiondata)
+ if err != nil {
+ return nil, err
+ }
+ }
+ rs := &SessionStore{c: c, sid: sid, values: kv}
+ return rs, nil
+}
+
+// SessionExist check postgresql session exist
+func (mp *Provider) SessionExist(sid string) bool {
+ c := mp.connectInit()
+ defer c.Close()
+ row := c.QueryRow("select session_data from session where session_key=$1", sid)
+ var sessiondata []byte
+ err := row.Scan(&sessiondata)
+
+ if err == sql.ErrNoRows {
+ return false
+ }
+ return true
+}
+
+// SessionRegenerate generate new sid for postgresql session
+func (mp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+ c := mp.connectInit()
+ row := c.QueryRow("select session_data from session where session_key=$1", oldsid)
+ var sessiondata []byte
+ err := row.Scan(&sessiondata)
+ if err == sql.ErrNoRows {
+ c.Exec("insert into session(session_key,session_data,session_expiry) values($1,$2,$3)",
+ oldsid, "", time.Now().Format(time.RFC3339))
+ }
+ c.Exec("update session set session_key=$1 where session_key=$2", sid, oldsid)
+ var kv map[interface{}]interface{}
+ if len(sessiondata) == 0 {
+ kv = make(map[interface{}]interface{})
+ } else {
+ kv, err = session.DecodeGob(sessiondata)
+ if err != nil {
+ return nil, err
+ }
+ }
+ rs := &SessionStore{c: c, sid: sid, values: kv}
+ return rs, nil
+}
+
+// SessionDestroy delete postgresql session by sid
+func (mp *Provider) SessionDestroy(sid string) error {
+ c := mp.connectInit()
+ c.Exec("DELETE FROM session where session_key=$1", sid)
+ c.Close()
+ return nil
+}
+
+// SessionGC delete expired values in postgresql session
+func (mp *Provider) SessionGC() {
+ c := mp.connectInit()
+ c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime)
+ c.Close()
+ return
+}
+
+// SessionAll count values in postgresql session
+func (mp *Provider) SessionAll() int {
+ c := mp.connectInit()
+ defer c.Close()
+ var total int
+ err := c.QueryRow("SELECT count(*) as num from session").Scan(&total)
+ if err != nil {
+ return 0
+ }
+ return total
+}
+
+func init() {
+ session.Register("postgresql", postgresqlpder)
+}
diff --git a/src/vendor/github.com/astaxie/beego/session/sess_cookie_test.go b/src/vendor/github.com/astaxie/beego/session/sess_cookie_test.go
new file mode 100644
index 0000000000..209e501cab
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/session/sess_cookie_test.go
@@ -0,0 +1,96 @@
+// Copyright 2014 beego Author. 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 session
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+)
+
+func TestCookie(t *testing.T) {
+ config := `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`
+ globalSessions, err := NewManager("cookie", config)
+ if err != nil {
+ t.Fatal("init cookie session err", err)
+ }
+ r, _ := http.NewRequest("GET", "/", nil)
+ w := httptest.NewRecorder()
+ sess, err := globalSessions.SessionStart(w, r)
+ if err != nil {
+ t.Fatal("set error,", err)
+ }
+ err = sess.Set("username", "astaxie")
+ if err != nil {
+ t.Fatal("set error,", err)
+ }
+ if username := sess.Get("username"); username != "astaxie" {
+ t.Fatal("get username error")
+ }
+ sess.SessionRelease(w)
+ if cookiestr := w.Header().Get("Set-Cookie"); cookiestr == "" {
+ t.Fatal("setcookie error")
+ } else {
+ parts := strings.Split(strings.TrimSpace(cookiestr), ";")
+ for k, v := range parts {
+ nameval := strings.Split(v, "=")
+ if k == 0 && nameval[0] != "gosessionid" {
+ t.Fatal("error")
+ }
+ }
+ }
+}
+
+func TestDestorySessionCookie(t *testing.T) {
+ config := `{"cookieName":"gosessionid","enableSetCookie":true,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`
+ globalSessions, err := NewManager("cookie", config)
+ if err != nil {
+ t.Fatal("init cookie session err", err)
+ }
+
+ r, _ := http.NewRequest("GET", "/", nil)
+ w := httptest.NewRecorder()
+ session, err := globalSessions.SessionStart(w, r)
+ if err != nil {
+ t.Fatal("session start err,", err)
+ }
+
+ // request again ,will get same sesssion id .
+ r1, _ := http.NewRequest("GET", "/", nil)
+ r1.Header.Set("Cookie", w.Header().Get("Set-Cookie"))
+ w = httptest.NewRecorder()
+ newSession, err := globalSessions.SessionStart(w, r1)
+ if err != nil {
+ t.Fatal("session start err,", err)
+ }
+ if newSession.SessionID() != session.SessionID() {
+ t.Fatal("get cookie session id is not the same again.")
+ }
+
+ // After destroy session , will get a new session id .
+ globalSessions.SessionDestroy(w, r1)
+ r2, _ := http.NewRequest("GET", "/", nil)
+ r2.Header.Set("Cookie", w.Header().Get("Set-Cookie"))
+
+ w = httptest.NewRecorder()
+ newSession, err = globalSessions.SessionStart(w, r2)
+ if err != nil {
+ t.Fatal("session start error")
+ }
+ if newSession.SessionID() == session.SessionID() {
+ t.Fatal("after destroy session and reqeust again ,get cookie session id is same.")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/session/sess_mem_test.go b/src/vendor/github.com/astaxie/beego/session/sess_mem_test.go
new file mode 100644
index 0000000000..43f5b0a908
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/session/sess_mem_test.go
@@ -0,0 +1,52 @@
+// Copyright 2014 beego Author. 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 session
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+)
+
+func TestMem(t *testing.T) {
+ globalSessions, _ := NewManager("memory", `{"cookieName":"gosessionid","gclifetime":10}`)
+ go globalSessions.GC()
+ r, _ := http.NewRequest("GET", "/", nil)
+ w := httptest.NewRecorder()
+ sess, err := globalSessions.SessionStart(w, r)
+ if err != nil {
+ t.Fatal("set error,", err)
+ }
+ defer sess.SessionRelease(w)
+ err = sess.Set("username", "astaxie")
+ if err != nil {
+ t.Fatal("set error,", err)
+ }
+ if username := sess.Get("username"); username != "astaxie" {
+ t.Fatal("get username error")
+ }
+ if cookiestr := w.Header().Get("Set-Cookie"); cookiestr == "" {
+ t.Fatal("setcookie error")
+ } else {
+ parts := strings.Split(strings.TrimSpace(cookiestr), ";")
+ for k, v := range parts {
+ nameval := strings.Split(v, "=")
+ if k == 0 && nameval[0] != "gosessionid" {
+ t.Fatal("error")
+ }
+ }
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/session/sess_test.go b/src/vendor/github.com/astaxie/beego/session/sess_test.go
new file mode 100644
index 0000000000..5ba910f254
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/session/sess_test.go
@@ -0,0 +1,132 @@
+// Copyright 2014 beego Author. 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 session
+
+import (
+ "crypto/aes"
+ "encoding/json"
+ "testing"
+)
+
+func Test_gob(t *testing.T) {
+ a := make(map[interface{}]interface{})
+ a["username"] = "astaxie"
+ a[12] = 234
+ a["user"] = User{"asta", "xie"}
+ b, err := EncodeGob(a)
+ if err != nil {
+ t.Error(err)
+ }
+ c, err := DecodeGob(b)
+ if err != nil {
+ t.Error(err)
+ }
+ if len(c) == 0 {
+ t.Error("decodeGob empty")
+ }
+ if c["username"] != "astaxie" {
+ t.Error("decode string error")
+ }
+ if c[12] != 234 {
+ t.Error("decode int error")
+ }
+ if c["user"].(User).Username != "asta" {
+ t.Error("decode struct error")
+ }
+}
+
+type User struct {
+ Username string
+ NickName string
+}
+
+func TestGenerate(t *testing.T) {
+ str := generateRandomKey(20)
+ if len(str) != 20 {
+ t.Fatal("generate length is not equal to 20")
+ }
+}
+
+func TestCookieEncodeDecode(t *testing.T) {
+ hashKey := "testhashKey"
+ blockkey := generateRandomKey(16)
+ block, err := aes.NewCipher(blockkey)
+ if err != nil {
+ t.Fatal("NewCipher:", err)
+ }
+ securityName := string(generateRandomKey(20))
+ val := make(map[interface{}]interface{})
+ val["name"] = "astaxie"
+ val["gender"] = "male"
+ str, err := encodeCookie(block, hashKey, securityName, val)
+ if err != nil {
+ t.Fatal("encodeCookie:", err)
+ }
+ dst := make(map[interface{}]interface{})
+ dst, err = decodeCookie(block, hashKey, securityName, str, 3600)
+ if err != nil {
+ t.Fatal("decodeCookie", err)
+ }
+ if dst["name"] != "astaxie" {
+ t.Fatal("dst get map error")
+ }
+ if dst["gender"] != "male" {
+ t.Fatal("dst get map error")
+ }
+}
+
+func TestParseConfig(t *testing.T) {
+ s := `{"cookieName":"gosessionid","gclifetime":3600}`
+ cf := new(managerConfig)
+ cf.EnableSetCookie = true
+ err := json.Unmarshal([]byte(s), cf)
+ if err != nil {
+ t.Fatal("parse json error,", err)
+ }
+ if cf.CookieName != "gosessionid" {
+ t.Fatal("parseconfig get cookiename error")
+ }
+ if cf.Gclifetime != 3600 {
+ t.Fatal("parseconfig get gclifetime error")
+ }
+
+ cc := `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`
+ cf2 := new(managerConfig)
+ cf2.EnableSetCookie = true
+ err = json.Unmarshal([]byte(cc), cf2)
+ if err != nil {
+ t.Fatal("parse json error,", err)
+ }
+ if cf2.CookieName != "gosessionid" {
+ t.Fatal("parseconfig get cookiename error")
+ }
+ if cf2.Gclifetime != 3600 {
+ t.Fatal("parseconfig get gclifetime error")
+ }
+ if cf2.EnableSetCookie != false {
+ t.Fatal("parseconfig get enableSetCookie error")
+ }
+ cconfig := new(cookieConfig)
+ err = json.Unmarshal([]byte(cf2.ProviderConfig), cconfig)
+ if err != nil {
+ t.Fatal("parse ProviderConfig err,", err)
+ }
+ if cconfig.CookieName != "gosessionid" {
+ t.Fatal("ProviderConfig get cookieName error")
+ }
+ if cconfig.SecurityKey != "beegocookiehashkey" {
+ t.Fatal("ProviderConfig get securityKey error")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/staticfile_test.go b/src/vendor/github.com/astaxie/beego/staticfile_test.go
new file mode 100644
index 0000000000..a043b4fd36
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/staticfile_test.go
@@ -0,0 +1,73 @@
+package beego
+
+import (
+ "bytes"
+ "compress/gzip"
+ "compress/zlib"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+var currentWorkDir, _ = os.Getwd()
+var licenseFile = filepath.Join(currentWorkDir, "LICENSE")
+
+func testOpenFile(encoding string, content []byte, t *testing.T) {
+ fi, _ := os.Stat(licenseFile)
+ b, n, sch, err := openFile(licenseFile, fi, encoding)
+ if err != nil {
+ t.Log(err)
+ t.Fail()
+ }
+
+ t.Log("open static file encoding "+n, b)
+
+ assetOpenFileAndContent(sch, content, t)
+}
+func TestOpenStaticFile_1(t *testing.T) {
+ file, _ := os.Open(licenseFile)
+ content, _ := ioutil.ReadAll(file)
+ testOpenFile("", content, t)
+}
+
+func TestOpenStaticFileGzip_1(t *testing.T) {
+ file, _ := os.Open(licenseFile)
+ var zipBuf bytes.Buffer
+ fileWriter, _ := gzip.NewWriterLevel(&zipBuf, gzip.BestCompression)
+ io.Copy(fileWriter, file)
+ fileWriter.Close()
+ content, _ := ioutil.ReadAll(&zipBuf)
+
+ testOpenFile("gzip", content, t)
+}
+func TestOpenStaticFileDeflate_1(t *testing.T) {
+ file, _ := os.Open(licenseFile)
+ var zipBuf bytes.Buffer
+ fileWriter, _ := zlib.NewWriterLevel(&zipBuf, zlib.BestCompression)
+ io.Copy(fileWriter, file)
+ fileWriter.Close()
+ content, _ := ioutil.ReadAll(&zipBuf)
+
+ testOpenFile("deflate", content, t)
+}
+
+func assetOpenFileAndContent(sch *serveContentHolder, content []byte, t *testing.T) {
+ t.Log(sch.size, len(content))
+ if sch.size != int64(len(content)) {
+ t.Log("static content file size not same")
+ t.Fail()
+ }
+ bs, _ := ioutil.ReadAll(sch)
+ for i, v := range content {
+ if v != bs[i] {
+ t.Log("content not same")
+ t.Fail()
+ }
+ }
+ if len(staticFileMap) == 0 {
+ t.Log("men map is empty")
+ t.Fail()
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/swagger/docs_spec.go b/src/vendor/github.com/astaxie/beego/swagger/docs_spec.go
new file mode 100644
index 0000000000..680324dc0a
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/swagger/docs_spec.go
@@ -0,0 +1,160 @@
+// Copyright 2014 beego Author. 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 swagger struct definition
+package swagger
+
+// SwaggerVersion show the current swagger version
+const SwaggerVersion = "1.2"
+
+// ResourceListing list the resource
+type ResourceListing struct {
+ APIVersion string `json:"apiVersion"`
+ SwaggerVersion string `json:"swaggerVersion"` // e.g 1.2
+ // BasePath string `json:"basePath"` obsolete in 1.1
+ APIs []APIRef `json:"apis"`
+ Info Information `json:"info"`
+}
+
+// APIRef description the api path and description
+type APIRef struct {
+ Path string `json:"path"` // relative or absolute, must start with /
+ Description string `json:"description"`
+}
+
+// Information show the API Information
+type Information struct {
+ Title string `json:"title,omitempty"`
+ Description string `json:"description,omitempty"`
+ Contact string `json:"contact,omitempty"`
+ TermsOfServiceURL string `json:"termsOfServiceUrl,omitempty"`
+ License string `json:"license,omitempty"`
+ LicenseURL string `json:"licenseUrl,omitempty"`
+}
+
+// APIDeclaration see https://github.com/wordnik/swagger-core/blob/scala_2.10-1.3-RC3/schemas/api-declaration-schema.json
+type APIDeclaration struct {
+ APIVersion string `json:"apiVersion"`
+ SwaggerVersion string `json:"swaggerVersion"`
+ BasePath string `json:"basePath"`
+ ResourcePath string `json:"resourcePath"` // must start with /
+ Consumes []string `json:"consumes,omitempty"`
+ Produces []string `json:"produces,omitempty"`
+ APIs []API `json:"apis,omitempty"`
+ Models map[string]Model `json:"models,omitempty"`
+}
+
+// API show tha API struct
+type API struct {
+ Path string `json:"path"` // relative or absolute, must start with /
+ Description string `json:"description"`
+ Operations []Operation `json:"operations,omitempty"`
+}
+
+// Operation desc the Operation
+type Operation struct {
+ HTTPMethod string `json:"httpMethod"`
+ Nickname string `json:"nickname"`
+ Type string `json:"type"` // in 1.1 = DataType
+ // ResponseClass string `json:"responseClass"` obsolete in 1.2
+ Summary string `json:"summary,omitempty"`
+ Notes string `json:"notes,omitempty"`
+ Parameters []Parameter `json:"parameters,omitempty"`
+ ResponseMessages []ResponseMessage `json:"responseMessages,omitempty"` // optional
+ Consumes []string `json:"consumes,omitempty"`
+ Produces []string `json:"produces,omitempty"`
+ Authorizations []Authorization `json:"authorizations,omitempty"`
+ Protocols []Protocol `json:"protocols,omitempty"`
+}
+
+// Protocol support which Protocol
+type Protocol struct {
+}
+
+// ResponseMessage Show the
+type ResponseMessage struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+ ResponseModel string `json:"responseModel"`
+}
+
+// Parameter desc the request parameters
+type Parameter struct {
+ ParamType string `json:"paramType"` // path,query,body,header,form
+ Name string `json:"name"`
+ Description string `json:"description"`
+ DataType string `json:"dataType"` // 1.2 needed?
+ Type string `json:"type"` // integer
+ Format string `json:"format"` // int64
+ AllowMultiple bool `json:"allowMultiple"`
+ Required bool `json:"required"`
+ Minimum int `json:"minimum"`
+ Maximum int `json:"maximum"`
+}
+
+// ErrorResponse desc response
+type ErrorResponse struct {
+ Code int `json:"code"`
+ Reason string `json:"reason"`
+}
+
+// Model define the data model
+type Model struct {
+ ID string `json:"id"`
+ Required []string `json:"required,omitempty"`
+ Properties map[string]ModelProperty `json:"properties"`
+}
+
+// ModelProperty define the properties
+type ModelProperty struct {
+ Type string `json:"type"`
+ Description string `json:"description"`
+ Items map[string]string `json:"items,omitempty"`
+ Format string `json:"format"`
+}
+
+// Authorization see https://github.com/wordnik/swagger-core/wiki/authorizations
+type Authorization struct {
+ LocalOAuth OAuth `json:"local-oauth"`
+ APIKey APIKey `json:"apiKey"`
+}
+
+// OAuth see https://github.com/wordnik/swagger-core/wiki/authorizations
+type OAuth struct {
+ Type string `json:"type"` // e.g. oauth2
+ Scopes []string `json:"scopes"` // e.g. PUBLIC
+ GrantTypes map[string]GrantType `json:"grantTypes"`
+}
+
+// GrantType see https://github.com/wordnik/swagger-core/wiki/authorizations
+type GrantType struct {
+ LoginEndpoint Endpoint `json:"loginEndpoint"`
+ TokenName string `json:"tokenName"` // e.g. access_code
+ TokenRequestEndpoint Endpoint `json:"tokenRequestEndpoint"`
+ TokenEndpoint Endpoint `json:"tokenEndpoint"`
+}
+
+// Endpoint see https://github.com/wordnik/swagger-core/wiki/authorizations
+type Endpoint struct {
+ URL string `json:"url"`
+ ClientIDName string `json:"clientIdName"`
+ ClientSecretName string `json:"clientSecretName"`
+ TokenName string `json:"tokenName"`
+}
+
+// APIKey see https://github.com/wordnik/swagger-core/wiki/authorizations
+type APIKey struct {
+ Type string `json:"type"` // e.g. apiKey
+ PassAs string `json:"passAs"` // e.g. header
+}
diff --git a/src/vendor/github.com/astaxie/beego/template_test.go b/src/vendor/github.com/astaxie/beego/template_test.go
new file mode 100644
index 0000000000..4f13736c49
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/template_test.go
@@ -0,0 +1,136 @@
+// Copyright 2014 beego Author. 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 beego
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+var header = `{{define "header"}}
+Hello, astaxie!
+{{end}}`
+
+var index = `
+
+
+ beego welcome template
+
+
+{{template "block"}}
+{{template "header"}}
+{{template "blocks/block.tpl"}}
+
+
+`
+
+var block = `{{define "block"}}
+Hello, blocks!
+{{end}}`
+
+func TestTemplate(t *testing.T) {
+ dir := "_beeTmp"
+ files := []string{
+ "header.tpl",
+ "index.tpl",
+ "blocks/block.tpl",
+ }
+ if err := os.MkdirAll(dir, 0777); err != nil {
+ t.Fatal(err)
+ }
+ for k, name := range files {
+ os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777)
+ if f, err := os.Create(filepath.Join(dir, name)); err != nil {
+ t.Fatal(err)
+ } else {
+ if k == 0 {
+ f.WriteString(header)
+ } else if k == 1 {
+ f.WriteString(index)
+ } else if k == 2 {
+ f.WriteString(block)
+ }
+
+ f.Close()
+ }
+ }
+ if err := BuildTemplate(dir); err != nil {
+ t.Fatal(err)
+ }
+ if len(beeTemplates) != 3 {
+ t.Fatalf("should be 3 but got %v", len(beeTemplates))
+ }
+ if err := beeTemplates["index.tpl"].ExecuteTemplate(os.Stdout, "index.tpl", nil); err != nil {
+ t.Fatal(err)
+ }
+ for _, name := range files {
+ os.RemoveAll(filepath.Join(dir, name))
+ }
+ os.RemoveAll(dir)
+}
+
+var menu = `
+`
+var user = `
+
+
+ beego welcome template
+
+
+{{template "../public/menu.tpl"}}
+
+
+`
+
+func TestRelativeTemplate(t *testing.T) {
+ dir := "_beeTmp"
+ files := []string{
+ "easyui/public/menu.tpl",
+ "easyui/rbac/user.tpl",
+ }
+ if err := os.MkdirAll(dir, 0777); err != nil {
+ t.Fatal(err)
+ }
+ for k, name := range files {
+ os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777)
+ if f, err := os.Create(filepath.Join(dir, name)); err != nil {
+ t.Fatal(err)
+ } else {
+ if k == 0 {
+ f.WriteString(menu)
+ } else if k == 1 {
+ f.WriteString(user)
+ }
+ f.Close()
+ }
+ }
+ if err := BuildTemplate(dir, files[1]); err != nil {
+ t.Fatal(err)
+ }
+ if err := beeTemplates["easyui/rbac/user.tpl"].ExecuteTemplate(os.Stdout, "easyui/rbac/user.tpl", nil); err != nil {
+ t.Fatal(err)
+ }
+ for _, name := range files {
+ os.RemoveAll(filepath.Join(dir, name))
+ }
+ os.RemoveAll(dir)
+}
diff --git a/src/vendor/github.com/astaxie/beego/templatefunc_test.go b/src/vendor/github.com/astaxie/beego/templatefunc_test.go
new file mode 100644
index 0000000000..98fbf7ab7d
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/templatefunc_test.go
@@ -0,0 +1,323 @@
+// Copyright 2014 beego Author. 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 beego
+
+import (
+ "html/template"
+ "net/url"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestSubstr(t *testing.T) {
+ s := `012345`
+ if Substr(s, 0, 2) != "01" {
+ t.Error("should be equal")
+ }
+ if Substr(s, 0, 100) != "012345" {
+ t.Error("should be equal")
+ }
+ if Substr(s, 12, 100) != "012345" {
+ t.Error("should be equal")
+ }
+}
+
+func TestHtml2str(t *testing.T) {
+ h := `<123> 123\n
+
+
+ \n`
+ if HTML2str(h) != "123\\n\n\\n" {
+ t.Error("should be equal")
+ }
+}
+
+func TestDateFormat(t *testing.T) {
+ ts := "Mon, 01 Jul 2013 13:27:42 CST"
+ tt, _ := time.Parse(time.RFC1123, ts)
+
+ if ss := DateFormat(tt, "2006-01-02 15:04:05"); ss != "2013-07-01 13:27:42" {
+ t.Errorf("2013-07-01 13:27:42 does not equal %v", ss)
+ }
+}
+
+func TestDate(t *testing.T) {
+ ts := "Mon, 01 Jul 2013 13:27:42 CST"
+ tt, _ := time.Parse(time.RFC1123, ts)
+
+ if ss := Date(tt, "Y-m-d H:i:s"); ss != "2013-07-01 13:27:42" {
+ t.Errorf("2013-07-01 13:27:42 does not equal %v", ss)
+ }
+ if ss := Date(tt, "y-n-j h:i:s A"); ss != "13-7-1 01:27:42 PM" {
+ t.Errorf("13-7-1 01:27:42 PM does not equal %v", ss)
+ }
+ if ss := Date(tt, "D, d M Y g:i:s a"); ss != "Mon, 01 Jul 2013 1:27:42 pm" {
+ t.Errorf("Mon, 01 Jul 2013 1:27:42 pm does not equal %v", ss)
+ }
+ if ss := Date(tt, "l, d F Y G:i:s"); ss != "Monday, 01 July 2013 13:27:42" {
+ t.Errorf("Monday, 01 July 2013 13:27:42 does not equal %v", ss)
+ }
+}
+
+func TestCompareRelated(t *testing.T) {
+ if !Compare("abc", "abc") {
+ t.Error("should be equal")
+ }
+ if Compare("abc", "aBc") {
+ t.Error("should be not equal")
+ }
+ if !Compare("1", 1) {
+ t.Error("should be equal")
+ }
+ if CompareNot("abc", "abc") {
+ t.Error("should be equal")
+ }
+ if !CompareNot("abc", "aBc") {
+ t.Error("should be not equal")
+ }
+ if !NotNil("a string") {
+ t.Error("should not be nil")
+ }
+}
+
+func TestHtmlquote(t *testing.T) {
+ h := `<' ”“&">`
+ s := `<' ”“&">`
+ if Htmlquote(s) != h {
+ t.Error("should be equal")
+ }
+}
+
+func TestHtmlunquote(t *testing.T) {
+ h := `<' ”“&">`
+ s := `<' ”“&">`
+ if Htmlunquote(h) != s {
+ t.Error("should be equal")
+ }
+}
+
+func TestParseForm(t *testing.T) {
+ type user struct {
+ ID int `form:"-"`
+ tag string `form:"tag"`
+ Name interface{} `form:"username"`
+ Age int `form:"age,text"`
+ Email string
+ Intro string `form:",textarea"`
+ StrBool bool `form:"strbool"`
+ Date time.Time `form:"date,2006-01-02"`
+ }
+
+ u := user{}
+ form := url.Values{
+ "ID": []string{"1"},
+ "-": []string{"1"},
+ "tag": []string{"no"},
+ "username": []string{"test"},
+ "age": []string{"40"},
+ "Email": []string{"test@gmail.com"},
+ "Intro": []string{"I am an engineer!"},
+ "strbool": []string{"yes"},
+ "date": []string{"2014-11-12"},
+ }
+ if err := ParseForm(form, u); err == nil {
+ t.Fatal("nothing will be changed")
+ }
+ if err := ParseForm(form, &u); err != nil {
+ t.Fatal(err)
+ }
+ if u.ID != 0 {
+ t.Errorf("ID should equal 0 but got %v", u.ID)
+ }
+ if len(u.tag) != 0 {
+ t.Errorf("tag's length should equal 0 but got %v", len(u.tag))
+ }
+ if u.Name.(string) != "test" {
+ t.Errorf("Name should equal `test` but got `%v`", u.Name.(string))
+ }
+ if u.Age != 40 {
+ t.Errorf("Age should equal 40 but got %v", u.Age)
+ }
+ if u.Email != "test@gmail.com" {
+ t.Errorf("Email should equal `test@gmail.com` but got `%v`", u.Email)
+ }
+ if u.Intro != "I am an engineer!" {
+ t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro)
+ }
+ if u.StrBool != true {
+ t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool)
+ }
+ y, m, d := u.Date.Date()
+ if y != 2014 || m.String() != "November" || d != 12 {
+ t.Errorf("Date should equal `2014-11-12`, but got `%v`", u.Date.String())
+ }
+}
+
+func TestRenderForm(t *testing.T) {
+ type user struct {
+ ID int `form:"-"`
+ tag string `form:"tag"`
+ Name interface{} `form:"username"`
+ Age int `form:"age,text,年龄:"`
+ Sex string
+ Email []string
+ Intro string `form:",textarea"`
+ Ignored string `form:"-"`
+ }
+
+ u := user{Name: "test", Intro: "Some Text"}
+ output := RenderForm(u)
+ if output != template.HTML("") {
+ t.Errorf("output should be empty but got %v", output)
+ }
+ output = RenderForm(&u)
+ result := template.HTML(
+ `Name: ` +
+ `年龄: ` +
+ `Sex: ` +
+ `Intro: `)
+ if output != result {
+ t.Errorf("output should equal `%v` but got `%v`", result, output)
+ }
+}
+
+func TestRenderFormField(t *testing.T) {
+ html := renderFormField("Label: ", "Name", "text", "Value", "", "")
+ if html != `Label: ` {
+ t.Errorf("Wrong html output for input[type=text]: %v ", html)
+ }
+
+ html = renderFormField("Label: ", "Name", "textarea", "Value", "", "")
+ if html != `Label: ` {
+ t.Errorf("Wrong html output for textarea: %v ", html)
+ }
+}
+
+func TestParseFormTag(t *testing.T) {
+ // create struct to contain field with different types of struct-tag `form`
+ type user struct {
+ All int `form:"name,text,年龄:"`
+ NoName int `form:",hidden,年龄:"`
+ OnlyLabel int `form:",,年龄:"`
+ OnlyName int `form:"name" id:"name" class:"form-name"`
+ Ignored int `form:"-"`
+ }
+
+ objT := reflect.TypeOf(&user{}).Elem()
+
+ label, name, fType, id, class, ignored := parseFormTag(objT.Field(0))
+ if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) {
+ t.Errorf("Form Tag with name, label and type was not correctly parsed.")
+ }
+
+ label, name, fType, id, class, ignored = parseFormTag(objT.Field(1))
+ if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) {
+ t.Errorf("Form Tag with label and type but without name was not correctly parsed.")
+ }
+
+ label, name, fType, id, class, ignored = parseFormTag(objT.Field(2))
+ if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) {
+ t.Errorf("Form Tag containing only label was not correctly parsed.")
+ }
+
+ label, name, fType, id, class, ignored = parseFormTag(objT.Field(3))
+ if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false &&
+ id == "name" && class == "form-name") {
+ t.Errorf("Form Tag containing only name was not correctly parsed.")
+ }
+
+ label, name, fType, id, class, ignored = parseFormTag(objT.Field(4))
+ if ignored == false {
+ t.Errorf("Form Tag that should be ignored was not correctly parsed.")
+ }
+}
+
+func TestMapGet(t *testing.T) {
+ // test one level map
+ m1 := map[string]int64{
+ "a": 1,
+ "1": 2,
+ }
+
+ if res, err := MapGet(m1, "a"); err == nil {
+ if res.(int64) != 1 {
+ t.Errorf("Should return 1, but return %v", res)
+ }
+ } else {
+ t.Errorf("Error happens %v", err)
+ }
+
+ if res, err := MapGet(m1, "1"); err == nil {
+ if res.(int64) != 2 {
+ t.Errorf("Should return 2, but return %v", res)
+ }
+ } else {
+ t.Errorf("Error happens %v", err)
+ }
+
+ if res, err := MapGet(m1, 1); err == nil {
+ if res.(int64) != 2 {
+ t.Errorf("Should return 2, but return %v", res)
+ }
+ } else {
+ t.Errorf("Error happens %v", err)
+ }
+
+ // test 2 level map
+ m2 := map[string]interface{}{
+ "1": map[string]float64{
+ "2": 3.5,
+ },
+ }
+
+ if res, err := MapGet(m2, 1, 2); err == nil {
+ if res.(float64) != 3.5 {
+ t.Errorf("Should return 3.5, but return %v", res)
+ }
+ } else {
+ t.Errorf("Error happens %v", err)
+ }
+
+ // test 5 level map
+ m5 := map[string]interface{}{
+ "1": map[string]interface{}{
+ "2": map[string]interface{}{
+ "3": map[string]interface{}{
+ "4": map[string]interface{}{
+ "5": 1.2,
+ },
+ },
+ },
+ },
+ }
+
+ if res, err := MapGet(m5, 1, 2, 3, 4, 5); err == nil {
+ if res.(float64) != 1.2 {
+ t.Errorf("Should return 1.2, but return %v", res)
+ }
+ } else {
+ t.Errorf("Error happens %v", err)
+ }
+
+ // check whether element not exists in map
+ if res, err := MapGet(m5, 5, 4, 3, 2, 1); err == nil {
+ if res != nil {
+ t.Errorf("Should return nil, but return %v", res)
+ }
+ } else {
+ t.Errorf("Error happens %v", err)
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/testing/assertions.go b/src/vendor/github.com/astaxie/beego/testing/assertions.go
new file mode 100644
index 0000000000..96c5d4ddc9
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/testing/assertions.go
@@ -0,0 +1,15 @@
+// Copyright 2014 beego Author. 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 testing
diff --git a/src/vendor/github.com/astaxie/beego/testing/client.go b/src/vendor/github.com/astaxie/beego/testing/client.go
new file mode 100644
index 0000000000..c3737e9c64
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/testing/client.go
@@ -0,0 +1,65 @@
+// Copyright 2014 beego Author. 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 testing
+
+import (
+ "github.com/astaxie/beego/config"
+ "github.com/astaxie/beego/httplib"
+)
+
+var port = ""
+var baseURL = "http://localhost:"
+
+// TestHTTPRequest beego test request client
+type TestHTTPRequest struct {
+ httplib.BeegoHTTPRequest
+}
+
+func getPort() string {
+ if port == "" {
+ config, err := config.NewConfig("ini", "../conf/app.conf")
+ if err != nil {
+ return "8080"
+ }
+ port = config.String("httpport")
+ return port
+ }
+ return port
+}
+
+// Get returns test client in GET method
+func Get(path string) *TestHTTPRequest {
+ return &TestHTTPRequest{*httplib.Get(baseURL + getPort() + path)}
+}
+
+// Post returns test client in POST method
+func Post(path string) *TestHTTPRequest {
+ return &TestHTTPRequest{*httplib.Post(baseURL + getPort() + path)}
+}
+
+// Put returns test client in PUT method
+func Put(path string) *TestHTTPRequest {
+ return &TestHTTPRequest{*httplib.Put(baseURL + getPort() + path)}
+}
+
+// Delete returns test client in DELETE method
+func Delete(path string) *TestHTTPRequest {
+ return &TestHTTPRequest{*httplib.Delete(baseURL + getPort() + path)}
+}
+
+// Head returns test client in HEAD method
+func Head(path string) *TestHTTPRequest {
+ return &TestHTTPRequest{*httplib.Head(baseURL + getPort() + path)}
+}
diff --git a/src/vendor/github.com/astaxie/beego/toolbox/profile_test.go b/src/vendor/github.com/astaxie/beego/toolbox/profile_test.go
new file mode 100644
index 0000000000..07a20c4eea
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/toolbox/profile_test.go
@@ -0,0 +1,28 @@
+// Copyright 2014 beego Author. 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 toolbox
+
+import (
+ "os"
+ "testing"
+)
+
+func TestProcessInput(t *testing.T) {
+ ProcessInput("lookup goroutine", os.Stdout)
+ ProcessInput("lookup heap", os.Stdout)
+ ProcessInput("lookup threadcreate", os.Stdout)
+ ProcessInput("lookup block", os.Stdout)
+ ProcessInput("gc summary", os.Stdout)
+}
diff --git a/src/vendor/github.com/astaxie/beego/toolbox/statistics_test.go b/src/vendor/github.com/astaxie/beego/toolbox/statistics_test.go
new file mode 100644
index 0000000000..ac29476c0a
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/toolbox/statistics_test.go
@@ -0,0 +1,40 @@
+// Copyright 2014 beego Author. 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 toolbox
+
+import (
+ "encoding/json"
+ "testing"
+ "time"
+)
+
+func TestStatics(t *testing.T) {
+ StatisticsMap.AddStatistics("POST", "/api/user", "&admin.user", time.Duration(2000))
+ StatisticsMap.AddStatistics("POST", "/api/user", "&admin.user", time.Duration(120000))
+ StatisticsMap.AddStatistics("GET", "/api/user", "&admin.user", time.Duration(13000))
+ StatisticsMap.AddStatistics("POST", "/api/admin", "&admin.user", time.Duration(14000))
+ StatisticsMap.AddStatistics("POST", "/api/user/astaxie", "&admin.user", time.Duration(12000))
+ StatisticsMap.AddStatistics("POST", "/api/user/xiemengjun", "&admin.user", time.Duration(13000))
+ StatisticsMap.AddStatistics("DELETE", "/api/user", "&admin.user", time.Duration(1400))
+ t.Log(StatisticsMap.GetMap())
+
+ data := StatisticsMap.GetMapData()
+ b, err := json.Marshal(data)
+ if err != nil {
+ t.Errorf(err.Error())
+ }
+
+ t.Log(string(b))
+}
diff --git a/src/vendor/github.com/astaxie/beego/toolbox/task_test.go b/src/vendor/github.com/astaxie/beego/toolbox/task_test.go
new file mode 100644
index 0000000000..596bc9c5b0
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/toolbox/task_test.go
@@ -0,0 +1,63 @@
+// Copyright 2014 beego Author. 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 toolbox
+
+import (
+ "fmt"
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestParse(t *testing.T) {
+ tk := NewTask("taska", "0/30 * * * * *", func() error { fmt.Println("hello world"); return nil })
+ err := tk.Run()
+ if err != nil {
+ t.Fatal(err)
+ }
+ AddTask("taska", tk)
+ StartTask()
+ time.Sleep(6 * time.Second)
+ StopTask()
+}
+
+func TestSpec(t *testing.T) {
+ wg := &sync.WaitGroup{}
+ wg.Add(2)
+ tk1 := NewTask("tk1", "0 12 * * * *", func() error { fmt.Println("tk1"); return nil })
+ tk2 := NewTask("tk2", "0,10,20 * * * * *", func() error { fmt.Println("tk2"); wg.Done(); return nil })
+ tk3 := NewTask("tk3", "0 10 * * * *", func() error { fmt.Println("tk3"); wg.Done(); return nil })
+
+ AddTask("tk1", tk1)
+ AddTask("tk2", tk2)
+ AddTask("tk3", tk3)
+ StartTask()
+ defer StopTask()
+
+ select {
+ case <-time.After(200 * time.Second):
+ t.FailNow()
+ case <-wait(wg):
+ }
+}
+
+func wait(wg *sync.WaitGroup) chan bool {
+ ch := make(chan bool)
+ go func() {
+ wg.Wait()
+ ch <- true
+ }()
+ return ch
+}
diff --git a/src/vendor/github.com/astaxie/beego/tree_test.go b/src/vendor/github.com/astaxie/beego/tree_test.go
new file mode 100644
index 0000000000..81ff7eddd6
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/tree_test.go
@@ -0,0 +1,306 @@
+// Copyright 2014 beego Author. 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 beego
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/astaxie/beego/context"
+)
+
+type testinfo struct {
+ url string
+ requesturl string
+ params map[string]string
+}
+
+var routers []testinfo
+
+func init() {
+ routers = make([]testinfo, 0)
+ routers = append(routers, testinfo{"/topic/?:auth:int", "/topic", nil})
+ routers = append(routers, testinfo{"/topic/?:auth:int", "/topic/123", map[string]string{":auth": "123"}})
+ routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1", map[string]string{":id": "1"}})
+ routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1", ":auth": "2"}})
+ routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1", map[string]string{":id": "1"}})
+ routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1", ":auth": "123"}})
+ routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}})
+ routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}})
+ routers = append(routers, testinfo{"/", "/", nil})
+ routers = append(routers, testinfo{"/customer/login", "/customer/login", nil})
+ routers = append(routers, testinfo{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}})
+ routers = append(routers, testinfo{"/*", "/customer/123", map[string]string{":splat": "customer/123"}})
+ routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}})
+ routers = append(routers, testinfo{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}})
+ routers = append(routers, testinfo{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}})
+ routers = append(routers, testinfo{"/cc/:id/*", "/cc/2009/11/dd", map[string]string{":id": "2009", ":splat": "11/dd"}})
+ routers = append(routers, testinfo{"/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}})
+ routers = append(routers, testinfo{"/thumbnail/:size/uploads/*",
+ "/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg",
+ map[string]string{":size": "100x100", ":splat": "items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg"}})
+ routers = append(routers, testinfo{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}})
+ routers = append(routers, testinfo{"/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
+ routers = append(routers, testinfo{"/:name/test/*.*", "/nice/test/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
+ routers = append(routers, testinfo{"/dl/:width:int/:height:int/*.*",
+ "/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg",
+ map[string]string{":width": "48", ":height": "48", ":ext": "jpg", ":path": "05ac66d9bda00a3acf948c43e306fc9a"}})
+ routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}})
+ routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(a)", map[string]string{":id": "123"}})
+ routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(b)", map[string]string{":id": "123"}})
+ routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(c)", map[string]string{":id": "123"}})
+ routers = append(routers, testinfo{"/:year:int/:month:int/:id/:endid", "/1111/111/aaa/aaa", map[string]string{":year": "1111", ":month": "111", ":id": "aaa", ":endid": "aaa"}})
+ routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}})
+ routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}})
+ routers = append(routers, testinfo{"/v1/shop/:name:string", "/v1/shop/nike", map[string]string{":name": "nike"}})
+ routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)", "/v1/shop//123", map[string]string{":id": "123"}})
+ routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)_:name", "/v1/shop/123_nike", map[string]string{":id": "123", ":name": "nike"}})
+ routers = append(routers, testinfo{"/v1/shop/:id(.+)_cms.html", "/v1/shop/123_cms.html", map[string]string{":id": "123"}})
+ routers = append(routers, testinfo{"/v1/shop/cms_:id(.+)_:page(.+).html", "/v1/shop/cms_123_1.html", map[string]string{":id": "123", ":page": "1"}})
+ routers = append(routers, testinfo{"/v1/:v/cms/aaa_:id(.+)_:page(.+).html", "/v1/2/cms/aaa_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
+ routers = append(routers, testinfo{"/v1/:v/cms_:id(.+)_:page(.+).html", "/v1/2/cms_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
+ routers = append(routers, testinfo{"/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", "/v1/2_cms/ttt_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
+ routers = append(routers, testinfo{"/api/projects/:pid/members/?:mid", "/api/projects/1/members", map[string]string{":pid": "1"}})
+ routers = append(routers, testinfo{"/api/projects/:pid/members/?:mid", "/api/projects/1/members/2", map[string]string{":pid": "1", ":mid": "2"}})
+}
+
+func TestTreeRouters(t *testing.T) {
+ for _, r := range routers {
+ tr := NewTree()
+ tr.AddRouter(r.url, "astaxie")
+ ctx := context.NewContext()
+ obj := tr.Match(r.requesturl, ctx)
+ if obj == nil || obj.(string) != "astaxie" {
+ t.Fatal(r.url+" can't get obj, Expect ", r.requesturl)
+ }
+ if r.params != nil {
+ for k, v := range r.params {
+ if vv := ctx.Input.Param(k); vv != v {
+ t.Fatal("The Rule: " + r.url + "\nThe RequestURL:" + r.requesturl + "\nThe Key is " + k + ", The Value should be: " + v + ", but get: " + vv)
+ } else if vv == "" && v != "" {
+ t.Fatal(r.url + " " + r.requesturl + " get param empty:" + k)
+ }
+ }
+ }
+ }
+}
+
+func TestStaticPath(t *testing.T) {
+ tr := NewTree()
+ tr.AddRouter("/topic/:id", "wildcard")
+ tr.AddRouter("/topic", "static")
+ ctx := context.NewContext()
+ obj := tr.Match("/topic", ctx)
+ if obj == nil || obj.(string) != "static" {
+ t.Fatal("/topic is a static route")
+ }
+ obj = tr.Match("/topic/1", ctx)
+ if obj == nil || obj.(string) != "wildcard" {
+ t.Fatal("/topic/1 is a wildcard route")
+ }
+}
+
+func TestAddTree(t *testing.T) {
+ tr := NewTree()
+ tr.AddRouter("/shop/:id/account", "astaxie")
+ tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie")
+ t1 := NewTree()
+ t1.AddTree("/v1/zl", tr)
+ ctx := context.NewContext()
+ obj := t1.Match("/v1/zl/shop/123/account", ctx)
+ if obj == nil || obj.(string) != "astaxie" {
+ t.Fatal("/v1/zl/shop/:id/account can't get obj ")
+ }
+ if ctx.Input.ParamsLen() == 0 {
+ t.Fatal("get param error")
+ }
+ if ctx.Input.Param(":id") != "123" {
+ t.Fatal("get :id param error")
+ }
+ ctx.Input.Reset(ctx)
+ obj = t1.Match("/v1/zl/shop/123/ttt_1_12.html", ctx)
+ if obj == nil || obj.(string) != "astaxie" {
+ t.Fatal("/v1/zl//shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
+ }
+ if ctx.Input.ParamsLen() == 0 {
+ t.Fatal("get param error")
+ }
+ if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" {
+ t.Fatal("get :sd :id :page param error")
+ }
+
+ t2 := NewTree()
+ t2.AddTree("/v1/:shopid", tr)
+ ctx.Input.Reset(ctx)
+ obj = t2.Match("/v1/zl/shop/123/account", ctx)
+ if obj == nil || obj.(string) != "astaxie" {
+ t.Fatal("/v1/:shopid/shop/:id/account can't get obj ")
+ }
+ if ctx.Input.ParamsLen() == 0 {
+ t.Fatal("get param error")
+ }
+ if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":shopid") != "zl" {
+ t.Fatal("get :id :shopid param error")
+ }
+ ctx.Input.Reset(ctx)
+ obj = t2.Match("/v1/zl/shop/123/ttt_1_12.html", ctx)
+ if obj == nil || obj.(string) != "astaxie" {
+ t.Fatal("/v1/:shopid/shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
+ }
+ if ctx.Input.ParamsLen() == 0 {
+ t.Fatal("get :shopid param error")
+ }
+ if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" || ctx.Input.Param(":shopid") != "zl" {
+ t.Fatal("get :sd :id :page :shopid param error")
+ }
+}
+
+func TestAddTree2(t *testing.T) {
+ tr := NewTree()
+ tr.AddRouter("/shop/:id/account", "astaxie")
+ tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie")
+ t3 := NewTree()
+ t3.AddTree("/:version(v1|v2)/:prefix", tr)
+ ctx := context.NewContext()
+ obj := t3.Match("/v1/zl/shop/123/account", ctx)
+ if obj == nil || obj.(string) != "astaxie" {
+ t.Fatal("/:version(v1|v2)/:prefix/shop/:id/account can't get obj ")
+ }
+ if ctx.Input.ParamsLen() == 0 {
+ t.Fatal("get param error")
+ }
+ if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":prefix") != "zl" || ctx.Input.Param(":version") != "v1" {
+ t.Fatal("get :id :prefix :version param error")
+ }
+}
+
+func TestAddTree3(t *testing.T) {
+ tr := NewTree()
+ tr.AddRouter("/create", "astaxie")
+ tr.AddRouter("/shop/:sd/account", "astaxie")
+ t3 := NewTree()
+ t3.AddTree("/table/:num", tr)
+ ctx := context.NewContext()
+ obj := t3.Match("/table/123/shop/123/account", ctx)
+ if obj == nil || obj.(string) != "astaxie" {
+ t.Fatal("/table/:num/shop/:sd/account can't get obj ")
+ }
+ if ctx.Input.ParamsLen() == 0 {
+ t.Fatal("get param error")
+ }
+ if ctx.Input.Param(":num") != "123" || ctx.Input.Param(":sd") != "123" {
+ t.Fatal("get :num :sd param error")
+ }
+ ctx.Input.Reset(ctx)
+ obj = t3.Match("/table/123/create", ctx)
+ if obj == nil || obj.(string) != "astaxie" {
+ t.Fatal("/table/:num/create can't get obj ")
+ }
+}
+
+func TestAddTree4(t *testing.T) {
+ tr := NewTree()
+ tr.AddRouter("/create", "astaxie")
+ tr.AddRouter("/shop/:sd/:account", "astaxie")
+ t4 := NewTree()
+ t4.AddTree("/:info:int/:num/:id", tr)
+ ctx := context.NewContext()
+ obj := t4.Match("/12/123/456/shop/123/account", ctx)
+ if obj == nil || obj.(string) != "astaxie" {
+ t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ")
+ }
+ if ctx.Input.ParamsLen() == 0 {
+ t.Fatal("get param error")
+ }
+ if ctx.Input.Param(":info") != "12" || ctx.Input.Param(":num") != "123" ||
+ ctx.Input.Param(":id") != "456" || ctx.Input.Param(":sd") != "123" ||
+ ctx.Input.Param(":account") != "account" {
+ t.Fatal("get :info :num :id :sd :account param error")
+ }
+ ctx.Input.Reset(ctx)
+ obj = t4.Match("/12/123/456/create", ctx)
+ if obj == nil || obj.(string) != "astaxie" {
+ t.Fatal("/:info:int/:num/:id/create can't get obj ")
+ }
+}
+
+// Test for issue #1595
+func TestAddTree5(t *testing.T) {
+ tr := NewTree()
+ tr.AddRouter("/v1/shop/:id", "shopdetail")
+ tr.AddRouter("/v1/shop/", "shophome")
+ ctx := context.NewContext()
+ obj := tr.Match("/v1/shop/", ctx)
+ if obj == nil || obj.(string) != "shophome" {
+ t.Fatal("url /v1/shop/ need match router /v1/shop/ ")
+ }
+}
+
+func TestSplitPath(t *testing.T) {
+ a := splitPath("")
+ if len(a) != 0 {
+ t.Fatal("/ should retrun []")
+ }
+ a = splitPath("/")
+ if len(a) != 0 {
+ t.Fatal("/ should retrun []")
+ }
+ a = splitPath("/admin")
+ if len(a) != 1 || a[0] != "admin" {
+ t.Fatal("/admin should retrun [admin]")
+ }
+ a = splitPath("/admin/")
+ if len(a) != 1 || a[0] != "admin" {
+ t.Fatal("/admin/ should retrun [admin]")
+ }
+ a = splitPath("/admin/users")
+ if len(a) != 2 || a[0] != "admin" || a[1] != "users" {
+ t.Fatal("/admin should retrun [admin users]")
+ }
+ a = splitPath("/admin/:id:int")
+ if len(a) != 2 || a[0] != "admin" || a[1] != ":id:int" {
+ t.Fatal("/admin should retrun [admin :id:int]")
+ }
+}
+
+func TestSplitSegment(t *testing.T) {
+
+ items := map[string]struct {
+ isReg bool
+ params []string
+ regStr string
+ }{
+ "admin": {false, nil, ""},
+ "*": {true, []string{":splat"}, ""},
+ "*.*": {true, []string{".", ":path", ":ext"}, ""},
+ ":id": {true, []string{":id"}, ""},
+ "?:id": {true, []string{":", ":id"}, ""},
+ ":id:int": {true, []string{":id"}, "([0-9]+)"},
+ ":name:string": {true, []string{":name"}, `([\w]+)`},
+ ":id([0-9]+)": {true, []string{":id"}, `([0-9]+)`},
+ ":id([0-9]+)_:name": {true, []string{":id", ":name"}, `([0-9]+)_(.+)`},
+ ":id(.+)_cms.html": {true, []string{":id"}, `(.+)_cms.html`},
+ "cms_:id(.+)_:page(.+).html": {true, []string{":id", ":page"}, `cms_(.+)_(.+).html`},
+ `:app(a|b|c)`: {true, []string{":app"}, `(a|b|c)`},
+ `:app\((a|b|c)\)`: {true, []string{":app"}, `(.+)\((a|b|c)\)`},
+ }
+
+ for pattern, v := range items {
+ b, w, r := splitSegment(pattern)
+ if b != v.isReg || r != v.regStr || strings.Join(w, ",") != strings.Join(v.params, ",") {
+ t.Fatalf("%s should return %t,%s,%q, got %t,%s,%q", pattern, v.isReg, v.params, v.regStr, b, w, r)
+ }
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/caller_test.go b/src/vendor/github.com/astaxie/beego/utils/caller_test.go
new file mode 100644
index 0000000000..0675f0aa41
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/caller_test.go
@@ -0,0 +1,28 @@
+// Copyright 2014 beego Author. 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 utils
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestGetFuncName(t *testing.T) {
+ name := GetFuncName(TestGetFuncName)
+ t.Log(name)
+ if !strings.HasSuffix(name, ".TestGetFuncName") {
+ t.Error("get func name error")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/captcha/LICENSE b/src/vendor/github.com/astaxie/beego/utils/captcha/LICENSE
new file mode 100644
index 0000000000..0ad73ae0ee
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/captcha/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2011-2014 Dmitry Chestnykh
+
+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.
diff --git a/src/vendor/github.com/astaxie/beego/utils/captcha/README.md b/src/vendor/github.com/astaxie/beego/utils/captcha/README.md
new file mode 100644
index 0000000000..dbc2026b1e
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/captcha/README.md
@@ -0,0 +1,45 @@
+# Captcha
+
+an example for use captcha
+
+```
+package controllers
+
+import (
+ "github.com/astaxie/beego"
+ "github.com/astaxie/beego/cache"
+ "github.com/astaxie/beego/utils/captcha"
+)
+
+var cpt *captcha.Captcha
+
+func init() {
+ // use beego cache system store the captcha data
+ store := cache.NewMemoryCache()
+ cpt = captcha.NewWithFilter("/captcha/", store)
+}
+
+type MainController struct {
+ beego.Controller
+}
+
+func (this *MainController) Get() {
+ this.TplName = "index.tpl"
+}
+
+func (this *MainController) Post() {
+ this.TplName = "index.tpl"
+
+ this.Data["Success"] = cpt.VerifyReq(this.Ctx.Request)
+}
+```
+
+template usage
+
+```
+{{.Success}}
+
+```
diff --git a/src/vendor/github.com/astaxie/beego/utils/captcha/captcha.go b/src/vendor/github.com/astaxie/beego/utils/captcha/captcha.go
new file mode 100644
index 0000000000..1a4a6edcbc
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/captcha/captcha.go
@@ -0,0 +1,269 @@
+// Copyright 2014 beego Author. 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 captcha implements generation and verification of image CAPTCHAs.
+// an example for use captcha
+//
+// ```
+// package controllers
+//
+// import (
+// "github.com/astaxie/beego"
+// "github.com/astaxie/beego/cache"
+// "github.com/astaxie/beego/utils/captcha"
+// )
+//
+// var cpt *captcha.Captcha
+//
+// func init() {
+// // use beego cache system store the captcha data
+// store := cache.NewMemoryCache()
+// cpt = captcha.NewWithFilter("/captcha/", store)
+// }
+//
+// type MainController struct {
+// beego.Controller
+// }
+//
+// func (this *MainController) Get() {
+// this.TplName = "index.tpl"
+// }
+//
+// func (this *MainController) Post() {
+// this.TplName = "index.tpl"
+//
+// this.Data["Success"] = cpt.VerifyReq(this.Ctx.Request)
+// }
+// ```
+//
+// template usage
+//
+// ```
+// {{.Success}}
+//
+// ```
+package captcha
+
+import (
+ "fmt"
+ "html/template"
+ "net/http"
+ "path"
+ "strings"
+ "time"
+
+ "github.com/astaxie/beego"
+ "github.com/astaxie/beego/cache"
+ "github.com/astaxie/beego/context"
+ "github.com/astaxie/beego/utils"
+)
+
+var (
+ defaultChars = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+)
+
+const (
+ // default captcha attributes
+ challengeNums = 6
+ expiration = 600 * time.Second
+ fieldIDName = "captcha_id"
+ fieldCaptchaName = "captcha"
+ cachePrefix = "captcha_"
+ defaultURLPrefix = "/captcha/"
+)
+
+// Captcha struct
+type Captcha struct {
+ // beego cache store
+ store cache.Cache
+
+ // url prefix for captcha image
+ URLPrefix string
+
+ // specify captcha id input field name
+ FieldIDName string
+ // specify captcha result input field name
+ FieldCaptchaName string
+
+ // captcha image width and height
+ StdWidth int
+ StdHeight int
+
+ // captcha chars nums
+ ChallengeNums int
+
+ // captcha expiration seconds
+ Expiration time.Duration
+
+ // cache key prefix
+ CachePrefix string
+}
+
+// generate key string
+func (c *Captcha) key(id string) string {
+ return c.CachePrefix + id
+}
+
+// generate rand chars with default chars
+func (c *Captcha) genRandChars() []byte {
+ return utils.RandomCreateBytes(c.ChallengeNums, defaultChars...)
+}
+
+// Handler beego filter handler for serve captcha image
+func (c *Captcha) Handler(ctx *context.Context) {
+ var chars []byte
+
+ id := path.Base(ctx.Request.RequestURI)
+ if i := strings.Index(id, "."); i != -1 {
+ id = id[:i]
+ }
+
+ key := c.key(id)
+
+ if len(ctx.Input.Query("reload")) > 0 {
+ chars = c.genRandChars()
+ if err := c.store.Put(key, chars, c.Expiration); err != nil {
+ ctx.Output.SetStatus(500)
+ ctx.WriteString("captcha reload error")
+ beego.Error("Reload Create Captcha Error:", err)
+ return
+ }
+ } else {
+ if v, ok := c.store.Get(key).([]byte); ok {
+ chars = v
+ } else {
+ ctx.Output.SetStatus(404)
+ ctx.WriteString("captcha not found")
+ return
+ }
+ }
+
+ img := NewImage(chars, c.StdWidth, c.StdHeight)
+ if _, err := img.WriteTo(ctx.ResponseWriter); err != nil {
+ beego.Error("Write Captcha Image Error:", err)
+ }
+}
+
+// CreateCaptchaHTML template func for output html
+func (c *Captcha) CreateCaptchaHTML() template.HTML {
+ value, err := c.CreateCaptcha()
+ if err != nil {
+ beego.Error("Create Captcha Error:", err)
+ return ""
+ }
+
+ // create html
+ return template.HTML(fmt.Sprintf(` `+
+ ``+
+ ` `+
+ ` `, c.FieldIDName, value, c.URLPrefix, value, c.URLPrefix, value))
+}
+
+// CreateCaptcha create a new captcha id
+func (c *Captcha) CreateCaptcha() (string, error) {
+ // generate captcha id
+ id := string(utils.RandomCreateBytes(15))
+
+ // get the captcha chars
+ chars := c.genRandChars()
+
+ // save to store
+ if err := c.store.Put(c.key(id), chars, c.Expiration); err != nil {
+ return "", err
+ }
+
+ return id, nil
+}
+
+// VerifyReq verify from a request
+func (c *Captcha) VerifyReq(req *http.Request) bool {
+ req.ParseForm()
+ return c.Verify(req.Form.Get(c.FieldIDName), req.Form.Get(c.FieldCaptchaName))
+}
+
+// Verify direct verify id and challenge string
+func (c *Captcha) Verify(id string, challenge string) (success bool) {
+ if len(challenge) == 0 || len(id) == 0 {
+ return
+ }
+
+ var chars []byte
+
+ key := c.key(id)
+
+ if v, ok := c.store.Get(key).([]byte); ok {
+ chars = v
+ } else {
+ return
+ }
+
+ defer func() {
+ // finally remove it
+ c.store.Delete(key)
+ }()
+
+ if len(chars) != len(challenge) {
+ return
+ }
+ // verify challenge
+ for i, c := range chars {
+ if c != challenge[i]-48 {
+ return
+ }
+ }
+
+ return true
+}
+
+// NewCaptcha create a new captcha.Captcha
+func NewCaptcha(urlPrefix string, store cache.Cache) *Captcha {
+ cpt := &Captcha{}
+ cpt.store = store
+ cpt.FieldIDName = fieldIDName
+ cpt.FieldCaptchaName = fieldCaptchaName
+ cpt.ChallengeNums = challengeNums
+ cpt.Expiration = expiration
+ cpt.CachePrefix = cachePrefix
+ cpt.StdWidth = stdWidth
+ cpt.StdHeight = stdHeight
+
+ if len(urlPrefix) == 0 {
+ urlPrefix = defaultURLPrefix
+ }
+
+ if urlPrefix[len(urlPrefix)-1] != '/' {
+ urlPrefix += "/"
+ }
+
+ cpt.URLPrefix = urlPrefix
+
+ return cpt
+}
+
+// NewWithFilter create a new captcha.Captcha and auto AddFilter for serve captacha image
+// and add a template func for output html
+func NewWithFilter(urlPrefix string, store cache.Cache) *Captcha {
+ cpt := NewCaptcha(urlPrefix, store)
+
+ // create filter for serve captcha image
+ beego.InsertFilter(cpt.URLPrefix+"*", beego.BeforeRouter, cpt.Handler)
+
+ // add to template func map
+ beego.AddFuncMap("create_captcha", cpt.CreateCaptchaHTML)
+
+ return cpt
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/captcha/image.go b/src/vendor/github.com/astaxie/beego/utils/captcha/image.go
new file mode 100644
index 0000000000..1057192aab
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/captcha/image.go
@@ -0,0 +1,498 @@
+// Copyright 2014 beego Author. 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 captcha
+
+import (
+ "bytes"
+ "image"
+ "image/color"
+ "image/png"
+ "io"
+ "math"
+)
+
+const (
+ fontWidth = 11
+ fontHeight = 18
+ blackChar = 1
+
+ // Standard width and height of a captcha image.
+ stdWidth = 240
+ stdHeight = 80
+ // Maximum absolute skew factor of a single digit.
+ maxSkew = 0.7
+ // Number of background circles.
+ circleCount = 20
+)
+
+var font = [][]byte{
+ { // 0
+ 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+ 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+ },
+ { // 1
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
+ 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+ 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ },
+ { // 2
+ 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,
+ 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ },
+ { // 3
+ 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+ 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+ },
+ { // 4
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
+ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
+ 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+ 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ },
+ { // 5
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+ },
+ { // 6
+ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0,
+ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
+ 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+ },
+ { // 7
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ },
+ { // 8
+ 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
+ 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+ },
+ { // 9
+ 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,
+ 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+ 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
+ 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+ },
+}
+
+// Image struct
+type Image struct {
+ *image.Paletted
+ numWidth int
+ numHeight int
+ dotSize int
+}
+
+var prng = &siprng{}
+
+// randIntn returns a pseudorandom non-negative int in range [0, n).
+func randIntn(n int) int {
+ return prng.Intn(n)
+}
+
+// randInt returns a pseudorandom int in range [from, to].
+func randInt(from, to int) int {
+ return prng.Intn(to+1-from) + from
+}
+
+// randFloat returns a pseudorandom float64 in range [from, to].
+func randFloat(from, to float64) float64 {
+ return (to-from)*prng.Float64() + from
+}
+
+func randomPalette() color.Palette {
+ p := make([]color.Color, circleCount+1)
+ // Transparent color.
+ p[0] = color.RGBA{0xFF, 0xFF, 0xFF, 0x00}
+ // Primary color.
+ prim := color.RGBA{
+ uint8(randIntn(129)),
+ uint8(randIntn(129)),
+ uint8(randIntn(129)),
+ 0xFF,
+ }
+ p[1] = prim
+ // Circle colors.
+ for i := 2; i <= circleCount; i++ {
+ p[i] = randomBrightness(prim, 255)
+ }
+ return p
+}
+
+// NewImage returns a new captcha image of the given width and height with the
+// given digits, where each digit must be in range 0-9.
+func NewImage(digits []byte, width, height int) *Image {
+ m := new(Image)
+ m.Paletted = image.NewPaletted(image.Rect(0, 0, width, height), randomPalette())
+ m.calculateSizes(width, height, len(digits))
+ // Randomly position captcha inside the image.
+ maxx := width - (m.numWidth+m.dotSize)*len(digits) - m.dotSize
+ maxy := height - m.numHeight - m.dotSize*2
+ var border int
+ if width > height {
+ border = height / 5
+ } else {
+ border = width / 5
+ }
+ x := randInt(border, maxx-border)
+ y := randInt(border, maxy-border)
+ // Draw digits.
+ for _, n := range digits {
+ m.drawDigit(font[n], x, y)
+ x += m.numWidth + m.dotSize
+ }
+ // Draw strike-through line.
+ m.strikeThrough()
+ // Apply wave distortion.
+ m.distort(randFloat(5, 10), randFloat(100, 200))
+ // Fill image with random circles.
+ m.fillWithCircles(circleCount, m.dotSize)
+ return m
+}
+
+// encodedPNG encodes an image to PNG and returns
+// the result as a byte slice.
+func (m *Image) encodedPNG() []byte {
+ var buf bytes.Buffer
+ if err := png.Encode(&buf, m.Paletted); err != nil {
+ panic(err.Error())
+ }
+ return buf.Bytes()
+}
+
+// WriteTo writes captcha image in PNG format into the given writer.
+func (m *Image) WriteTo(w io.Writer) (int64, error) {
+ n, err := w.Write(m.encodedPNG())
+ return int64(n), err
+}
+
+func (m *Image) calculateSizes(width, height, ncount int) {
+ // Goal: fit all digits inside the image.
+ var border int
+ if width > height {
+ border = height / 4
+ } else {
+ border = width / 4
+ }
+ // Convert everything to floats for calculations.
+ w := float64(width - border*2)
+ h := float64(height - border*2)
+ // fw takes into account 1-dot spacing between digits.
+ fw := float64(fontWidth + 1)
+ fh := float64(fontHeight)
+ nc := float64(ncount)
+ // Calculate the width of a single digit taking into account only the
+ // width of the image.
+ nw := w / nc
+ // Calculate the height of a digit from this width.
+ nh := nw * fh / fw
+ // Digit too high?
+ if nh > h {
+ // Fit digits based on height.
+ nh = h
+ nw = fw / fh * nh
+ }
+ // Calculate dot size.
+ m.dotSize = int(nh / fh)
+ // Save everything, making the actual width smaller by 1 dot to account
+ // for spacing between digits.
+ m.numWidth = int(nw) - m.dotSize
+ m.numHeight = int(nh)
+}
+
+func (m *Image) drawHorizLine(fromX, toX, y int, colorIdx uint8) {
+ for x := fromX; x <= toX; x++ {
+ m.SetColorIndex(x, y, colorIdx)
+ }
+}
+
+func (m *Image) drawCircle(x, y, radius int, colorIdx uint8) {
+ f := 1 - radius
+ dfx := 1
+ dfy := -2 * radius
+ xo := 0
+ yo := radius
+
+ m.SetColorIndex(x, y+radius, colorIdx)
+ m.SetColorIndex(x, y-radius, colorIdx)
+ m.drawHorizLine(x-radius, x+radius, y, colorIdx)
+
+ for xo < yo {
+ if f >= 0 {
+ yo--
+ dfy += 2
+ f += dfy
+ }
+ xo++
+ dfx += 2
+ f += dfx
+ m.drawHorizLine(x-xo, x+xo, y+yo, colorIdx)
+ m.drawHorizLine(x-xo, x+xo, y-yo, colorIdx)
+ m.drawHorizLine(x-yo, x+yo, y+xo, colorIdx)
+ m.drawHorizLine(x-yo, x+yo, y-xo, colorIdx)
+ }
+}
+
+func (m *Image) fillWithCircles(n, maxradius int) {
+ maxx := m.Bounds().Max.X
+ maxy := m.Bounds().Max.Y
+ for i := 0; i < n; i++ {
+ colorIdx := uint8(randInt(1, circleCount-1))
+ r := randInt(1, maxradius)
+ m.drawCircle(randInt(r, maxx-r), randInt(r, maxy-r), r, colorIdx)
+ }
+}
+
+func (m *Image) strikeThrough() {
+ maxx := m.Bounds().Max.X
+ maxy := m.Bounds().Max.Y
+ y := randInt(maxy/3, maxy-maxy/3)
+ amplitude := randFloat(5, 20)
+ period := randFloat(80, 180)
+ dx := 2.0 * math.Pi / period
+ for x := 0; x < maxx; x++ {
+ xo := amplitude * math.Cos(float64(y)*dx)
+ yo := amplitude * math.Sin(float64(x)*dx)
+ for yn := 0; yn < m.dotSize; yn++ {
+ r := randInt(0, m.dotSize)
+ m.drawCircle(x+int(xo), y+int(yo)+(yn*m.dotSize), r/2, 1)
+ }
+ }
+}
+
+func (m *Image) drawDigit(digit []byte, x, y int) {
+ skf := randFloat(-maxSkew, maxSkew)
+ xs := float64(x)
+ r := m.dotSize / 2
+ y += randInt(-r, r)
+ for yo := 0; yo < fontHeight; yo++ {
+ for xo := 0; xo < fontWidth; xo++ {
+ if digit[yo*fontWidth+xo] != blackChar {
+ continue
+ }
+ m.drawCircle(x+xo*m.dotSize, y+yo*m.dotSize, r, 1)
+ }
+ xs += skf
+ x = int(xs)
+ }
+}
+
+func (m *Image) distort(amplude float64, period float64) {
+ w := m.Bounds().Max.X
+ h := m.Bounds().Max.Y
+
+ oldm := m.Paletted
+ newm := image.NewPaletted(image.Rect(0, 0, w, h), oldm.Palette)
+
+ dx := 2.0 * math.Pi / period
+ for x := 0; x < w; x++ {
+ for y := 0; y < h; y++ {
+ xo := amplude * math.Sin(float64(y)*dx)
+ yo := amplude * math.Cos(float64(x)*dx)
+ newm.SetColorIndex(x, y, oldm.ColorIndexAt(x+int(xo), y+int(yo)))
+ }
+ }
+ m.Paletted = newm
+}
+
+func randomBrightness(c color.RGBA, max uint8) color.RGBA {
+ minc := min3(c.R, c.G, c.B)
+ maxc := max3(c.R, c.G, c.B)
+ if maxc > max {
+ return c
+ }
+ n := randIntn(int(max-maxc)) - int(minc)
+ return color.RGBA{
+ uint8(int(c.R) + n),
+ uint8(int(c.G) + n),
+ uint8(int(c.B) + n),
+ uint8(c.A),
+ }
+}
+
+func min3(x, y, z uint8) (m uint8) {
+ m = x
+ if y < m {
+ m = y
+ }
+ if z < m {
+ m = z
+ }
+ return
+}
+
+func max3(x, y, z uint8) (m uint8) {
+ m = x
+ if y > m {
+ m = y
+ }
+ if z > m {
+ m = z
+ }
+ return
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/captcha/image_test.go b/src/vendor/github.com/astaxie/beego/utils/captcha/image_test.go
new file mode 100644
index 0000000000..5e35b7f779
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/captcha/image_test.go
@@ -0,0 +1,52 @@
+// Copyright 2014 beego Author. 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 captcha
+
+import (
+ "testing"
+
+ "github.com/astaxie/beego/utils"
+)
+
+type byteCounter struct {
+ n int64
+}
+
+func (bc *byteCounter) Write(b []byte) (int, error) {
+ bc.n += int64(len(b))
+ return len(b), nil
+}
+
+func BenchmarkNewImage(b *testing.B) {
+ b.StopTimer()
+ d := utils.RandomCreateBytes(challengeNums, defaultChars...)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ NewImage(d, stdWidth, stdHeight)
+ }
+}
+
+func BenchmarkImageWriteTo(b *testing.B) {
+ b.StopTimer()
+ d := utils.RandomCreateBytes(challengeNums, defaultChars...)
+ b.StartTimer()
+ counter := &byteCounter{}
+ for i := 0; i < b.N; i++ {
+ img := NewImage(d, stdWidth, stdHeight)
+ img.WriteTo(counter)
+ b.SetBytes(counter.n)
+ counter.n = 0
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/captcha/siprng.go b/src/vendor/github.com/astaxie/beego/utils/captcha/siprng.go
new file mode 100644
index 0000000000..5e256cf93a
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/captcha/siprng.go
@@ -0,0 +1,277 @@
+// Copyright 2014 beego Author. 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 captcha
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ "io"
+ "sync"
+)
+
+// siprng is PRNG based on SipHash-2-4.
+type siprng struct {
+ mu sync.Mutex
+ k0, k1, ctr uint64
+}
+
+// siphash implements SipHash-2-4, accepting a uint64 as a message.
+func siphash(k0, k1, m uint64) uint64 {
+ // Initialization.
+ v0 := k0 ^ 0x736f6d6570736575
+ v1 := k1 ^ 0x646f72616e646f6d
+ v2 := k0 ^ 0x6c7967656e657261
+ v3 := k1 ^ 0x7465646279746573
+ t := uint64(8) << 56
+
+ // Compression.
+ v3 ^= m
+
+ // Round 1.
+ v0 += v1
+ v1 = v1<<13 | v1>>(64-13)
+ v1 ^= v0
+ v0 = v0<<32 | v0>>(64-32)
+
+ v2 += v3
+ v3 = v3<<16 | v3>>(64-16)
+ v3 ^= v2
+
+ v0 += v3
+ v3 = v3<<21 | v3>>(64-21)
+ v3 ^= v0
+
+ v2 += v1
+ v1 = v1<<17 | v1>>(64-17)
+ v1 ^= v2
+ v2 = v2<<32 | v2>>(64-32)
+
+ // Round 2.
+ v0 += v1
+ v1 = v1<<13 | v1>>(64-13)
+ v1 ^= v0
+ v0 = v0<<32 | v0>>(64-32)
+
+ v2 += v3
+ v3 = v3<<16 | v3>>(64-16)
+ v3 ^= v2
+
+ v0 += v3
+ v3 = v3<<21 | v3>>(64-21)
+ v3 ^= v0
+
+ v2 += v1
+ v1 = v1<<17 | v1>>(64-17)
+ v1 ^= v2
+ v2 = v2<<32 | v2>>(64-32)
+
+ v0 ^= m
+
+ // Compress last block.
+ v3 ^= t
+
+ // Round 1.
+ v0 += v1
+ v1 = v1<<13 | v1>>(64-13)
+ v1 ^= v0
+ v0 = v0<<32 | v0>>(64-32)
+
+ v2 += v3
+ v3 = v3<<16 | v3>>(64-16)
+ v3 ^= v2
+
+ v0 += v3
+ v3 = v3<<21 | v3>>(64-21)
+ v3 ^= v0
+
+ v2 += v1
+ v1 = v1<<17 | v1>>(64-17)
+ v1 ^= v2
+ v2 = v2<<32 | v2>>(64-32)
+
+ // Round 2.
+ v0 += v1
+ v1 = v1<<13 | v1>>(64-13)
+ v1 ^= v0
+ v0 = v0<<32 | v0>>(64-32)
+
+ v2 += v3
+ v3 = v3<<16 | v3>>(64-16)
+ v3 ^= v2
+
+ v0 += v3
+ v3 = v3<<21 | v3>>(64-21)
+ v3 ^= v0
+
+ v2 += v1
+ v1 = v1<<17 | v1>>(64-17)
+ v1 ^= v2
+ v2 = v2<<32 | v2>>(64-32)
+
+ v0 ^= t
+
+ // Finalization.
+ v2 ^= 0xff
+
+ // Round 1.
+ v0 += v1
+ v1 = v1<<13 | v1>>(64-13)
+ v1 ^= v0
+ v0 = v0<<32 | v0>>(64-32)
+
+ v2 += v3
+ v3 = v3<<16 | v3>>(64-16)
+ v3 ^= v2
+
+ v0 += v3
+ v3 = v3<<21 | v3>>(64-21)
+ v3 ^= v0
+
+ v2 += v1
+ v1 = v1<<17 | v1>>(64-17)
+ v1 ^= v2
+ v2 = v2<<32 | v2>>(64-32)
+
+ // Round 2.
+ v0 += v1
+ v1 = v1<<13 | v1>>(64-13)
+ v1 ^= v0
+ v0 = v0<<32 | v0>>(64-32)
+
+ v2 += v3
+ v3 = v3<<16 | v3>>(64-16)
+ v3 ^= v2
+
+ v0 += v3
+ v3 = v3<<21 | v3>>(64-21)
+ v3 ^= v0
+
+ v2 += v1
+ v1 = v1<<17 | v1>>(64-17)
+ v1 ^= v2
+ v2 = v2<<32 | v2>>(64-32)
+
+ // Round 3.
+ v0 += v1
+ v1 = v1<<13 | v1>>(64-13)
+ v1 ^= v0
+ v0 = v0<<32 | v0>>(64-32)
+
+ v2 += v3
+ v3 = v3<<16 | v3>>(64-16)
+ v3 ^= v2
+
+ v0 += v3
+ v3 = v3<<21 | v3>>(64-21)
+ v3 ^= v0
+
+ v2 += v1
+ v1 = v1<<17 | v1>>(64-17)
+ v1 ^= v2
+ v2 = v2<<32 | v2>>(64-32)
+
+ // Round 4.
+ v0 += v1
+ v1 = v1<<13 | v1>>(64-13)
+ v1 ^= v0
+ v0 = v0<<32 | v0>>(64-32)
+
+ v2 += v3
+ v3 = v3<<16 | v3>>(64-16)
+ v3 ^= v2
+
+ v0 += v3
+ v3 = v3<<21 | v3>>(64-21)
+ v3 ^= v0
+
+ v2 += v1
+ v1 = v1<<17 | v1>>(64-17)
+ v1 ^= v2
+ v2 = v2<<32 | v2>>(64-32)
+
+ return v0 ^ v1 ^ v2 ^ v3
+}
+
+// rekey sets a new PRNG key, which is read from crypto/rand.
+func (p *siprng) rekey() {
+ var k [16]byte
+ if _, err := io.ReadFull(rand.Reader, k[:]); err != nil {
+ panic(err.Error())
+ }
+ p.k0 = binary.LittleEndian.Uint64(k[0:8])
+ p.k1 = binary.LittleEndian.Uint64(k[8:16])
+ p.ctr = 1
+}
+
+// Uint64 returns a new pseudorandom uint64.
+// It rekeys PRNG on the first call and every 64 MB of generated data.
+func (p *siprng) Uint64() uint64 {
+ p.mu.Lock()
+ if p.ctr == 0 || p.ctr > 8*1024*1024 {
+ p.rekey()
+ }
+ v := siphash(p.k0, p.k1, p.ctr)
+ p.ctr++
+ p.mu.Unlock()
+ return v
+}
+
+func (p *siprng) Int63() int64 {
+ return int64(p.Uint64() & 0x7fffffffffffffff)
+}
+
+func (p *siprng) Uint32() uint32 {
+ return uint32(p.Uint64())
+}
+
+func (p *siprng) Int31() int32 {
+ return int32(p.Uint32() & 0x7fffffff)
+}
+
+func (p *siprng) Intn(n int) int {
+ if n <= 0 {
+ panic("invalid argument to Intn")
+ }
+ if n <= 1<<31-1 {
+ return int(p.Int31n(int32(n)))
+ }
+ return int(p.Int63n(int64(n)))
+}
+
+func (p *siprng) Int63n(n int64) int64 {
+ if n <= 0 {
+ panic("invalid argument to Int63n")
+ }
+ max := int64((1 << 63) - 1 - (1<<63)%uint64(n))
+ v := p.Int63()
+ for v > max {
+ v = p.Int63()
+ }
+ return v % n
+}
+
+func (p *siprng) Int31n(n int32) int32 {
+ if n <= 0 {
+ panic("invalid argument to Int31n")
+ }
+ max := int32((1 << 31) - 1 - (1<<31)%uint32(n))
+ v := p.Int31()
+ for v > max {
+ v = p.Int31()
+ }
+ return v % n
+}
+
+func (p *siprng) Float64() float64 { return float64(p.Int63()) / (1 << 63) }
diff --git a/src/vendor/github.com/astaxie/beego/utils/captcha/siprng_test.go b/src/vendor/github.com/astaxie/beego/utils/captcha/siprng_test.go
new file mode 100644
index 0000000000..189d3d3cda
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/captcha/siprng_test.go
@@ -0,0 +1,33 @@
+// Copyright 2014 beego Author. 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 captcha
+
+import "testing"
+
+func TestSiphash(t *testing.T) {
+ good := uint64(0xe849e8bb6ffe2567)
+ cur := siphash(0, 0, 0)
+ if cur != good {
+ t.Fatalf("siphash: expected %x, got %x", good, cur)
+ }
+}
+
+func BenchmarkSiprng(b *testing.B) {
+ b.SetBytes(8)
+ p := &siprng{}
+ for i := 0; i < b.N; i++ {
+ p.Uint64()
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/debug_test.go b/src/vendor/github.com/astaxie/beego/utils/debug_test.go
new file mode 100644
index 0000000000..efb8924ec9
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/debug_test.go
@@ -0,0 +1,46 @@
+// Copyright 2014 beego Author. 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 utils
+
+import (
+ "testing"
+)
+
+type mytype struct {
+ next *mytype
+ prev *mytype
+}
+
+func TestPrint(t *testing.T) {
+ Display("v1", 1, "v2", 2, "v3", 3)
+}
+
+func TestPrintPoint(t *testing.T) {
+ var v1 = new(mytype)
+ var v2 = new(mytype)
+
+ v1.prev = nil
+ v1.next = v2
+
+ v2.prev = v1
+ v2.next = nil
+
+ Display("v1", v1, "v2", v2)
+}
+
+func TestPrintString(t *testing.T) {
+ str := GetDisplayString("v1", 1, "v2", 2)
+ println(str)
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/file_test.go b/src/vendor/github.com/astaxie/beego/utils/file_test.go
new file mode 100644
index 0000000000..020d7e4c6a
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/file_test.go
@@ -0,0 +1,75 @@
+// Copyright 2014 beego Author. 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 utils
+
+import (
+ "path/filepath"
+ "reflect"
+ "testing"
+)
+
+var noExistedFile = "/tmp/not_existed_file"
+
+func TestSelfPath(t *testing.T) {
+ path := SelfPath()
+ if path == "" {
+ t.Error("path cannot be empty")
+ }
+ t.Logf("SelfPath: %s", path)
+}
+
+func TestSelfDir(t *testing.T) {
+ dir := SelfDir()
+ t.Logf("SelfDir: %s", dir)
+}
+
+func TestFileExists(t *testing.T) {
+ if !FileExists("./file.go") {
+ t.Errorf("./file.go should exists, but it didn't")
+ }
+
+ if FileExists(noExistedFile) {
+ t.Errorf("Wierd, how could this file exists: %s", noExistedFile)
+ }
+}
+
+func TestSearchFile(t *testing.T) {
+ path, err := SearchFile(filepath.Base(SelfPath()), SelfDir())
+ if err != nil {
+ t.Error(err)
+ }
+ t.Log(path)
+
+ path, err = SearchFile(noExistedFile, ".")
+ if err == nil {
+ t.Errorf("err shouldnot be nil, got path: %s", SelfDir())
+ }
+}
+
+func TestGrepFile(t *testing.T) {
+ _, err := GrepFile("", noExistedFile)
+ if err == nil {
+ t.Error("expect file-not-existed error, but got nothing")
+ }
+
+ path := filepath.Join(".", "testdata", "grepe.test")
+ lines, err := GrepFile(`^\s*[^#]+`, path)
+ if err != nil {
+ t.Error(err)
+ }
+ if !reflect.DeepEqual(lines, []string{"hello", "world"}) {
+ t.Errorf("expect [hello world], but receive %v", lines)
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/mail_test.go b/src/vendor/github.com/astaxie/beego/utils/mail_test.go
new file mode 100644
index 0000000000..c38356a2f1
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/mail_test.go
@@ -0,0 +1,41 @@
+// Copyright 2014 beego Author. 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 utils
+
+import "testing"
+
+func TestMail(t *testing.T) {
+ config := `{"username":"astaxie@gmail.com","password":"astaxie","host":"smtp.gmail.com","port":587}`
+ mail := NewEMail(config)
+ if mail.Username != "astaxie@gmail.com" {
+ t.Fatal("email parse get username error")
+ }
+ if mail.Password != "astaxie" {
+ t.Fatal("email parse get password error")
+ }
+ if mail.Host != "smtp.gmail.com" {
+ t.Fatal("email parse get host error")
+ }
+ if mail.Port != 587 {
+ t.Fatal("email parse get port error")
+ }
+ mail.To = []string{"xiemengjun@gmail.com"}
+ mail.From = "astaxie@gmail.com"
+ mail.Subject = "hi, just from beego!"
+ mail.Text = "Text Body is, of course, supported!"
+ mail.HTML = "Fancy Html is supported, too! "
+ mail.AttachFile("/Users/astaxie/github/beego/beego.go")
+ mail.Send()
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/pagination/controller.go b/src/vendor/github.com/astaxie/beego/utils/pagination/controller.go
new file mode 100644
index 0000000000..1d99cac5ca
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/pagination/controller.go
@@ -0,0 +1,26 @@
+// Copyright 2014 beego Author. 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 pagination
+
+import (
+ "github.com/astaxie/beego/context"
+)
+
+// SetPaginator Instantiates a Paginator and assigns it to context.Input.Data["paginator"].
+func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) {
+ paginator = NewPaginator(context.Request, per, nums)
+ context.Input.SetData("paginator", &paginator)
+ return
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/pagination/doc.go b/src/vendor/github.com/astaxie/beego/utils/pagination/doc.go
new file mode 100644
index 0000000000..9abc6d782c
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/pagination/doc.go
@@ -0,0 +1,58 @@
+/*
+Package pagination provides utilities to setup a paginator within the
+context of a http request.
+
+Usage
+
+In your beego.Controller:
+
+ package controllers
+
+ import "github.com/astaxie/beego/utils/pagination"
+
+ type PostsController struct {
+ beego.Controller
+ }
+
+ func (this *PostsController) ListAllPosts() {
+ // sets this.Data["paginator"] with the current offset (from the url query param)
+ postsPerPage := 20
+ paginator := pagination.SetPaginator(this.Ctx, postsPerPage, CountPosts())
+
+ // fetch the next 20 posts
+ this.Data["posts"] = ListPostsByOffsetAndLimit(paginator.Offset(), postsPerPage)
+ }
+
+
+In your view templates:
+
+ {{if .paginator.HasPages}}
+
+ {{end}}
+
+See also
+
+http://beego.me/docs/mvc/view/page.md
+
+*/
+package pagination
diff --git a/src/vendor/github.com/astaxie/beego/utils/pagination/paginator.go b/src/vendor/github.com/astaxie/beego/utils/pagination/paginator.go
new file mode 100644
index 0000000000..c6db31e082
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/pagination/paginator.go
@@ -0,0 +1,189 @@
+// Copyright 2014 beego Author. 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 pagination
+
+import (
+ "math"
+ "net/http"
+ "net/url"
+ "strconv"
+)
+
+// Paginator within the state of a http request.
+type Paginator struct {
+ Request *http.Request
+ PerPageNums int
+ MaxPages int
+
+ nums int64
+ pageRange []int
+ pageNums int
+ page int
+}
+
+// PageNums Returns the total number of pages.
+func (p *Paginator) PageNums() int {
+ if p.pageNums != 0 {
+ return p.pageNums
+ }
+ pageNums := math.Ceil(float64(p.nums) / float64(p.PerPageNums))
+ if p.MaxPages > 0 {
+ pageNums = math.Min(pageNums, float64(p.MaxPages))
+ }
+ p.pageNums = int(pageNums)
+ return p.pageNums
+}
+
+// Nums Returns the total number of items (e.g. from doing SQL count).
+func (p *Paginator) Nums() int64 {
+ return p.nums
+}
+
+// SetNums Sets the total number of items.
+func (p *Paginator) SetNums(nums interface{}) {
+ p.nums, _ = toInt64(nums)
+}
+
+// Page Returns the current page.
+func (p *Paginator) Page() int {
+ if p.page != 0 {
+ return p.page
+ }
+ if p.Request.Form == nil {
+ p.Request.ParseForm()
+ }
+ p.page, _ = strconv.Atoi(p.Request.Form.Get("p"))
+ if p.page > p.PageNums() {
+ p.page = p.PageNums()
+ }
+ if p.page <= 0 {
+ p.page = 1
+ }
+ return p.page
+}
+
+// Pages Returns a list of all pages.
+//
+// Usage (in a view template):
+//
+// {{range $index, $page := .paginator.Pages}}
+//
+// {{$page}}
+//
+// {{end}}
+func (p *Paginator) Pages() []int {
+ if p.pageRange == nil && p.nums > 0 {
+ var pages []int
+ pageNums := p.PageNums()
+ page := p.Page()
+ switch {
+ case page >= pageNums-4 && pageNums > 9:
+ start := pageNums - 9 + 1
+ pages = make([]int, 9)
+ for i := range pages {
+ pages[i] = start + i
+ }
+ case page >= 5 && pageNums > 9:
+ start := page - 5 + 1
+ pages = make([]int, int(math.Min(9, float64(page+4+1))))
+ for i := range pages {
+ pages[i] = start + i
+ }
+ default:
+ pages = make([]int, int(math.Min(9, float64(pageNums))))
+ for i := range pages {
+ pages[i] = i + 1
+ }
+ }
+ p.pageRange = pages
+ }
+ return p.pageRange
+}
+
+// PageLink Returns URL for a given page index.
+func (p *Paginator) PageLink(page int) string {
+ link, _ := url.ParseRequestURI(p.Request.URL.String())
+ values := link.Query()
+ if page == 1 {
+ values.Del("p")
+ } else {
+ values.Set("p", strconv.Itoa(page))
+ }
+ link.RawQuery = values.Encode()
+ return link.String()
+}
+
+// PageLinkPrev Returns URL to the previous page.
+func (p *Paginator) PageLinkPrev() (link string) {
+ if p.HasPrev() {
+ link = p.PageLink(p.Page() - 1)
+ }
+ return
+}
+
+// PageLinkNext Returns URL to the next page.
+func (p *Paginator) PageLinkNext() (link string) {
+ if p.HasNext() {
+ link = p.PageLink(p.Page() + 1)
+ }
+ return
+}
+
+// PageLinkFirst Returns URL to the first page.
+func (p *Paginator) PageLinkFirst() (link string) {
+ return p.PageLink(1)
+}
+
+// PageLinkLast Returns URL to the last page.
+func (p *Paginator) PageLinkLast() (link string) {
+ return p.PageLink(p.PageNums())
+}
+
+// HasPrev Returns true if the current page has a predecessor.
+func (p *Paginator) HasPrev() bool {
+ return p.Page() > 1
+}
+
+// HasNext Returns true if the current page has a successor.
+func (p *Paginator) HasNext() bool {
+ return p.Page() < p.PageNums()
+}
+
+// IsActive Returns true if the given page index points to the current page.
+func (p *Paginator) IsActive(page int) bool {
+ return p.Page() == page
+}
+
+// Offset Returns the current offset.
+func (p *Paginator) Offset() int {
+ return (p.Page() - 1) * p.PerPageNums
+}
+
+// HasPages Returns true if there is more than one page.
+func (p *Paginator) HasPages() bool {
+ return p.PageNums() > 1
+}
+
+// NewPaginator Instantiates a paginator struct for the current http request.
+func NewPaginator(req *http.Request, per int, nums interface{}) *Paginator {
+ p := Paginator{}
+ p.Request = req
+ if per <= 0 {
+ per = 10
+ }
+ p.PerPageNums = per
+ p.SetNums(nums)
+ return &p
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/pagination/utils.go b/src/vendor/github.com/astaxie/beego/utils/pagination/utils.go
new file mode 100644
index 0000000000..686e68b0d2
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/pagination/utils.go
@@ -0,0 +1,34 @@
+// Copyright 2014 beego Author. 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 pagination
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// ToInt64 convert any numeric value to int64
+func toInt64(value interface{}) (d int64, err error) {
+ val := reflect.ValueOf(value)
+ switch value.(type) {
+ case int, int8, int16, int32, int64:
+ d = val.Int()
+ case uint, uint8, uint16, uint32, uint64:
+ d = int64(val.Uint())
+ default:
+ err = fmt.Errorf("ToInt64 need numeric not `%T`", value)
+ }
+ return
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/safemap_test.go b/src/vendor/github.com/astaxie/beego/utils/safemap_test.go
new file mode 100644
index 0000000000..fb271d1806
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/safemap_test.go
@@ -0,0 +1,38 @@
+// Copyright 2014 beego Author. 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 utils
+
+import (
+ "testing"
+)
+
+func Test_beemap(t *testing.T) {
+ bm := NewBeeMap()
+ if !bm.Set("astaxie", 1) {
+ t.Error("set Error")
+ }
+ if !bm.Check("astaxie") {
+ t.Error("check err")
+ }
+
+ if v := bm.Get("astaxie"); v.(int) != 1 {
+ t.Error("get err")
+ }
+
+ bm.Delete("astaxie")
+ if bm.Check("astaxie") {
+ t.Error("delete err")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/slice_test.go b/src/vendor/github.com/astaxie/beego/utils/slice_test.go
new file mode 100644
index 0000000000..142dec96db
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/slice_test.go
@@ -0,0 +1,29 @@
+// Copyright 2014 beego Author. 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 utils
+
+import (
+ "testing"
+)
+
+func TestInSlice(t *testing.T) {
+ sl := []string{"A", "b"}
+ if !InSlice("A", sl) {
+ t.Error("should be true")
+ }
+ if InSlice("B", sl) {
+ t.Error("should be false")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/utils/testdata/grepe.test b/src/vendor/github.com/astaxie/beego/utils/testdata/grepe.test
new file mode 100644
index 0000000000..6c014c403a
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/utils/testdata/grepe.test
@@ -0,0 +1,7 @@
+# empty lines
+
+
+
+hello
+# comment
+world
diff --git a/src/vendor/github.com/astaxie/beego/validation/util_test.go b/src/vendor/github.com/astaxie/beego/validation/util_test.go
new file mode 100644
index 0000000000..d7e105060a
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/validation/util_test.go
@@ -0,0 +1,100 @@
+// Copyright 2014 beego Author. 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 validation
+
+import (
+ "reflect"
+ "testing"
+)
+
+type user struct {
+ ID int
+ Tag string `valid:"Maxx(aa)"`
+ Name string `valid:"Required;"`
+ Age int `valid:"Required;Range(1, 140)"`
+ match string `valid:"Required; Match(/^(test)?\\w*@(/test/);com$/);Max(2)"`
+}
+
+func TestGetValidFuncs(t *testing.T) {
+ u := user{Name: "test", Age: 1}
+ tf := reflect.TypeOf(u)
+ var vfs []ValidFunc
+ var err error
+
+ f, _ := tf.FieldByName("ID")
+ if vfs, err = getValidFuncs(f); err != nil {
+ t.Fatal(err)
+ }
+ if len(vfs) != 0 {
+ t.Fatal("should get none ValidFunc")
+ }
+
+ f, _ = tf.FieldByName("Tag")
+ if vfs, err = getValidFuncs(f); err.Error() != "doesn't exsits Maxx valid function" {
+ t.Fatal(err)
+ }
+
+ f, _ = tf.FieldByName("Name")
+ if vfs, err = getValidFuncs(f); err != nil {
+ t.Fatal(err)
+ }
+ if len(vfs) != 1 {
+ t.Fatal("should get 1 ValidFunc")
+ }
+ if vfs[0].Name != "Required" && len(vfs[0].Params) != 0 {
+ t.Error("Required funcs should be got")
+ }
+
+ f, _ = tf.FieldByName("Age")
+ if vfs, err = getValidFuncs(f); err != nil {
+ t.Fatal(err)
+ }
+ if len(vfs) != 2 {
+ t.Fatal("should get 2 ValidFunc")
+ }
+ if vfs[0].Name != "Required" && len(vfs[0].Params) != 0 {
+ t.Error("Required funcs should be got")
+ }
+ if vfs[1].Name != "Range" && len(vfs[1].Params) != 2 {
+ t.Error("Range funcs should be got")
+ }
+
+ f, _ = tf.FieldByName("match")
+ if vfs, err = getValidFuncs(f); err != nil {
+ t.Fatal(err)
+ }
+ if len(vfs) != 3 {
+ t.Fatal("should get 3 ValidFunc but now is", len(vfs))
+ }
+}
+
+func TestCall(t *testing.T) {
+ u := user{Name: "test", Age: 180}
+ tf := reflect.TypeOf(u)
+ var vfs []ValidFunc
+ var err error
+ f, _ := tf.FieldByName("Age")
+ if vfs, err = getValidFuncs(f); err != nil {
+ t.Fatal(err)
+ }
+ valid := &Validation{}
+ vfs[1].Params = append([]interface{}{valid, u.Age}, vfs[1].Params...)
+ if _, err = funcs.Call(vfs[1].Name, vfs[1].Params...); err != nil {
+ t.Fatal(err)
+ }
+ if len(valid.Errors) != 1 {
+ t.Error("age out of range should be has an error")
+ }
+}
diff --git a/src/vendor/github.com/astaxie/beego/validation/validation_test.go b/src/vendor/github.com/astaxie/beego/validation/validation_test.go
new file mode 100644
index 0000000000..ec65b6d028
--- /dev/null
+++ b/src/vendor/github.com/astaxie/beego/validation/validation_test.go
@@ -0,0 +1,381 @@
+// Copyright 2014 beego Author. 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 validation
+
+import (
+ "regexp"
+ "testing"
+ "time"
+)
+
+func TestRequired(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Required(nil, "nil").Ok {
+ t.Error("nil object should be false")
+ }
+ if !valid.Required(true, "bool").Ok {
+ t.Error("Bool value should always return true")
+ }
+ if !valid.Required(false, "bool").Ok {
+ t.Error("Bool value should always return true")
+ }
+ if valid.Required("", "string").Ok {
+ t.Error("\"'\" string should be false")
+ }
+ if !valid.Required("astaxie", "string").Ok {
+ t.Error("string should be true")
+ }
+ if valid.Required(0, "zero").Ok {
+ t.Error("Integer should not be equal 0")
+ }
+ if !valid.Required(1, "int").Ok {
+ t.Error("Integer except 0 should be true")
+ }
+ if !valid.Required(time.Now(), "time").Ok {
+ t.Error("time should be true")
+ }
+ if valid.Required([]string{}, "emptySlice").Ok {
+ t.Error("empty slice should be false")
+ }
+ if !valid.Required([]interface{}{"ok"}, "slice").Ok {
+ t.Error("slice should be true")
+ }
+}
+
+func TestMin(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Min(-1, 0, "min0").Ok {
+ t.Error("-1 is less than the minimum value of 0 should be false")
+ }
+ if !valid.Min(1, 0, "min0").Ok {
+ t.Error("1 is greater or equal than the minimum value of 0 should be true")
+ }
+}
+
+func TestMax(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Max(1, 0, "max0").Ok {
+ t.Error("1 is greater than the minimum value of 0 should be false")
+ }
+ if !valid.Max(-1, 0, "max0").Ok {
+ t.Error("-1 is less or equal than the maximum value of 0 should be true")
+ }
+}
+
+func TestRange(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Range(-1, 0, 1, "range0_1").Ok {
+ t.Error("-1 is between 0 and 1 should be false")
+ }
+ if !valid.Range(1, 0, 1, "range0_1").Ok {
+ t.Error("1 is between 0 and 1 should be true")
+ }
+}
+
+func TestMinSize(t *testing.T) {
+ valid := Validation{}
+
+ if valid.MinSize("", 1, "minSize1").Ok {
+ t.Error("the length of \"\" is less than the minimum value of 1 should be false")
+ }
+ if !valid.MinSize("ok", 1, "minSize1").Ok {
+ t.Error("the length of \"ok\" is greater or equal than the minimum value of 1 should be true")
+ }
+ if valid.MinSize([]string{}, 1, "minSize1").Ok {
+ t.Error("the length of empty slice is less than the minimum value of 1 should be false")
+ }
+ if !valid.MinSize([]interface{}{"ok"}, 1, "minSize1").Ok {
+ t.Error("the length of [\"ok\"] is greater or equal than the minimum value of 1 should be true")
+ }
+}
+
+func TestMaxSize(t *testing.T) {
+ valid := Validation{}
+
+ if valid.MaxSize("ok", 1, "maxSize1").Ok {
+ t.Error("the length of \"ok\" is greater than the maximum value of 1 should be false")
+ }
+ if !valid.MaxSize("", 1, "maxSize1").Ok {
+ t.Error("the length of \"\" is less or equal than the maximum value of 1 should be true")
+ }
+ if valid.MaxSize([]interface{}{"ok", false}, 1, "maxSize1").Ok {
+ t.Error("the length of [\"ok\", false] is greater than the maximum value of 1 should be false")
+ }
+ if !valid.MaxSize([]string{}, 1, "maxSize1").Ok {
+ t.Error("the length of empty slice is less or equal than the maximum value of 1 should be true")
+ }
+}
+
+func TestLength(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Length("", 1, "length1").Ok {
+ t.Error("the length of \"\" must equal 1 should be false")
+ }
+ if !valid.Length("1", 1, "length1").Ok {
+ t.Error("the length of \"1\" must equal 1 should be true")
+ }
+ if valid.Length([]string{}, 1, "length1").Ok {
+ t.Error("the length of empty slice must equal 1 should be false")
+ }
+ if !valid.Length([]interface{}{"ok"}, 1, "length1").Ok {
+ t.Error("the length of [\"ok\"] must equal 1 should be true")
+ }
+}
+
+func TestAlpha(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Alpha("a,1-@ $", "alpha").Ok {
+ t.Error("\"a,1-@ $\" are valid alpha characters should be false")
+ }
+ if !valid.Alpha("abCD", "alpha").Ok {
+ t.Error("\"abCD\" are valid alpha characters should be true")
+ }
+}
+
+func TestNumeric(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Numeric("a,1-@ $", "numeric").Ok {
+ t.Error("\"a,1-@ $\" are valid numeric characters should be false")
+ }
+ if !valid.Numeric("1234", "numeric").Ok {
+ t.Error("\"1234\" are valid numeric characters should be true")
+ }
+}
+
+func TestAlphaNumeric(t *testing.T) {
+ valid := Validation{}
+
+ if valid.AlphaNumeric("a,1-@ $", "alphaNumeric").Ok {
+ t.Error("\"a,1-@ $\" are valid alpha or numeric characters should be false")
+ }
+ if !valid.AlphaNumeric("1234aB", "alphaNumeric").Ok {
+ t.Error("\"1234aB\" are valid alpha or numeric characters should be true")
+ }
+}
+
+func TestMatch(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Match("suchuangji@gmail", regexp.MustCompile("^\\w+@\\w+\\.\\w+$"), "match").Ok {
+ t.Error("\"suchuangji@gmail\" match \"^\\w+@\\w+\\.\\w+$\" should be false")
+ }
+ if !valid.Match("suchuangji@gmail.com", regexp.MustCompile("^\\w+@\\w+\\.\\w+$"), "match").Ok {
+ t.Error("\"suchuangji@gmail\" match \"^\\w+@\\w+\\.\\w+$\" should be true")
+ }
+}
+
+func TestNoMatch(t *testing.T) {
+ valid := Validation{}
+
+ if valid.NoMatch("123@gmail", regexp.MustCompile("[^\\w\\d]"), "nomatch").Ok {
+ t.Error("\"123@gmail\" not match \"[^\\w\\d]\" should be false")
+ }
+ if !valid.NoMatch("123gmail", regexp.MustCompile("[^\\w\\d]"), "match").Ok {
+ t.Error("\"123@gmail\" not match \"[^\\w\\d@]\" should be true")
+ }
+}
+
+func TestAlphaDash(t *testing.T) {
+ valid := Validation{}
+
+ if valid.AlphaDash("a,1-@ $", "alphaDash").Ok {
+ t.Error("\"a,1-@ $\" are valid alpha or numeric or dash(-_) characters should be false")
+ }
+ if !valid.AlphaDash("1234aB-_", "alphaDash").Ok {
+ t.Error("\"1234aB\" are valid alpha or numeric or dash(-_) characters should be true")
+ }
+}
+
+func TestEmail(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Email("not@a email", "email").Ok {
+ t.Error("\"not@a email\" is a valid email address should be false")
+ }
+ if !valid.Email("suchuangji@gmail.com", "email").Ok {
+ t.Error("\"suchuangji@gmail.com\" is a valid email address should be true")
+ }
+}
+
+func TestIP(t *testing.T) {
+ valid := Validation{}
+
+ if valid.IP("11.255.255.256", "IP").Ok {
+ t.Error("\"11.255.255.256\" is a valid ip address should be false")
+ }
+ if !valid.IP("01.11.11.11", "IP").Ok {
+ t.Error("\"suchuangji@gmail.com\" is a valid ip address should be true")
+ }
+}
+
+func TestBase64(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Base64("suchuangji@gmail.com", "base64").Ok {
+ t.Error("\"suchuangji@gmail.com\" are a valid base64 characters should be false")
+ }
+ if !valid.Base64("c3VjaHVhbmdqaUBnbWFpbC5jb20=", "base64").Ok {
+ t.Error("\"c3VjaHVhbmdqaUBnbWFpbC5jb20=\" are a valid base64 characters should be true")
+ }
+}
+
+func TestMobile(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Mobile("19800008888", "mobile").Ok {
+ t.Error("\"19800008888\" is a valid mobile phone number should be false")
+ }
+ if !valid.Mobile("18800008888", "mobile").Ok {
+ t.Error("\"18800008888\" is a valid mobile phone number should be true")
+ }
+ if !valid.Mobile("18000008888", "mobile").Ok {
+ t.Error("\"18000008888\" is a valid mobile phone number should be true")
+ }
+ if !valid.Mobile("8618300008888", "mobile").Ok {
+ t.Error("\"8618300008888\" is a valid mobile phone number should be true")
+ }
+ if !valid.Mobile("+8614700008888", "mobile").Ok {
+ t.Error("\"+8614700008888\" is a valid mobile phone number should be true")
+ }
+}
+
+func TestTel(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Tel("222-00008888", "telephone").Ok {
+ t.Error("\"222-00008888\" is a valid telephone number should be false")
+ }
+ if !valid.Tel("022-70008888", "telephone").Ok {
+ t.Error("\"022-70008888\" is a valid telephone number should be true")
+ }
+ if !valid.Tel("02270008888", "telephone").Ok {
+ t.Error("\"02270008888\" is a valid telephone number should be true")
+ }
+ if !valid.Tel("70008888", "telephone").Ok {
+ t.Error("\"70008888\" is a valid telephone number should be true")
+ }
+}
+
+func TestPhone(t *testing.T) {
+ valid := Validation{}
+
+ if valid.Phone("222-00008888", "phone").Ok {
+ t.Error("\"222-00008888\" is a valid phone number should be false")
+ }
+ if !valid.Mobile("+8614700008888", "phone").Ok {
+ t.Error("\"+8614700008888\" is a valid phone number should be true")
+ }
+ if !valid.Tel("02270008888", "phone").Ok {
+ t.Error("\"02270008888\" is a valid phone number should be true")
+ }
+}
+
+func TestZipCode(t *testing.T) {
+ valid := Validation{}
+
+ if valid.ZipCode("", "zipcode").Ok {
+ t.Error("\"00008888\" is a valid zipcode should be false")
+ }
+ if !valid.ZipCode("536000", "zipcode").Ok {
+ t.Error("\"536000\" is a valid zipcode should be true")
+ }
+}
+
+func TestValid(t *testing.T) {
+ type user struct {
+ ID int
+ Name string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"`
+ Age int `valid:"Required;Range(1, 140)"`
+ }
+ valid := Validation{}
+
+ u := user{Name: "test@/test/;com", Age: 40}
+ b, err := valid.Valid(u)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !b {
+ t.Error("validation should be passed")
+ }
+
+ uptr := &user{Name: "test", Age: 40}
+ valid.Clear()
+ b, err = valid.Valid(uptr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if b {
+ t.Error("validation should not be passed")
+ }
+ if len(valid.Errors) != 1 {
+ t.Fatalf("valid errors len should be 1 but got %d", len(valid.Errors))
+ }
+ if valid.Errors[0].Key != "Name.Match" {
+ t.Errorf("Message key should be `Name.Match` but got %s", valid.Errors[0].Key)
+ }
+
+ u = user{Name: "test@/test/;com", Age: 180}
+ valid.Clear()
+ b, err = valid.Valid(u)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if b {
+ t.Error("validation should not be passed")
+ }
+ if len(valid.Errors) != 1 {
+ t.Fatalf("valid errors len should be 1 but got %d", len(valid.Errors))
+ }
+ if valid.Errors[0].Key != "Age.Range" {
+ t.Errorf("Message key should be `Name.Match` but got %s", valid.Errors[0].Key)
+ }
+}
+
+func TestRecursiveValid(t *testing.T) {
+ type User struct {
+ ID int
+ Name string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"`
+ Age int `valid:"Required;Range(1, 140)"`
+ }
+
+ type AnonymouseUser struct {
+ ID2 int
+ Name2 string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"`
+ Age2 int `valid:"Required;Range(1, 140)"`
+ }
+
+ type Account struct {
+ Password string `valid:"Required"`
+ U User
+ AnonymouseUser
+ }
+ valid := Validation{}
+
+ u := Account{Password: "abc123_", U: User{}}
+ b, err := valid.RecursiveValid(u)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if b {
+ t.Error("validation should not be passed")
+ }
+}
diff --git a/src/vendor/github.com/beego/i18n/.gitignore b/src/vendor/github.com/beego/i18n/.gitignore
new file mode 100644
index 0000000000..00268614f0
--- /dev/null
+++ b/src/vendor/github.com/beego/i18n/.gitignore
@@ -0,0 +1,22 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
diff --git a/src/vendor/github.com/beego/i18n/beei18n/beei18n.go b/src/vendor/github.com/beego/i18n/beei18n/beei18n.go
new file mode 100644
index 0000000000..487f792fce
--- /dev/null
+++ b/src/vendor/github.com/beego/i18n/beei18n/beei18n.go
@@ -0,0 +1,166 @@
+// Copyright 2013-2014 beego authors
+//
+// 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.
+
+// beei18n is a helper tool for beego/i18n package.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "html/template"
+ "io"
+ "log"
+ "os"
+ "strings"
+)
+
+type Command struct {
+ // Run runs the command.
+ // The args are the arguments after the command name.
+ Run func(cmd *Command, args []string)
+
+ // UsageLine is the one-line usage message.
+ // The first word in the line is taken to be the command name.
+ UsageLine string
+
+ // Short is the short description shown in the 'go help' output.
+ Short string
+
+ // Long is the long message shown in the 'go help ' output.
+ Long string
+
+ // Flag is a set of flags specific to this command.
+ Flag flag.FlagSet
+
+ // CustomFlags indicates that the command will do its own
+ // flag parsing.
+ CustomFlags bool
+}
+
+// Name returns the command's name: the first word in the usage line.
+func (c *Command) Name() string {
+ name := c.UsageLine
+ i := strings.Index(name, " ")
+ if i >= 0 {
+ name = name[:i]
+ }
+ return name
+}
+
+func (c *Command) Usage() {
+ fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
+ fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
+ os.Exit(2)
+}
+
+// Runnable reports whether the command can be run; otherwise
+// it is a documentation pseudo-command such as importpath.
+func (c *Command) Runnable() bool {
+ return c.Run != nil
+}
+
+var commands = []*Command{
+ cmdSync,
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+ log.SetFlags(0)
+
+ args := flag.Args()
+ if len(args) < 1 {
+ usage()
+ }
+
+ if args[0] == "help" {
+ help(args[1:])
+ return
+ }
+
+ for _, cmd := range commands {
+ if cmd.Name() == args[0] && cmd.Run != nil {
+ cmd.Flag.Usage = func() { cmd.Usage() }
+ if cmd.CustomFlags {
+ args = args[1:]
+ } else {
+ cmd.Flag.Parse(args[1:])
+ args = cmd.Flag.Args()
+ }
+ cmd.Run(cmd, args)
+ os.Exit(2)
+ return
+ }
+ }
+
+ fmt.Fprintf(os.Stderr, "beei18n: unknown subcommand %q\nRun 'bee help' for usage.\n", args[0])
+ os.Exit(2)
+}
+
+var usageTemplate = `beei18n is a helper tool for beego/i18n package.
+
+Usage:
+
+ beei18n command [arguments]
+
+The commands are:
+{{range .}}{{if .Runnable}}
+ {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
+
+Use "beei18n help [command]" for more information about a command.
+`
+
+var helpTemplate = `{{if .Runnable}}usage: beei18n {{.UsageLine}}
+
+{{end}}{{.Long | trim}}
+`
+
+func usage() {
+ tmpl(os.Stdout, usageTemplate, commands)
+ os.Exit(2)
+}
+
+func tmpl(w io.Writer, text string, data interface{}) {
+ t := template.New("top")
+ t.Funcs(template.FuncMap{"trim": strings.TrimSpace})
+ template.Must(t.Parse(text))
+ if err := t.Execute(w, data); err != nil {
+ panic(err)
+ }
+}
+
+func help(args []string) {
+ if len(args) == 0 {
+ usage()
+ // not exit 2: succeeded at 'go help'.
+ return
+ }
+ if len(args) != 1 {
+ fmt.Fprintf(os.Stdout, "usage: beei18n help command\n\nToo many arguments given.\n")
+ os.Exit(2) // failed at 'bee help'
+ }
+
+ arg := args[0]
+
+ for _, cmd := range commands {
+ if cmd.Name() == arg {
+ tmpl(os.Stdout, helpTemplate, cmd)
+ // not exit 2: succeeded at 'go help cmd'.
+ return
+ }
+ }
+
+ fmt.Fprintf(os.Stdout, "Unknown help topic %#q. Run 'beei18n help'.\n", arg)
+ os.Exit(2) // failed at 'bee help cmd'
+}
diff --git a/src/vendor/github.com/beego/i18n/beei18n/sync.go b/src/vendor/github.com/beego/i18n/beei18n/sync.go
new file mode 100644
index 0000000000..324444bb3a
--- /dev/null
+++ b/src/vendor/github.com/beego/i18n/beei18n/sync.go
@@ -0,0 +1,86 @@
+// Copyright 2013-2014 beego authors
+//
+// 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 (
+ "log"
+ "os"
+
+ "github.com/Unknwon/goconfig"
+)
+
+var cmdSync = &Command{
+ UsageLine: "sync [source file] [target files]",
+ Short: "sync keys for locale files",
+ Long: `to quickly sync keys for one or more locale files
+based on the one you already have
+`,
+}
+
+func init() {
+ cmdSync.Run = syncLocales
+}
+
+func syncLocales(cmd *Command, args []string) {
+ switch len(args) {
+ case 0:
+ log.Fatalln("No source locale file is specified")
+ case 1:
+ log.Fatalln("No target locale file is specified")
+ }
+
+ srcLocale, err := goconfig.LoadConfigFile(args[0])
+ if err != nil {
+ log.Fatalln(err)
+ }
+
+ // Load or create target locales.
+ targets := args[1:]
+ targetLocales := make([]*goconfig.ConfigFile, len(targets))
+ for i, target := range targets {
+ if !isExist(target) {
+ os.Create(target)
+ }
+
+ targetLocales[i], err = goconfig.LoadConfigFile(target)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ }
+
+ for _, secName := range srcLocale.GetSectionList() {
+ keyList := srcLocale.GetKeyList(secName)
+ for _, target := range targetLocales {
+ for _, k := range keyList {
+ if _, err = target.GetValue(secName, k); err != nil {
+ target.SetValue(secName, k, "")
+ }
+ }
+ }
+ }
+
+ for i, target := range targetLocales {
+ err = goconfig.SaveConfigFile(target, targets[i])
+ if err != nil {
+ log.Fatalln(err)
+ }
+ }
+}
+
+// IsExist returns whether a file or directory exists.
+func isExist(path string) bool {
+ _, err := os.Stat(path)
+ return err == nil || os.IsExist(err)
+}
diff --git a/src/vendor/github.com/davecgh/go-spew/.gitignore b/src/vendor/github.com/davecgh/go-spew/.gitignore
new file mode 100644
index 0000000000..00268614f0
--- /dev/null
+++ b/src/vendor/github.com/davecgh/go-spew/.gitignore
@@ -0,0 +1,22 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
diff --git a/src/vendor/github.com/davecgh/go-spew/.travis.yml b/src/vendor/github.com/davecgh/go-spew/.travis.yml
new file mode 100644
index 0000000000..984e0736e7
--- /dev/null
+++ b/src/vendor/github.com/davecgh/go-spew/.travis.yml
@@ -0,0 +1,14 @@
+language: go
+go:
+ - 1.5.4
+ - 1.6.3
+ - 1.7
+install:
+ - go get -v golang.org/x/tools/cmd/cover
+script:
+ - go test -v -tags=safe ./spew
+ - go test -v -tags=testcgo ./spew -covermode=count -coverprofile=profile.cov
+after_success:
+ - go get -v github.com/mattn/goveralls
+ - export PATH=$PATH:$HOME/gopath/bin
+ - goveralls -coverprofile=profile.cov -service=travis-ci
diff --git a/src/vendor/github.com/davecgh/go-spew/README.md b/src/vendor/github.com/davecgh/go-spew/README.md
new file mode 100644
index 0000000000..262430449b
--- /dev/null
+++ b/src/vendor/github.com/davecgh/go-spew/README.md
@@ -0,0 +1,205 @@
+go-spew
+=======
+
+[![Build Status](https://img.shields.io/travis/davecgh/go-spew.svg)]
+(https://travis-ci.org/davecgh/go-spew) [![ISC License]
+(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) [![Coverage Status]
+(https://img.shields.io/coveralls/davecgh/go-spew.svg)]
+(https://coveralls.io/r/davecgh/go-spew?branch=master)
+
+
+Go-spew implements a deep pretty printer for Go data structures to aid in
+debugging. A comprehensive suite of tests with 100% test coverage is provided
+to ensure proper functionality. See `test_coverage.txt` for the gocov coverage
+report. Go-spew is licensed under the liberal ISC license, so it may be used in
+open source or commercial projects.
+
+If you're interested in reading about how this package came to life and some
+of the challenges involved in providing a deep pretty printer, there is a blog
+post about it
+[here](https://web.archive.org/web/20160304013555/https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/).
+
+## Documentation
+
+[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)]
+(http://godoc.org/github.com/davecgh/go-spew/spew)
+
+Full `go doc` style documentation for the project can be viewed online without
+installing this package by using the excellent GoDoc site here:
+http://godoc.org/github.com/davecgh/go-spew/spew
+
+You can also view the documentation locally once the package is installed with
+the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
+http://localhost:6060/pkg/github.com/davecgh/go-spew/spew
+
+## Installation
+
+```bash
+$ go get -u github.com/davecgh/go-spew/spew
+```
+
+## Quick Start
+
+Add this import line to the file you're working in:
+
+```Go
+import "github.com/davecgh/go-spew/spew"
+```
+
+To dump a variable with full newlines, indentation, type, and pointer
+information use Dump, Fdump, or Sdump:
+
+```Go
+spew.Dump(myVar1, myVar2, ...)
+spew.Fdump(someWriter, myVar1, myVar2, ...)
+str := spew.Sdump(myVar1, myVar2, ...)
+```
+
+Alternatively, if you would prefer to use format strings with a compacted inline
+printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most
+compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types
+and pointer addresses):
+
+```Go
+spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+```
+
+## Debugging a Web Application Example
+
+Here is an example of how you can use `spew.Sdump()` to help debug a web application. Please be sure to wrap your output using the `html.EscapeString()` function for safety reasons. You should also only use this debugging technique in a development environment, never in production.
+
+```Go
+package main
+
+import (
+ "fmt"
+ "html"
+ "net/http"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+func handler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/html")
+ fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:])
+ fmt.Fprintf(w, "")
+}
+
+func main() {
+ http.HandleFunc("/", handler)
+ http.ListenAndServe(":8080", nil)
+}
+```
+
+## Sample Dump Output
+
+```
+(main.Foo) {
+ unexportedField: (*main.Bar)(0xf84002e210)({
+ flag: (main.Flag) flagTwo,
+ data: (uintptr)
+ }),
+ ExportedField: (map[interface {}]interface {}) {
+ (string) "one": (bool) true
+ }
+}
+([]uint8) {
+ 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
+ 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
+ 00000020 31 32 |12|
+}
+```
+
+## Sample Formatter Output
+
+Double pointer to a uint8:
+```
+ %v: <**>5
+ %+v: <**>(0xf8400420d0->0xf8400420c8)5
+ %#v: (**uint8)5
+ %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
+```
+
+Pointer to circular struct with a uint8 field and a pointer to itself:
+```
+ %v: <*>{1 <*>}
+ %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)}
+ %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)}
+ %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)}
+```
+
+## Configuration Options
+
+Configuration of spew is handled by fields in the ConfigState type. For
+convenience, all of the top-level functions use a global state available via the
+spew.Config global.
+
+It is also possible to create a ConfigState instance that provides methods
+equivalent to the top-level functions. This allows concurrent configuration
+options. See the ConfigState documentation for more details.
+
+```
+* Indent
+ String to use for each indentation level for Dump functions.
+ It is a single space by default. A popular alternative is "\t".
+
+* MaxDepth
+ Maximum number of levels to descend into nested data structures.
+ There is no limit by default.
+
+* DisableMethods
+ Disables invocation of error and Stringer interface methods.
+ Method invocation is enabled by default.
+
+* DisablePointerMethods
+ Disables invocation of error and Stringer interface methods on types
+ which only accept pointer receivers from non-pointer variables. This option
+ relies on access to the unsafe package, so it will not have any effect when
+ running in environments without access to the unsafe package such as Google
+ App Engine or with the "safe" build tag specified.
+ Pointer method invocation is enabled by default.
+
+* DisablePointerAddresses
+ DisablePointerAddresses specifies whether to disable the printing of
+ pointer addresses. This is useful when diffing data structures in tests.
+
+* DisableCapacities
+ DisableCapacities specifies whether to disable the printing of capacities
+ for arrays, slices, maps and channels. This is useful when diffing data
+ structures in tests.
+
+* ContinueOnMethod
+ Enables recursion into types after invoking error and Stringer interface
+ methods. Recursion after method invocation is disabled by default.
+
+* SortKeys
+ Specifies map keys should be sorted before being printed. Use
+ this to have a more deterministic, diffable output. Note that
+ only native types (bool, int, uint, floats, uintptr and string)
+ and types which implement error or Stringer interfaces are supported,
+ with other types sorted according to the reflect.Value.String() output
+ which guarantees display stability. Natural map order is used by
+ default.
+
+* SpewKeys
+ SpewKeys specifies that, as a last resort attempt, map keys should be
+ spewed to strings and sorted by those strings. This is only considered
+ if SortKeys is true.
+
+```
+
+## Unsafe Package Dependency
+
+This package relies on the unsafe package to perform some of the more advanced
+features, however it also supports a "limited" mode which allows it to work in
+environments where the unsafe package is not available. By default, it will
+operate in this mode on Google App Engine and when compiled with GopherJS. The
+"safe" build tag may also be specified to force the package to build without
+using the unsafe package.
+
+## License
+
+Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License.
diff --git a/src/vendor/github.com/davecgh/go-spew/cov_report.sh b/src/vendor/github.com/davecgh/go-spew/cov_report.sh
new file mode 100644
index 0000000000..9579497e41
--- /dev/null
+++ b/src/vendor/github.com/davecgh/go-spew/cov_report.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# This script uses gocov to generate a test coverage report.
+# The gocov tool my be obtained with the following command:
+# go get github.com/axw/gocov/gocov
+#
+# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
+
+# Check for gocov.
+if ! type gocov >/dev/null 2>&1; then
+ echo >&2 "This script requires the gocov tool."
+ echo >&2 "You may obtain it with the following command:"
+ echo >&2 "go get github.com/axw/gocov/gocov"
+ exit 1
+fi
+
+# Only run the cgo tests if gcc is installed.
+if type gcc >/dev/null 2>&1; then
+ (cd spew && gocov test -tags testcgo | gocov report)
+else
+ (cd spew && gocov test | gocov report)
+fi
diff --git a/src/vendor/github.com/davecgh/go-spew/spew/common_test.go b/src/vendor/github.com/davecgh/go-spew/spew/common_test.go
new file mode 100644
index 0000000000..0f5ce47dca
--- /dev/null
+++ b/src/vendor/github.com/davecgh/go-spew/spew/common_test.go
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew_test
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+// custom type to test Stinger interface on non-pointer receiver.
+type stringer string
+
+// String implements the Stringer interface for testing invocation of custom
+// stringers on types with non-pointer receivers.
+func (s stringer) String() string {
+ return "stringer " + string(s)
+}
+
+// custom type to test Stinger interface on pointer receiver.
+type pstringer string
+
+// String implements the Stringer interface for testing invocation of custom
+// stringers on types with only pointer receivers.
+func (s *pstringer) String() string {
+ return "stringer " + string(*s)
+}
+
+// xref1 and xref2 are cross referencing structs for testing circular reference
+// detection.
+type xref1 struct {
+ ps2 *xref2
+}
+type xref2 struct {
+ ps1 *xref1
+}
+
+// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular
+// reference for testing detection.
+type indirCir1 struct {
+ ps2 *indirCir2
+}
+type indirCir2 struct {
+ ps3 *indirCir3
+}
+type indirCir3 struct {
+ ps1 *indirCir1
+}
+
+// embed is used to test embedded structures.
+type embed struct {
+ a string
+}
+
+// embedwrap is used to test embedded structures.
+type embedwrap struct {
+ *embed
+ e *embed
+}
+
+// panicer is used to intentionally cause a panic for testing spew properly
+// handles them
+type panicer int
+
+func (p panicer) String() string {
+ panic("test panic")
+}
+
+// customError is used to test custom error interface invocation.
+type customError int
+
+func (e customError) Error() string {
+ return fmt.Sprintf("error: %d", int(e))
+}
+
+// stringizeWants converts a slice of wanted test output into a format suitable
+// for a test error message.
+func stringizeWants(wants []string) string {
+ s := ""
+ for i, want := range wants {
+ if i > 0 {
+ s += fmt.Sprintf("want%d: %s", i+1, want)
+ } else {
+ s += "want: " + want
+ }
+ }
+ return s
+}
+
+// testFailed returns whether or not a test failed by checking if the result
+// of the test is in the slice of wanted strings.
+func testFailed(result string, wants []string) bool {
+ for _, want := range wants {
+ if result == want {
+ return false
+ }
+ }
+ return true
+}
+
+type sortableStruct struct {
+ x int
+}
+
+func (ss sortableStruct) String() string {
+ return fmt.Sprintf("ss.%d", ss.x)
+}
+
+type unsortableStruct struct {
+ x int
+}
+
+type sortTestCase struct {
+ input []reflect.Value
+ expected []reflect.Value
+}
+
+func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) {
+ getInterfaces := func(values []reflect.Value) []interface{} {
+ interfaces := []interface{}{}
+ for _, v := range values {
+ interfaces = append(interfaces, v.Interface())
+ }
+ return interfaces
+ }
+
+ for _, test := range tests {
+ spew.SortValues(test.input, cs)
+ // reflect.DeepEqual cannot really make sense of reflect.Value,
+ // probably because of all the pointer tricks. For instance,
+ // v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{}
+ // instead.
+ input := getInterfaces(test.input)
+ expected := getInterfaces(test.expected)
+ if !reflect.DeepEqual(input, expected) {
+ t.Errorf("Sort mismatch:\n %v != %v", input, expected)
+ }
+ }
+}
+
+// TestSortValues ensures the sort functionality for relect.Value based sorting
+// works as intended.
+func TestSortValues(t *testing.T) {
+ v := reflect.ValueOf
+
+ a := v("a")
+ b := v("b")
+ c := v("c")
+ embedA := v(embed{"a"})
+ embedB := v(embed{"b"})
+ embedC := v(embed{"c"})
+ tests := []sortTestCase{
+ // No values.
+ {
+ []reflect.Value{},
+ []reflect.Value{},
+ },
+ // Bools.
+ {
+ []reflect.Value{v(false), v(true), v(false)},
+ []reflect.Value{v(false), v(false), v(true)},
+ },
+ // Ints.
+ {
+ []reflect.Value{v(2), v(1), v(3)},
+ []reflect.Value{v(1), v(2), v(3)},
+ },
+ // Uints.
+ {
+ []reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))},
+ []reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))},
+ },
+ // Floats.
+ {
+ []reflect.Value{v(2.0), v(1.0), v(3.0)},
+ []reflect.Value{v(1.0), v(2.0), v(3.0)},
+ },
+ // Strings.
+ {
+ []reflect.Value{b, a, c},
+ []reflect.Value{a, b, c},
+ },
+ // Array
+ {
+ []reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})},
+ []reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})},
+ },
+ // Uintptrs.
+ {
+ []reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
+ []reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
+ },
+ // SortableStructs.
+ {
+ // Note: not sorted - DisableMethods is set.
+ []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
+ []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
+ },
+ // UnsortableStructs.
+ {
+ // Note: not sorted - SpewKeys is false.
+ []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
+ []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
+ },
+ // Invalid.
+ {
+ []reflect.Value{embedB, embedA, embedC},
+ []reflect.Value{embedB, embedA, embedC},
+ },
+ }
+ cs := spew.ConfigState{DisableMethods: true, SpewKeys: false}
+ helpTestSortValues(tests, &cs, t)
+}
+
+// TestSortValuesWithMethods ensures the sort functionality for relect.Value
+// based sorting works as intended when using string methods.
+func TestSortValuesWithMethods(t *testing.T) {
+ v := reflect.ValueOf
+
+ a := v("a")
+ b := v("b")
+ c := v("c")
+ tests := []sortTestCase{
+ // Ints.
+ {
+ []reflect.Value{v(2), v(1), v(3)},
+ []reflect.Value{v(1), v(2), v(3)},
+ },
+ // Strings.
+ {
+ []reflect.Value{b, a, c},
+ []reflect.Value{a, b, c},
+ },
+ // SortableStructs.
+ {
+ []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
+ []reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
+ },
+ // UnsortableStructs.
+ {
+ // Note: not sorted - SpewKeys is false.
+ []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
+ []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
+ },
+ }
+ cs := spew.ConfigState{DisableMethods: false, SpewKeys: false}
+ helpTestSortValues(tests, &cs, t)
+}
+
+// TestSortValuesWithSpew ensures the sort functionality for relect.Value
+// based sorting works as intended when using spew to stringify keys.
+func TestSortValuesWithSpew(t *testing.T) {
+ v := reflect.ValueOf
+
+ a := v("a")
+ b := v("b")
+ c := v("c")
+ tests := []sortTestCase{
+ // Ints.
+ {
+ []reflect.Value{v(2), v(1), v(3)},
+ []reflect.Value{v(1), v(2), v(3)},
+ },
+ // Strings.
+ {
+ []reflect.Value{b, a, c},
+ []reflect.Value{a, b, c},
+ },
+ // SortableStructs.
+ {
+ []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
+ []reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
+ },
+ // UnsortableStructs.
+ {
+ []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
+ []reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})},
+ },
+ }
+ cs := spew.ConfigState{DisableMethods: true, SpewKeys: true}
+ helpTestSortValues(tests, &cs, t)
+}
diff --git a/src/vendor/github.com/davecgh/go-spew/spew/dump_test.go b/src/vendor/github.com/davecgh/go-spew/spew/dump_test.go
new file mode 100644
index 0000000000..5aad9c7af0
--- /dev/null
+++ b/src/vendor/github.com/davecgh/go-spew/spew/dump_test.go
@@ -0,0 +1,1042 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+Test Summary:
+NOTE: For each test, a nil pointer, a single pointer and double pointer to the
+base test element are also tested to ensure proper indirection across all types.
+
+- Max int8, int16, int32, int64, int
+- Max uint8, uint16, uint32, uint64, uint
+- Boolean true and false
+- Standard complex64 and complex128
+- Array containing standard ints
+- Array containing type with custom formatter on pointer receiver only
+- Array containing interfaces
+- Array containing bytes
+- Slice containing standard float32 values
+- Slice containing type with custom formatter on pointer receiver only
+- Slice containing interfaces
+- Slice containing bytes
+- Nil slice
+- Standard string
+- Nil interface
+- Sub-interface
+- Map with string keys and int vals
+- Map with custom formatter type on pointer receiver only keys and vals
+- Map with interface keys and values
+- Map with nil interface value
+- Struct with primitives
+- Struct that contains another struct
+- Struct that contains custom type with Stringer pointer interface via both
+ exported and unexported fields
+- Struct that contains embedded struct and field to same struct
+- Uintptr to 0 (null pointer)
+- Uintptr address of real variable
+- Unsafe.Pointer to 0 (null pointer)
+- Unsafe.Pointer to address of real variable
+- Nil channel
+- Standard int channel
+- Function with no params and no returns
+- Function with param and no returns
+- Function with multiple params and multiple returns
+- Struct that is circular through self referencing
+- Structs that are circular through cross referencing
+- Structs that are indirectly circular
+- Type that panics in its Stringer interface
+*/
+
+package spew_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+ "unsafe"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+// dumpTest is used to describe a test to be performed against the Dump method.
+type dumpTest struct {
+ in interface{}
+ wants []string
+}
+
+// dumpTests houses all of the tests to be performed against the Dump method.
+var dumpTests = make([]dumpTest, 0)
+
+// addDumpTest is a helper method to append the passed input and desired result
+// to dumpTests
+func addDumpTest(in interface{}, wants ...string) {
+ test := dumpTest{in, wants}
+ dumpTests = append(dumpTests, test)
+}
+
+func addIntDumpTests() {
+ // Max int8.
+ v := int8(127)
+ nv := (*int8)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "int8"
+ vs := "127"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Max int16.
+ v2 := int16(32767)
+ nv2 := (*int16)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "int16"
+ v2s := "32767"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+
+ // Max int32.
+ v3 := int32(2147483647)
+ nv3 := (*int32)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "int32"
+ v3s := "2147483647"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+
+ // Max int64.
+ v4 := int64(9223372036854775807)
+ nv4 := (*int64)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "int64"
+ v4s := "9223372036854775807"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")()\n")
+
+ // Max int.
+ v5 := int(2147483647)
+ nv5 := (*int)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "int"
+ v5s := "2147483647"
+ addDumpTest(v5, "("+v5t+") "+v5s+"\n")
+ addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
+ addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
+ addDumpTest(nv5, "(*"+v5t+")()\n")
+}
+
+func addUintDumpTests() {
+ // Max uint8.
+ v := uint8(255)
+ nv := (*uint8)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "uint8"
+ vs := "255"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Max uint16.
+ v2 := uint16(65535)
+ nv2 := (*uint16)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uint16"
+ v2s := "65535"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+
+ // Max uint32.
+ v3 := uint32(4294967295)
+ nv3 := (*uint32)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "uint32"
+ v3s := "4294967295"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+
+ // Max uint64.
+ v4 := uint64(18446744073709551615)
+ nv4 := (*uint64)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "uint64"
+ v4s := "18446744073709551615"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")()\n")
+
+ // Max uint.
+ v5 := uint(4294967295)
+ nv5 := (*uint)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "uint"
+ v5s := "4294967295"
+ addDumpTest(v5, "("+v5t+") "+v5s+"\n")
+ addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
+ addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
+ addDumpTest(nv5, "(*"+v5t+")()\n")
+}
+
+func addBoolDumpTests() {
+ // Boolean true.
+ v := bool(true)
+ nv := (*bool)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "bool"
+ vs := "true"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Boolean false.
+ v2 := bool(false)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "bool"
+ v2s := "false"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+}
+
+func addFloatDumpTests() {
+ // Standard float32.
+ v := float32(3.1415)
+ nv := (*float32)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "float32"
+ vs := "3.1415"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Standard float64.
+ v2 := float64(3.1415926)
+ nv2 := (*float64)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "float64"
+ v2s := "3.1415926"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+}
+
+func addComplexDumpTests() {
+ // Standard complex64.
+ v := complex(float32(6), -2)
+ nv := (*complex64)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "complex64"
+ vs := "(6-2i)"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Standard complex128.
+ v2 := complex(float64(-6), 2)
+ nv2 := (*complex128)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "complex128"
+ v2s := "(-6+2i)"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+}
+
+func addArrayDumpTests() {
+ // Array containing standard ints.
+ v := [3]int{1, 2, 3}
+ vLen := fmt.Sprintf("%d", len(v))
+ vCap := fmt.Sprintf("%d", cap(v))
+ nv := (*[3]int)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "int"
+ vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 1,\n (" +
+ vt + ") 2,\n (" + vt + ") 3\n}"
+ addDumpTest(v, "([3]"+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*[3]"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**[3]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*[3]"+vt+")()\n")
+
+ // Array containing type with custom formatter on pointer receiver only.
+ v2i0 := pstringer("1")
+ v2i1 := pstringer("2")
+ v2i2 := pstringer("3")
+ v2 := [3]pstringer{v2i0, v2i1, v2i2}
+ v2i0Len := fmt.Sprintf("%d", len(v2i0))
+ v2i1Len := fmt.Sprintf("%d", len(v2i1))
+ v2i2Len := fmt.Sprintf("%d", len(v2i2))
+ v2Len := fmt.Sprintf("%d", len(v2))
+ v2Cap := fmt.Sprintf("%d", cap(v2))
+ nv2 := (*[3]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.pstringer"
+ v2sp := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t +
+ ") (len=" + v2i0Len + ") stringer 1,\n (" + v2t +
+ ") (len=" + v2i1Len + ") stringer 2,\n (" + v2t +
+ ") (len=" + v2i2Len + ") " + "stringer 3\n}"
+ v2s := v2sp
+ if spew.UnsafeDisabled {
+ v2s = "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t +
+ ") (len=" + v2i0Len + ") \"1\",\n (" + v2t + ") (len=" +
+ v2i1Len + ") \"2\",\n (" + v2t + ") (len=" + v2i2Len +
+ ") " + "\"3\"\n}"
+ }
+ addDumpTest(v2, "([3]"+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*[3]"+v2t+")("+v2Addr+")("+v2sp+")\n")
+ addDumpTest(&pv2, "(**[3]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2sp+")\n")
+ addDumpTest(nv2, "(*[3]"+v2t+")()\n")
+
+ // Array containing interfaces.
+ v3i0 := "one"
+ v3 := [3]interface{}{v3i0, int(2), uint(3)}
+ v3i0Len := fmt.Sprintf("%d", len(v3i0))
+ v3Len := fmt.Sprintf("%d", len(v3))
+ v3Cap := fmt.Sprintf("%d", cap(v3))
+ nv3 := (*[3]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "[3]interface {}"
+ v3t2 := "string"
+ v3t3 := "int"
+ v3t4 := "uint"
+ v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " +
+ "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" +
+ v3t4 + ") 3\n}"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+
+ // Array containing bytes.
+ v4 := [34]byte{
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32,
+ }
+ v4Len := fmt.Sprintf("%d", len(v4))
+ v4Cap := fmt.Sprintf("%d", cap(v4))
+ nv4 := (*[34]byte)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "[34]uint8"
+ v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
+ "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" +
+ " |............... |\n" +
+ " 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" +
+ " |!\"#$%&'()*+,-./0|\n" +
+ " 00000020 31 32 " +
+ " |12|\n}"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")()\n")
+}
+
+func addSliceDumpTests() {
+ // Slice containing standard float32 values.
+ v := []float32{3.14, 6.28, 12.56}
+ vLen := fmt.Sprintf("%d", len(v))
+ vCap := fmt.Sprintf("%d", cap(v))
+ nv := (*[]float32)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "float32"
+ vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 3.14,\n (" +
+ vt + ") 6.28,\n (" + vt + ") 12.56\n}"
+ addDumpTest(v, "([]"+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*[]"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**[]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*[]"+vt+")()\n")
+
+ // Slice containing type with custom formatter on pointer receiver only.
+ v2i0 := pstringer("1")
+ v2i1 := pstringer("2")
+ v2i2 := pstringer("3")
+ v2 := []pstringer{v2i0, v2i1, v2i2}
+ v2i0Len := fmt.Sprintf("%d", len(v2i0))
+ v2i1Len := fmt.Sprintf("%d", len(v2i1))
+ v2i2Len := fmt.Sprintf("%d", len(v2i2))
+ v2Len := fmt.Sprintf("%d", len(v2))
+ v2Cap := fmt.Sprintf("%d", cap(v2))
+ nv2 := (*[]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.pstringer"
+ v2s := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t + ") (len=" +
+ v2i0Len + ") stringer 1,\n (" + v2t + ") (len=" + v2i1Len +
+ ") stringer 2,\n (" + v2t + ") (len=" + v2i2Len + ") " +
+ "stringer 3\n}"
+ addDumpTest(v2, "([]"+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*[]"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**[]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*[]"+v2t+")()\n")
+
+ // Slice containing interfaces.
+ v3i0 := "one"
+ v3 := []interface{}{v3i0, int(2), uint(3), nil}
+ v3i0Len := fmt.Sprintf("%d", len(v3i0))
+ v3Len := fmt.Sprintf("%d", len(v3))
+ v3Cap := fmt.Sprintf("%d", cap(v3))
+ nv3 := (*[]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "[]interface {}"
+ v3t2 := "string"
+ v3t3 := "int"
+ v3t4 := "uint"
+ v3t5 := "interface {}"
+ v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " +
+ "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" +
+ v3t4 + ") 3,\n (" + v3t5 + ") \n}"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+
+ // Slice containing bytes.
+ v4 := []byte{
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32,
+ }
+ v4Len := fmt.Sprintf("%d", len(v4))
+ v4Cap := fmt.Sprintf("%d", cap(v4))
+ nv4 := (*[]byte)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "[]uint8"
+ v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
+ "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" +
+ " |............... |\n" +
+ " 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" +
+ " |!\"#$%&'()*+,-./0|\n" +
+ " 00000020 31 32 " +
+ " |12|\n}"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")()\n")
+
+ // Nil slice.
+ v5 := []int(nil)
+ nv5 := (*[]int)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "[]int"
+ v5s := ""
+ addDumpTest(v5, "("+v5t+") "+v5s+"\n")
+ addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
+ addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
+ addDumpTest(nv5, "(*"+v5t+")()\n")
+}
+
+func addStringDumpTests() {
+ // Standard string.
+ v := "test"
+ vLen := fmt.Sprintf("%d", len(v))
+ nv := (*string)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "string"
+ vs := "(len=" + vLen + ") \"test\""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+}
+
+func addInterfaceDumpTests() {
+ // Nil interface.
+ var v interface{}
+ nv := (*interface{})(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "interface {}"
+ vs := ""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Sub-interface.
+ v2 := interface{}(uint16(65535))
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uint16"
+ v2s := "65535"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+}
+
+func addMapDumpTests() {
+ // Map with string keys and int vals.
+ k := "one"
+ kk := "two"
+ m := map[string]int{k: 1, kk: 2}
+ klen := fmt.Sprintf("%d", len(k)) // not kLen to shut golint up
+ kkLen := fmt.Sprintf("%d", len(kk))
+ mLen := fmt.Sprintf("%d", len(m))
+ nilMap := map[string]int(nil)
+ nm := (*map[string]int)(nil)
+ pm := &m
+ mAddr := fmt.Sprintf("%p", pm)
+ pmAddr := fmt.Sprintf("%p", &pm)
+ mt := "map[string]int"
+ mt1 := "string"
+ mt2 := "int"
+ ms := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + klen + ") " +
+ "\"one\": (" + mt2 + ") 1,\n (" + mt1 + ") (len=" + kkLen +
+ ") \"two\": (" + mt2 + ") 2\n}"
+ ms2 := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + kkLen + ") " +
+ "\"two\": (" + mt2 + ") 2,\n (" + mt1 + ") (len=" + klen +
+ ") \"one\": (" + mt2 + ") 1\n}"
+ addDumpTest(m, "("+mt+") "+ms+"\n", "("+mt+") "+ms2+"\n")
+ addDumpTest(pm, "(*"+mt+")("+mAddr+")("+ms+")\n",
+ "(*"+mt+")("+mAddr+")("+ms2+")\n")
+ addDumpTest(&pm, "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms+")\n",
+ "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms2+")\n")
+ addDumpTest(nm, "(*"+mt+")()\n")
+ addDumpTest(nilMap, "("+mt+") \n")
+
+ // Map with custom formatter type on pointer receiver only keys and vals.
+ k2 := pstringer("one")
+ v2 := pstringer("1")
+ m2 := map[pstringer]pstringer{k2: v2}
+ k2Len := fmt.Sprintf("%d", len(k2))
+ v2Len := fmt.Sprintf("%d", len(v2))
+ m2Len := fmt.Sprintf("%d", len(m2))
+ nilMap2 := map[pstringer]pstringer(nil)
+ nm2 := (*map[pstringer]pstringer)(nil)
+ pm2 := &m2
+ m2Addr := fmt.Sprintf("%p", pm2)
+ pm2Addr := fmt.Sprintf("%p", &pm2)
+ m2t := "map[spew_test.pstringer]spew_test.pstringer"
+ m2t1 := "spew_test.pstringer"
+ m2t2 := "spew_test.pstringer"
+ m2s := "(len=" + m2Len + ") {\n (" + m2t1 + ") (len=" + k2Len + ") " +
+ "stringer one: (" + m2t2 + ") (len=" + v2Len + ") stringer 1\n}"
+ if spew.UnsafeDisabled {
+ m2s = "(len=" + m2Len + ") {\n (" + m2t1 + ") (len=" + k2Len +
+ ") " + "\"one\": (" + m2t2 + ") (len=" + v2Len +
+ ") \"1\"\n}"
+ }
+ addDumpTest(m2, "("+m2t+") "+m2s+"\n")
+ addDumpTest(pm2, "(*"+m2t+")("+m2Addr+")("+m2s+")\n")
+ addDumpTest(&pm2, "(**"+m2t+")("+pm2Addr+"->"+m2Addr+")("+m2s+")\n")
+ addDumpTest(nm2, "(*"+m2t+")()\n")
+ addDumpTest(nilMap2, "("+m2t+") \n")
+
+ // Map with interface keys and values.
+ k3 := "one"
+ k3Len := fmt.Sprintf("%d", len(k3))
+ m3 := map[interface{}]interface{}{k3: 1}
+ m3Len := fmt.Sprintf("%d", len(m3))
+ nilMap3 := map[interface{}]interface{}(nil)
+ nm3 := (*map[interface{}]interface{})(nil)
+ pm3 := &m3
+ m3Addr := fmt.Sprintf("%p", pm3)
+ pm3Addr := fmt.Sprintf("%p", &pm3)
+ m3t := "map[interface {}]interface {}"
+ m3t1 := "string"
+ m3t2 := "int"
+ m3s := "(len=" + m3Len + ") {\n (" + m3t1 + ") (len=" + k3Len + ") " +
+ "\"one\": (" + m3t2 + ") 1\n}"
+ addDumpTest(m3, "("+m3t+") "+m3s+"\n")
+ addDumpTest(pm3, "(*"+m3t+")("+m3Addr+")("+m3s+")\n")
+ addDumpTest(&pm3, "(**"+m3t+")("+pm3Addr+"->"+m3Addr+")("+m3s+")\n")
+ addDumpTest(nm3, "(*"+m3t+")()\n")
+ addDumpTest(nilMap3, "("+m3t+") \n")
+
+ // Map with nil interface value.
+ k4 := "nil"
+ k4Len := fmt.Sprintf("%d", len(k4))
+ m4 := map[string]interface{}{k4: nil}
+ m4Len := fmt.Sprintf("%d", len(m4))
+ nilMap4 := map[string]interface{}(nil)
+ nm4 := (*map[string]interface{})(nil)
+ pm4 := &m4
+ m4Addr := fmt.Sprintf("%p", pm4)
+ pm4Addr := fmt.Sprintf("%p", &pm4)
+ m4t := "map[string]interface {}"
+ m4t1 := "string"
+ m4t2 := "interface {}"
+ m4s := "(len=" + m4Len + ") {\n (" + m4t1 + ") (len=" + k4Len + ")" +
+ " \"nil\": (" + m4t2 + ") \n}"
+ addDumpTest(m4, "("+m4t+") "+m4s+"\n")
+ addDumpTest(pm4, "(*"+m4t+")("+m4Addr+")("+m4s+")\n")
+ addDumpTest(&pm4, "(**"+m4t+")("+pm4Addr+"->"+m4Addr+")("+m4s+")\n")
+ addDumpTest(nm4, "(*"+m4t+")()\n")
+ addDumpTest(nilMap4, "("+m4t+") \n")
+}
+
+func addStructDumpTests() {
+ // Struct with primitives.
+ type s1 struct {
+ a int8
+ b uint8
+ }
+ v := s1{127, 255}
+ nv := (*s1)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.s1"
+ vt2 := "int8"
+ vt3 := "uint8"
+ vs := "{\n a: (" + vt2 + ") 127,\n b: (" + vt3 + ") 255\n}"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Struct that contains another struct.
+ type s2 struct {
+ s1 s1
+ b bool
+ }
+ v2 := s2{s1{127, 255}, true}
+ nv2 := (*s2)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.s2"
+ v2t2 := "spew_test.s1"
+ v2t3 := "int8"
+ v2t4 := "uint8"
+ v2t5 := "bool"
+ v2s := "{\n s1: (" + v2t2 + ") {\n a: (" + v2t3 + ") 127,\n b: (" +
+ v2t4 + ") 255\n },\n b: (" + v2t5 + ") true\n}"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+
+ // Struct that contains custom type with Stringer pointer interface via both
+ // exported and unexported fields.
+ type s3 struct {
+ s pstringer
+ S pstringer
+ }
+ v3 := s3{"test", "test2"}
+ nv3 := (*s3)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "spew_test.s3"
+ v3t2 := "spew_test.pstringer"
+ v3s := "{\n s: (" + v3t2 + ") (len=4) stringer test,\n S: (" + v3t2 +
+ ") (len=5) stringer test2\n}"
+ v3sp := v3s
+ if spew.UnsafeDisabled {
+ v3s = "{\n s: (" + v3t2 + ") (len=4) \"test\",\n S: (" +
+ v3t2 + ") (len=5) \"test2\"\n}"
+ v3sp = "{\n s: (" + v3t2 + ") (len=4) \"test\",\n S: (" +
+ v3t2 + ") (len=5) stringer test2\n}"
+ }
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3sp+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3sp+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+
+ // Struct that contains embedded struct and field to same struct.
+ e := embed{"embedstr"}
+ eLen := fmt.Sprintf("%d", len("embedstr"))
+ v4 := embedwrap{embed: &e, e: &e}
+ nv4 := (*embedwrap)(nil)
+ pv4 := &v4
+ eAddr := fmt.Sprintf("%p", &e)
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "spew_test.embedwrap"
+ v4t2 := "spew_test.embed"
+ v4t3 := "string"
+ v4s := "{\n embed: (*" + v4t2 + ")(" + eAddr + ")({\n a: (" + v4t3 +
+ ") (len=" + eLen + ") \"embedstr\"\n }),\n e: (*" + v4t2 +
+ ")(" + eAddr + ")({\n a: (" + v4t3 + ") (len=" + eLen + ")" +
+ " \"embedstr\"\n })\n}"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")()\n")
+}
+
+func addUintptrDumpTests() {
+ // Null pointer.
+ v := uintptr(0)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "uintptr"
+ vs := ""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+
+ // Address of real variable.
+ i := 1
+ v2 := uintptr(unsafe.Pointer(&i))
+ nv2 := (*uintptr)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uintptr"
+ v2s := fmt.Sprintf("%p", &i)
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+}
+
+func addUnsafePointerDumpTests() {
+ // Null pointer.
+ v := unsafe.Pointer(uintptr(0))
+ nv := (*unsafe.Pointer)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "unsafe.Pointer"
+ vs := ""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Address of real variable.
+ i := 1
+ v2 := unsafe.Pointer(&i)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "unsafe.Pointer"
+ v2s := fmt.Sprintf("%p", &i)
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+}
+
+func addChanDumpTests() {
+ // Nil channel.
+ var v chan int
+ pv := &v
+ nv := (*chan int)(nil)
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "chan int"
+ vs := ""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Real channel.
+ v2 := make(chan int)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "chan int"
+ v2s := fmt.Sprintf("%p", v2)
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+}
+
+func addFuncDumpTests() {
+ // Function with no params and no returns.
+ v := addIntDumpTests
+ nv := (*func())(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "func()"
+ vs := fmt.Sprintf("%p", v)
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Function with param and no returns.
+ v2 := TestDump
+ nv2 := (*func(*testing.T))(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "func(*testing.T)"
+ v2s := fmt.Sprintf("%p", v2)
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+
+ // Function with multiple params and multiple returns.
+ var v3 = func(i int, s string) (b bool, err error) {
+ return true, nil
+ }
+ nv3 := (*func(int, string) (bool, error))(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "func(int, string) (bool, error)"
+ v3s := fmt.Sprintf("%p", v3)
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+}
+
+func addCircularDumpTests() {
+ // Struct that is circular through self referencing.
+ type circular struct {
+ c *circular
+ }
+ v := circular{nil}
+ v.c = &v
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.circular"
+ vs := "{\n c: (*" + vt + ")(" + vAddr + ")({\n c: (*" + vt + ")(" +
+ vAddr + ")()\n })\n}"
+ vs2 := "{\n c: (*" + vt + ")(" + vAddr + ")()\n}"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs2+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs2+")\n")
+
+ // Structs that are circular through cross referencing.
+ v2 := xref1{nil}
+ ts2 := xref2{&v2}
+ v2.ps2 = &ts2
+ pv2 := &v2
+ ts2Addr := fmt.Sprintf("%p", &ts2)
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.xref1"
+ v2t2 := "spew_test.xref2"
+ v2s := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t +
+ ")(" + v2Addr + ")({\n ps2: (*" + v2t2 + ")(" + ts2Addr +
+ ")()\n })\n })\n}"
+ v2s2 := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t +
+ ")(" + v2Addr + ")()\n })\n}"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s2+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s2+")\n")
+
+ // Structs that are indirectly circular.
+ v3 := indirCir1{nil}
+ tic2 := indirCir2{nil}
+ tic3 := indirCir3{&v3}
+ tic2.ps3 = &tic3
+ v3.ps2 = &tic2
+ pv3 := &v3
+ tic2Addr := fmt.Sprintf("%p", &tic2)
+ tic3Addr := fmt.Sprintf("%p", &tic3)
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "spew_test.indirCir1"
+ v3t2 := "spew_test.indirCir2"
+ v3t3 := "spew_test.indirCir3"
+ v3s := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 +
+ ")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr +
+ ")({\n ps2: (*" + v3t2 + ")(" + tic2Addr +
+ ")()\n })\n })\n })\n}"
+ v3s2 := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 +
+ ")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr +
+ ")()\n })\n })\n}"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s2+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s2+")\n")
+}
+
+func addPanicDumpTests() {
+ // Type that panics in its Stringer interface.
+ v := panicer(127)
+ nv := (*panicer)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.panicer"
+ vs := "(PANIC=test panic)127"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+}
+
+func addErrorDumpTests() {
+ // Type that has a custom Error interface.
+ v := customError(127)
+ nv := (*customError)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.customError"
+ vs := "error: 127"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+}
+
+// TestDump executes all of the tests described by dumpTests.
+func TestDump(t *testing.T) {
+ // Setup tests.
+ addIntDumpTests()
+ addUintDumpTests()
+ addBoolDumpTests()
+ addFloatDumpTests()
+ addComplexDumpTests()
+ addArrayDumpTests()
+ addSliceDumpTests()
+ addStringDumpTests()
+ addInterfaceDumpTests()
+ addMapDumpTests()
+ addStructDumpTests()
+ addUintptrDumpTests()
+ addUnsafePointerDumpTests()
+ addChanDumpTests()
+ addFuncDumpTests()
+ addCircularDumpTests()
+ addPanicDumpTests()
+ addErrorDumpTests()
+ addCgoDumpTests()
+
+ t.Logf("Running %d tests", len(dumpTests))
+ for i, test := range dumpTests {
+ buf := new(bytes.Buffer)
+ spew.Fdump(buf, test.in)
+ s := buf.String()
+ if testFailed(s, test.wants) {
+ t.Errorf("Dump #%d\n got: %s %s", i, s, stringizeWants(test.wants))
+ continue
+ }
+ }
+}
+
+func TestDumpSortedKeys(t *testing.T) {
+ cfg := spew.ConfigState{SortKeys: true}
+ s := cfg.Sdump(map[int]string{1: "1", 3: "3", 2: "2"})
+ expected := "(map[int]string) (len=3) {\n(int) 1: (string) (len=1) " +
+ "\"1\",\n(int) 2: (string) (len=1) \"2\",\n(int) 3: (string) " +
+ "(len=1) \"3\"\n" +
+ "}\n"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch:\n %v %v", s, expected)
+ }
+
+ s = cfg.Sdump(map[stringer]int{"1": 1, "3": 3, "2": 2})
+ expected = "(map[spew_test.stringer]int) (len=3) {\n" +
+ "(spew_test.stringer) (len=1) stringer 1: (int) 1,\n" +
+ "(spew_test.stringer) (len=1) stringer 2: (int) 2,\n" +
+ "(spew_test.stringer) (len=1) stringer 3: (int) 3\n" +
+ "}\n"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch:\n %v %v", s, expected)
+ }
+
+ s = cfg.Sdump(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2})
+ expected = "(map[spew_test.pstringer]int) (len=3) {\n" +
+ "(spew_test.pstringer) (len=1) stringer 1: (int) 1,\n" +
+ "(spew_test.pstringer) (len=1) stringer 2: (int) 2,\n" +
+ "(spew_test.pstringer) (len=1) stringer 3: (int) 3\n" +
+ "}\n"
+ if spew.UnsafeDisabled {
+ expected = "(map[spew_test.pstringer]int) (len=3) {\n" +
+ "(spew_test.pstringer) (len=1) \"1\": (int) 1,\n" +
+ "(spew_test.pstringer) (len=1) \"2\": (int) 2,\n" +
+ "(spew_test.pstringer) (len=1) \"3\": (int) 3\n" +
+ "}\n"
+ }
+ if s != expected {
+ t.Errorf("Sorted keys mismatch:\n %v %v", s, expected)
+ }
+
+ s = cfg.Sdump(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2})
+ expected = "(map[spew_test.customError]int) (len=3) {\n" +
+ "(spew_test.customError) error: 1: (int) 1,\n" +
+ "(spew_test.customError) error: 2: (int) 2,\n" +
+ "(spew_test.customError) error: 3: (int) 3\n" +
+ "}\n"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch:\n %v %v", s, expected)
+ }
+
+}
diff --git a/src/vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go b/src/vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
new file mode 100644
index 0000000000..6ab180809a
--- /dev/null
+++ b/src/vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
@@ -0,0 +1,99 @@
+// Copyright (c) 2013-2016 Dave Collins
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when both cgo is supported and "-tags testcgo" is added to the go test
+// command line. This means the cgo tests are only added (and hence run) when
+// specifially requested. This configuration is used because spew itself
+// does not require cgo to run even though it does handle certain cgo types
+// specially. Rather than forcing all clients to require cgo and an external
+// C compiler just to run the tests, this scheme makes them optional.
+// +build cgo,testcgo
+
+package spew_test
+
+import (
+ "fmt"
+
+ "github.com/davecgh/go-spew/spew/testdata"
+)
+
+func addCgoDumpTests() {
+ // C char pointer.
+ v := testdata.GetCgoCharPointer()
+ nv := testdata.GetCgoNullCharPointer()
+ pv := &v
+ vcAddr := fmt.Sprintf("%p", v)
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "*testdata._Ctype_char"
+ vs := "116"
+ addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n")
+ addDumpTest(nv, "("+vt+")()\n")
+
+ // C char array.
+ v2, v2l, v2c := testdata.GetCgoCharArray()
+ v2Len := fmt.Sprintf("%d", v2l)
+ v2Cap := fmt.Sprintf("%d", v2c)
+ v2t := "[6]testdata._Ctype_char"
+ v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " +
+ "{\n 00000000 74 65 73 74 32 00 " +
+ " |test2.|\n}"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+
+ // C unsigned char array.
+ v3, v3l, v3c := testdata.GetCgoUnsignedCharArray()
+ v3Len := fmt.Sprintf("%d", v3l)
+ v3Cap := fmt.Sprintf("%d", v3c)
+ v3t := "[6]testdata._Ctype_unsignedchar"
+ v3t2 := "[6]testdata._Ctype_uchar"
+ v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " +
+ "{\n 00000000 74 65 73 74 33 00 " +
+ " |test3.|\n}"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n", "("+v3t2+") "+v3s+"\n")
+
+ // C signed char array.
+ v4, v4l, v4c := testdata.GetCgoSignedCharArray()
+ v4Len := fmt.Sprintf("%d", v4l)
+ v4Cap := fmt.Sprintf("%d", v4c)
+ v4t := "[6]testdata._Ctype_schar"
+ v4t2 := "testdata._Ctype_schar"
+ v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
+ "{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 +
+ ") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 +
+ ") 0\n}"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+
+ // C uint8_t array.
+ v5, v5l, v5c := testdata.GetCgoUint8tArray()
+ v5Len := fmt.Sprintf("%d", v5l)
+ v5Cap := fmt.Sprintf("%d", v5c)
+ v5t := "[6]testdata._Ctype_uint8_t"
+ v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " +
+ "{\n 00000000 74 65 73 74 35 00 " +
+ " |test5.|\n}"
+ addDumpTest(v5, "("+v5t+") "+v5s+"\n")
+
+ // C typedefed unsigned char array.
+ v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
+ v6Len := fmt.Sprintf("%d", v6l)
+ v6Cap := fmt.Sprintf("%d", v6c)
+ v6t := "[6]testdata._Ctype_custom_uchar_t"
+ v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " +
+ "{\n 00000000 74 65 73 74 36 00 " +
+ " |test6.|\n}"
+ addDumpTest(v6, "("+v6t+") "+v6s+"\n")
+}
diff --git a/src/vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go b/src/vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
new file mode 100644
index 0000000000..52a0971fb3
--- /dev/null
+++ b/src/vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 Dave Collins
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when either cgo is not supported or "-tags testcgo" is not added to the go
+// test command line. This file intentionally does not setup any cgo tests in
+// this scenario.
+// +build !cgo !testcgo
+
+package spew_test
+
+func addCgoDumpTests() {
+ // Don't add any tests for cgo since this file is only compiled when
+ // there should not be any cgo tests.
+}
diff --git a/src/vendor/github.com/davecgh/go-spew/spew/example_test.go b/src/vendor/github.com/davecgh/go-spew/spew/example_test.go
new file mode 100644
index 0000000000..c6ec8c6d59
--- /dev/null
+++ b/src/vendor/github.com/davecgh/go-spew/spew/example_test.go
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew_test
+
+import (
+ "fmt"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+type Flag int
+
+const (
+ flagOne Flag = iota
+ flagTwo
+)
+
+var flagStrings = map[Flag]string{
+ flagOne: "flagOne",
+ flagTwo: "flagTwo",
+}
+
+func (f Flag) String() string {
+ if s, ok := flagStrings[f]; ok {
+ return s
+ }
+ return fmt.Sprintf("Unknown flag (%d)", int(f))
+}
+
+type Bar struct {
+ data uintptr
+}
+
+type Foo struct {
+ unexportedField Bar
+ ExportedField map[interface{}]interface{}
+}
+
+// This example demonstrates how to use Dump to dump variables to stdout.
+func ExampleDump() {
+ // The following package level declarations are assumed for this example:
+ /*
+ type Flag int
+
+ const (
+ flagOne Flag = iota
+ flagTwo
+ )
+
+ var flagStrings = map[Flag]string{
+ flagOne: "flagOne",
+ flagTwo: "flagTwo",
+ }
+
+ func (f Flag) String() string {
+ if s, ok := flagStrings[f]; ok {
+ return s
+ }
+ return fmt.Sprintf("Unknown flag (%d)", int(f))
+ }
+
+ type Bar struct {
+ data uintptr
+ }
+
+ type Foo struct {
+ unexportedField Bar
+ ExportedField map[interface{}]interface{}
+ }
+ */
+
+ // Setup some sample data structures for the example.
+ bar := Bar{uintptr(0)}
+ s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
+ f := Flag(5)
+ b := []byte{
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32,
+ }
+
+ // Dump!
+ spew.Dump(s1, f, b)
+
+ // Output:
+ // (spew_test.Foo) {
+ // unexportedField: (spew_test.Bar) {
+ // data: (uintptr)
+ // },
+ // ExportedField: (map[interface {}]interface {}) (len=1) {
+ // (string) (len=3) "one": (bool) true
+ // }
+ // }
+ // (spew_test.Flag) Unknown flag (5)
+ // ([]uint8) (len=34 cap=34) {
+ // 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
+ // 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
+ // 00000020 31 32 |12|
+ // }
+ //
+}
+
+// This example demonstrates how to use Printf to display a variable with a
+// format string and inline formatting.
+func ExamplePrintf() {
+ // Create a double pointer to a uint 8.
+ ui8 := uint8(5)
+ pui8 := &ui8
+ ppui8 := &pui8
+
+ // Create a circular data type.
+ type circular struct {
+ ui8 uint8
+ c *circular
+ }
+ c := circular{ui8: 1}
+ c.c = &c
+
+ // Print!
+ spew.Printf("ppui8: %v\n", ppui8)
+ spew.Printf("circular: %v\n", c)
+
+ // Output:
+ // ppui8: <**>5
+ // circular: {1 <*>{1 <*>}}
+}
+
+// This example demonstrates how to use a ConfigState.
+func ExampleConfigState() {
+ // Modify the indent level of the ConfigState only. The global
+ // configuration is not modified.
+ scs := spew.ConfigState{Indent: "\t"}
+
+ // Output using the ConfigState instance.
+ v := map[string]int{"one": 1}
+ scs.Printf("v: %v\n", v)
+ scs.Dump(v)
+
+ // Output:
+ // v: map[one:1]
+ // (map[string]int) (len=1) {
+ // (string) (len=3) "one": (int) 1
+ // }
+}
+
+// This example demonstrates how to use ConfigState.Dump to dump variables to
+// stdout
+func ExampleConfigState_Dump() {
+ // See the top-level Dump example for details on the types used in this
+ // example.
+
+ // Create two ConfigState instances with different indentation.
+ scs := spew.ConfigState{Indent: "\t"}
+ scs2 := spew.ConfigState{Indent: " "}
+
+ // Setup some sample data structures for the example.
+ bar := Bar{uintptr(0)}
+ s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
+
+ // Dump using the ConfigState instances.
+ scs.Dump(s1)
+ scs2.Dump(s1)
+
+ // Output:
+ // (spew_test.Foo) {
+ // unexportedField: (spew_test.Bar) {
+ // data: (uintptr)
+ // },
+ // ExportedField: (map[interface {}]interface {}) (len=1) {
+ // (string) (len=3) "one": (bool) true
+ // }
+ // }
+ // (spew_test.Foo) {
+ // unexportedField: (spew_test.Bar) {
+ // data: (uintptr)
+ // },
+ // ExportedField: (map[interface {}]interface {}) (len=1) {
+ // (string) (len=3) "one": (bool) true
+ // }
+ // }
+ //
+}
+
+// This example demonstrates how to use ConfigState.Printf to display a variable
+// with a format string and inline formatting.
+func ExampleConfigState_Printf() {
+ // See the top-level Dump example for details on the types used in this
+ // example.
+
+ // Create two ConfigState instances and modify the method handling of the
+ // first ConfigState only.
+ scs := spew.NewDefaultConfig()
+ scs2 := spew.NewDefaultConfig()
+ scs.DisableMethods = true
+
+ // Alternatively
+ // scs := spew.ConfigState{Indent: " ", DisableMethods: true}
+ // scs2 := spew.ConfigState{Indent: " "}
+
+ // This is of type Flag which implements a Stringer and has raw value 1.
+ f := flagTwo
+
+ // Dump using the ConfigState instances.
+ scs.Printf("f: %v\n", f)
+ scs2.Printf("f: %v\n", f)
+
+ // Output:
+ // f: 1
+ // f: flagTwo
+}
diff --git a/src/vendor/github.com/davecgh/go-spew/spew/format_test.go b/src/vendor/github.com/davecgh/go-spew/spew/format_test.go
new file mode 100644
index 0000000000..f9b93abe86
--- /dev/null
+++ b/src/vendor/github.com/davecgh/go-spew/spew/format_test.go
@@ -0,0 +1,1558 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+Test Summary:
+NOTE: For each test, a nil pointer, a single pointer and double pointer to the
+base test element are also tested to ensure proper indirection across all types.
+
+- Max int8, int16, int32, int64, int
+- Max uint8, uint16, uint32, uint64, uint
+- Boolean true and false
+- Standard complex64 and complex128
+- Array containing standard ints
+- Array containing type with custom formatter on pointer receiver only
+- Array containing interfaces
+- Slice containing standard float32 values
+- Slice containing type with custom formatter on pointer receiver only
+- Slice containing interfaces
+- Nil slice
+- Standard string
+- Nil interface
+- Sub-interface
+- Map with string keys and int vals
+- Map with custom formatter type on pointer receiver only keys and vals
+- Map with interface keys and values
+- Map with nil interface value
+- Struct with primitives
+- Struct that contains another struct
+- Struct that contains custom type with Stringer pointer interface via both
+ exported and unexported fields
+- Struct that contains embedded struct and field to same struct
+- Uintptr to 0 (null pointer)
+- Uintptr address of real variable
+- Unsafe.Pointer to 0 (null pointer)
+- Unsafe.Pointer to address of real variable
+- Nil channel
+- Standard int channel
+- Function with no params and no returns
+- Function with param and no returns
+- Function with multiple params and multiple returns
+- Struct that is circular through self referencing
+- Structs that are circular through cross referencing
+- Structs that are indirectly circular
+- Type that panics in its Stringer interface
+- Type that has a custom Error interface
+- %x passthrough with uint
+- %#x passthrough with uint
+- %f passthrough with precision
+- %f passthrough with width and precision
+- %d passthrough with width
+- %q passthrough with string
+*/
+
+package spew_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+ "unsafe"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+// formatterTest is used to describe a test to be performed against NewFormatter.
+type formatterTest struct {
+ format string
+ in interface{}
+ wants []string
+}
+
+// formatterTests houses all of the tests to be performed against NewFormatter.
+var formatterTests = make([]formatterTest, 0)
+
+// addFormatterTest is a helper method to append the passed input and desired
+// result to formatterTests.
+func addFormatterTest(format string, in interface{}, wants ...string) {
+ test := formatterTest{format, in, wants}
+ formatterTests = append(formatterTests, test)
+}
+
+func addIntFormatterTests() {
+ // Max int8.
+ v := int8(127)
+ nv := (*int8)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "int8"
+ vs := "127"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Max int16.
+ v2 := int16(32767)
+ nv2 := (*int16)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "int16"
+ v2s := "32767"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%v", nv2, "")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"")
+
+ // Max int32.
+ v3 := int32(2147483647)
+ nv3 := (*int32)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "int32"
+ v3s := "2147483647"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%v", nv3, "")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"")
+
+ // Max int64.
+ v4 := int64(9223372036854775807)
+ nv4 := (*int64)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "int64"
+ v4s := "9223372036854775807"
+ addFormatterTest("%v", v4, v4s)
+ addFormatterTest("%v", pv4, "<*>"+v4s)
+ addFormatterTest("%v", &pv4, "<**>"+v4s)
+ addFormatterTest("%v", nv4, "")
+ addFormatterTest("%+v", v4, v4s)
+ addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s)
+ addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%+v", nv4, "")
+ addFormatterTest("%#v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s)
+ addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s)
+ addFormatterTest("%#v", nv4, "(*"+v4t+")"+"")
+ addFormatterTest("%#+v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"")
+
+ // Max int.
+ v5 := int(2147483647)
+ nv5 := (*int)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "int"
+ v5s := "2147483647"
+ addFormatterTest("%v", v5, v5s)
+ addFormatterTest("%v", pv5, "<*>"+v5s)
+ addFormatterTest("%v", &pv5, "<**>"+v5s)
+ addFormatterTest("%v", nv5, "")
+ addFormatterTest("%+v", v5, v5s)
+ addFormatterTest("%+v", pv5, "<*>("+v5Addr+")"+v5s)
+ addFormatterTest("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s)
+ addFormatterTest("%+v", nv5, "")
+ addFormatterTest("%#v", v5, "("+v5t+")"+v5s)
+ addFormatterTest("%#v", pv5, "(*"+v5t+")"+v5s)
+ addFormatterTest("%#v", &pv5, "(**"+v5t+")"+v5s)
+ addFormatterTest("%#v", nv5, "(*"+v5t+")"+"")
+ addFormatterTest("%#+v", v5, "("+v5t+")"+v5s)
+ addFormatterTest("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s)
+ addFormatterTest("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s)
+ addFormatterTest("%#+v", nv5, "(*"+v5t+")"+"")
+}
+
+func addUintFormatterTests() {
+ // Max uint8.
+ v := uint8(255)
+ nv := (*uint8)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "uint8"
+ vs := "255"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Max uint16.
+ v2 := uint16(65535)
+ nv2 := (*uint16)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uint16"
+ v2s := "65535"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%v", nv2, "")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"")
+
+ // Max uint32.
+ v3 := uint32(4294967295)
+ nv3 := (*uint32)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "uint32"
+ v3s := "4294967295"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%v", nv3, "")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"")
+
+ // Max uint64.
+ v4 := uint64(18446744073709551615)
+ nv4 := (*uint64)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "uint64"
+ v4s := "18446744073709551615"
+ addFormatterTest("%v", v4, v4s)
+ addFormatterTest("%v", pv4, "<*>"+v4s)
+ addFormatterTest("%v", &pv4, "<**>"+v4s)
+ addFormatterTest("%v", nv4, "")
+ addFormatterTest("%+v", v4, v4s)
+ addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s)
+ addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%+v", nv4, "")
+ addFormatterTest("%#v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s)
+ addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s)
+ addFormatterTest("%#v", nv4, "(*"+v4t+")"+"")
+ addFormatterTest("%#+v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"")
+
+ // Max uint.
+ v5 := uint(4294967295)
+ nv5 := (*uint)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "uint"
+ v5s := "4294967295"
+ addFormatterTest("%v", v5, v5s)
+ addFormatterTest("%v", pv5, "<*>"+v5s)
+ addFormatterTest("%v", &pv5, "<**>"+v5s)
+ addFormatterTest("%v", nv5, "")
+ addFormatterTest("%+v", v5, v5s)
+ addFormatterTest("%+v", pv5, "<*>("+v5Addr+")"+v5s)
+ addFormatterTest("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s)
+ addFormatterTest("%+v", nv5, "")
+ addFormatterTest("%#v", v5, "("+v5t+")"+v5s)
+ addFormatterTest("%#v", pv5, "(*"+v5t+")"+v5s)
+ addFormatterTest("%#v", &pv5, "(**"+v5t+")"+v5s)
+ addFormatterTest("%#v", nv5, "(*"+v5t+")"+"")
+ addFormatterTest("%#+v", v5, "("+v5t+")"+v5s)
+ addFormatterTest("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s)
+ addFormatterTest("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s)
+ addFormatterTest("%#v", nv5, "(*"+v5t+")"+"")
+}
+
+func addBoolFormatterTests() {
+ // Boolean true.
+ v := bool(true)
+ nv := (*bool)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "bool"
+ vs := "true"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Boolean false.
+ v2 := bool(false)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "bool"
+ v2s := "false"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+}
+
+func addFloatFormatterTests() {
+ // Standard float32.
+ v := float32(3.1415)
+ nv := (*float32)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "float32"
+ vs := "3.1415"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Standard float64.
+ v2 := float64(3.1415926)
+ nv2 := (*float64)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "float64"
+ v2s := "3.1415926"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"")
+}
+
+func addComplexFormatterTests() {
+ // Standard complex64.
+ v := complex(float32(6), -2)
+ nv := (*complex64)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "complex64"
+ vs := "(6-2i)"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Standard complex128.
+ v2 := complex(float64(-6), 2)
+ nv2 := (*complex128)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "complex128"
+ v2s := "(-6+2i)"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"")
+}
+
+func addArrayFormatterTests() {
+ // Array containing standard ints.
+ v := [3]int{1, 2, 3}
+ nv := (*[3]int)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "[3]int"
+ vs := "[1 2 3]"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Array containing type with custom formatter on pointer receiver only.
+ v2 := [3]pstringer{"1", "2", "3"}
+ nv2 := (*[3]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "[3]spew_test.pstringer"
+ v2sp := "[stringer 1 stringer 2 stringer 3]"
+ v2s := v2sp
+ if spew.UnsafeDisabled {
+ v2s = "[1 2 3]"
+ }
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2sp)
+ addFormatterTest("%v", &pv2, "<**>"+v2sp)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2sp)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2sp)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2sp)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2sp)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2sp)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2sp)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"")
+
+ // Array containing interfaces.
+ v3 := [3]interface{}{"one", int(2), uint(3)}
+ nv3 := (*[3]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "[3]interface {}"
+ v3t2 := "string"
+ v3t3 := "int"
+ v3t4 := "uint"
+ v3s := "[one 2 3]"
+ v3s2 := "[(" + v3t2 + ")one (" + v3t3 + ")2 (" + v3t4 + ")3]"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"")
+}
+
+func addSliceFormatterTests() {
+ // Slice containing standard float32 values.
+ v := []float32{3.14, 6.28, 12.56}
+ nv := (*[]float32)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "[]float32"
+ vs := "[3.14 6.28 12.56]"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Slice containing type with custom formatter on pointer receiver only.
+ v2 := []pstringer{"1", "2", "3"}
+ nv2 := (*[]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "[]spew_test.pstringer"
+ v2s := "[stringer 1 stringer 2 stringer 3]"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"")
+
+ // Slice containing interfaces.
+ v3 := []interface{}{"one", int(2), uint(3), nil}
+ nv3 := (*[]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "[]interface {}"
+ v3t2 := "string"
+ v3t3 := "int"
+ v3t4 := "uint"
+ v3t5 := "interface {}"
+ v3s := "[one 2 3 ]"
+ v3s2 := "[(" + v3t2 + ")one (" + v3t3 + ")2 (" + v3t4 + ")3 (" + v3t5 +
+ ")]"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"")
+
+ // Nil slice.
+ var v4 []int
+ nv4 := (*[]int)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "[]int"
+ v4s := ""
+ addFormatterTest("%v", v4, v4s)
+ addFormatterTest("%v", pv4, "<*>"+v4s)
+ addFormatterTest("%v", &pv4, "<**>"+v4s)
+ addFormatterTest("%+v", nv4, "")
+ addFormatterTest("%+v", v4, v4s)
+ addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s)
+ addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%+v", nv4, "")
+ addFormatterTest("%#v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s)
+ addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s)
+ addFormatterTest("%#v", nv4, "(*"+v4t+")"+"")
+ addFormatterTest("%#+v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"")
+}
+
+func addStringFormatterTests() {
+ // Standard string.
+ v := "test"
+ nv := (*string)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "string"
+ vs := "test"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+}
+
+func addInterfaceFormatterTests() {
+ // Nil interface.
+ var v interface{}
+ nv := (*interface{})(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "interface {}"
+ vs := ""
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Sub-interface.
+ v2 := interface{}(uint16(65535))
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uint16"
+ v2s := "65535"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+}
+
+func addMapFormatterTests() {
+ // Map with string keys and int vals.
+ v := map[string]int{"one": 1, "two": 2}
+ nilMap := map[string]int(nil)
+ nv := (*map[string]int)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "map[string]int"
+ vs := "map[one:1 two:2]"
+ vs2 := "map[two:2 one:1]"
+ addFormatterTest("%v", v, vs, vs2)
+ addFormatterTest("%v", pv, "<*>"+vs, "<*>"+vs2)
+ addFormatterTest("%v", &pv, "<**>"+vs, "<**>"+vs2)
+ addFormatterTest("%+v", nilMap, "")
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs, vs2)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs, "<*>("+vAddr+")"+vs2)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs,
+ "<**>("+pvAddr+"->"+vAddr+")"+vs2)
+ addFormatterTest("%+v", nilMap, "")
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs, "("+vt+")"+vs2)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs, "(*"+vt+")"+vs2)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs, "(**"+vt+")"+vs2)
+ addFormatterTest("%#v", nilMap, "("+vt+")"+"")
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs, "("+vt+")"+vs2)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs,
+ "(*"+vt+")("+vAddr+")"+vs2)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs,
+ "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs2)
+ addFormatterTest("%#+v", nilMap, "("+vt+")"+"")
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Map with custom formatter type on pointer receiver only keys and vals.
+ v2 := map[pstringer]pstringer{"one": "1"}
+ nv2 := (*map[pstringer]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "map[spew_test.pstringer]spew_test.pstringer"
+ v2s := "map[stringer one:stringer 1]"
+ if spew.UnsafeDisabled {
+ v2s = "map[one:1]"
+ }
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"")
+
+ // Map with interface keys and values.
+ v3 := map[interface{}]interface{}{"one": 1}
+ nv3 := (*map[interface{}]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "map[interface {}]interface {}"
+ v3t1 := "string"
+ v3t2 := "int"
+ v3s := "map[one:1]"
+ v3s2 := "map[(" + v3t1 + ")one:(" + v3t2 + ")1]"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"")
+
+ // Map with nil interface value
+ v4 := map[string]interface{}{"nil": nil}
+ nv4 := (*map[string]interface{})(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "map[string]interface {}"
+ v4t1 := "interface {}"
+ v4s := "map[nil:]"
+ v4s2 := "map[nil:(" + v4t1 + ")]"
+ addFormatterTest("%v", v4, v4s)
+ addFormatterTest("%v", pv4, "<*>"+v4s)
+ addFormatterTest("%v", &pv4, "<**>"+v4s)
+ addFormatterTest("%+v", nv4, "")
+ addFormatterTest("%+v", v4, v4s)
+ addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s)
+ addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%+v", nv4, "")
+ addFormatterTest("%#v", v4, "("+v4t+")"+v4s2)
+ addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s2)
+ addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s2)
+ addFormatterTest("%#v", nv4, "(*"+v4t+")"+"")
+ addFormatterTest("%#+v", v4, "("+v4t+")"+v4s2)
+ addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s2)
+ addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s2)
+ addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"")
+}
+
+func addStructFormatterTests() {
+ // Struct with primitives.
+ type s1 struct {
+ a int8
+ b uint8
+ }
+ v := s1{127, 255}
+ nv := (*s1)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.s1"
+ vt2 := "int8"
+ vt3 := "uint8"
+ vs := "{127 255}"
+ vs2 := "{a:127 b:255}"
+ vs3 := "{a:(" + vt2 + ")127 b:(" + vt3 + ")255}"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs2)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs2)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs2)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs3)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs3)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs3)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs3)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs3)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs3)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Struct that contains another struct.
+ type s2 struct {
+ s1 s1
+ b bool
+ }
+ v2 := s2{s1{127, 255}, true}
+ nv2 := (*s2)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.s2"
+ v2t2 := "spew_test.s1"
+ v2t3 := "int8"
+ v2t4 := "uint8"
+ v2t5 := "bool"
+ v2s := "{{127 255} true}"
+ v2s2 := "{s1:{a:127 b:255} b:true}"
+ v2s3 := "{s1:(" + v2t2 + "){a:(" + v2t3 + ")127 b:(" + v2t4 + ")255} b:(" +
+ v2t5 + ")true}"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%+v", v2, v2s2)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s2)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s2)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s3)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s3)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s3)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s3)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s3)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s3)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"")
+
+ // Struct that contains custom type with Stringer pointer interface via both
+ // exported and unexported fields.
+ type s3 struct {
+ s pstringer
+ S pstringer
+ }
+ v3 := s3{"test", "test2"}
+ nv3 := (*s3)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "spew_test.s3"
+ v3t2 := "spew_test.pstringer"
+ v3s := "{stringer test stringer test2}"
+ v3sp := v3s
+ v3s2 := "{s:stringer test S:stringer test2}"
+ v3s2p := v3s2
+ v3s3 := "{s:(" + v3t2 + ")stringer test S:(" + v3t2 + ")stringer test2}"
+ v3s3p := v3s3
+ if spew.UnsafeDisabled {
+ v3s = "{test test2}"
+ v3sp = "{test stringer test2}"
+ v3s2 = "{s:test S:test2}"
+ v3s2p = "{s:test S:stringer test2}"
+ v3s3 = "{s:(" + v3t2 + ")test S:(" + v3t2 + ")test2}"
+ v3s3p = "{s:(" + v3t2 + ")test S:(" + v3t2 + ")stringer test2}"
+ }
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3sp)
+ addFormatterTest("%v", &pv3, "<**>"+v3sp)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%+v", v3, v3s2)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s2p)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s2p)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s3)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s3p)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s3p)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s3)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s3p)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s3p)
+ addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"")
+
+ // Struct that contains embedded struct and field to same struct.
+ e := embed{"embedstr"}
+ v4 := embedwrap{embed: &e, e: &e}
+ nv4 := (*embedwrap)(nil)
+ pv4 := &v4
+ eAddr := fmt.Sprintf("%p", &e)
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "spew_test.embedwrap"
+ v4t2 := "spew_test.embed"
+ v4t3 := "string"
+ v4s := "{<*>{embedstr} <*>{embedstr}}"
+ v4s2 := "{embed:<*>(" + eAddr + "){a:embedstr} e:<*>(" + eAddr +
+ "){a:embedstr}}"
+ v4s3 := "{embed:(*" + v4t2 + "){a:(" + v4t3 + ")embedstr} e:(*" + v4t2 +
+ "){a:(" + v4t3 + ")embedstr}}"
+ v4s4 := "{embed:(*" + v4t2 + ")(" + eAddr + "){a:(" + v4t3 +
+ ")embedstr} e:(*" + v4t2 + ")(" + eAddr + "){a:(" + v4t3 + ")embedstr}}"
+ addFormatterTest("%v", v4, v4s)
+ addFormatterTest("%v", pv4, "<*>"+v4s)
+ addFormatterTest("%v", &pv4, "<**>"+v4s)
+ addFormatterTest("%+v", nv4, "")
+ addFormatterTest("%+v", v4, v4s2)
+ addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s2)
+ addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s2)
+ addFormatterTest("%+v", nv4, "")
+ addFormatterTest("%#v", v4, "("+v4t+")"+v4s3)
+ addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s3)
+ addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s3)
+ addFormatterTest("%#v", nv4, "(*"+v4t+")"+"")
+ addFormatterTest("%#+v", v4, "("+v4t+")"+v4s4)
+ addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s4)
+ addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s4)
+ addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"")
+}
+
+func addUintptrFormatterTests() {
+ // Null pointer.
+ v := uintptr(0)
+ nv := (*uintptr)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "uintptr"
+ vs := ""
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Address of real variable.
+ i := 1
+ v2 := uintptr(unsafe.Pointer(&i))
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uintptr"
+ v2s := fmt.Sprintf("%p", &i)
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+}
+
+func addUnsafePointerFormatterTests() {
+ // Null pointer.
+ v := unsafe.Pointer(uintptr(0))
+ nv := (*unsafe.Pointer)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "unsafe.Pointer"
+ vs := ""
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Address of real variable.
+ i := 1
+ v2 := unsafe.Pointer(&i)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "unsafe.Pointer"
+ v2s := fmt.Sprintf("%p", &i)
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+}
+
+func addChanFormatterTests() {
+ // Nil channel.
+ var v chan int
+ pv := &v
+ nv := (*chan int)(nil)
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "chan int"
+ vs := ""
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Real channel.
+ v2 := make(chan int)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "chan int"
+ v2s := fmt.Sprintf("%p", v2)
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+}
+
+func addFuncFormatterTests() {
+ // Function with no params and no returns.
+ v := addIntFormatterTests
+ nv := (*func())(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "func()"
+ vs := fmt.Sprintf("%p", v)
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"")
+
+ // Function with param and no returns.
+ v2 := TestFormatter
+ nv2 := (*func(*testing.T))(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "func(*testing.T)"
+ v2s := fmt.Sprintf("%p", v2)
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"")
+
+ // Function with multiple params and multiple returns.
+ var v3 = func(i int, s string) (b bool, err error) {
+ return true, nil
+ }
+ nv3 := (*func(int, string) (bool, error))(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "func(int, string) (bool, error)"
+ v3s := fmt.Sprintf("%p", v3)
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"")
+}
+
+func addCircularFormatterTests() {
+ // Struct that is circular through self referencing.
+ type circular struct {
+ c *circular
+ }
+ v := circular{nil}
+ v.c = &v
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.circular"
+ vs := "{<*>{<*>}}"
+ vs2 := "{<*>}"
+ vs3 := "{c:<*>(" + vAddr + "){c:<*>(" + vAddr + ")}}"
+ vs4 := "{c:<*>(" + vAddr + ")}"
+ vs5 := "{c:(*" + vt + "){c:(*" + vt + ")}}"
+ vs6 := "{c:(*" + vt + ")}"
+ vs7 := "{c:(*" + vt + ")(" + vAddr + "){c:(*" + vt + ")(" + vAddr +
+ ")}}"
+ vs8 := "{c:(*" + vt + ")(" + vAddr + ")}"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs2)
+ addFormatterTest("%v", &pv, "<**>"+vs2)
+ addFormatterTest("%+v", v, vs3)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs4)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs4)
+ addFormatterTest("%#v", v, "("+vt+")"+vs5)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs6)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs6)
+ addFormatterTest("%#+v", v, "("+vt+")"+vs7)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs8)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs8)
+
+ // Structs that are circular through cross referencing.
+ v2 := xref1{nil}
+ ts2 := xref2{&v2}
+ v2.ps2 = &ts2
+ pv2 := &v2
+ ts2Addr := fmt.Sprintf("%p", &ts2)
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.xref1"
+ v2t2 := "spew_test.xref2"
+ v2s := "{<*>{<*>{<*>}}}"
+ v2s2 := "{<*>{<*>