From 936cba087caa281af1d2d52313e4d2b786a549eb Mon Sep 17 00:00:00 2001 From: Androlo <andreas@erisindustries.com> Date: Sat, 20 Jun 2015 12:49:17 +0200 Subject: [PATCH] godep save no path changes --- Godeps/Godeps.json | 194 ++ Godeps/Readme | 5 + Godeps/_workspace/.gitignore | 2 + .../src/golang.org/x/net/context/context.go | 447 +++ .../golang.org/x/net/context/context_test.go | 575 ++++ .../x/net/context/withtimeout_test.go | 26 + .../src/golang.org/x/net/netutil/listen.go | 48 + .../golang.org/x/net/netutil/listen_test.go | 103 + .../bluesuncorp/validator.v5/.gitignore | 26 + .../bluesuncorp/validator.v5/.travis.yml | 13 + .../gopkg.in/bluesuncorp/validator.v5/LICENSE | 22 + .../bluesuncorp/validator.v5/README.md | 44 + .../bluesuncorp/validator.v5/baked_in.go | 864 ++++++ .../gopkg.in/bluesuncorp/validator.v5/doc.go | 395 +++ .../bluesuncorp/validator.v5/regexes.go | 38 + .../bluesuncorp/validator.v5/validator.go | 571 ++++ .../validator.v5/validator_test.go | 2497 +++++++++++++++++ .../src/gopkg.in/fatih/set.v0/.travis.yml | 3 + .../src/gopkg.in/fatih/set.v0/LICENSE.md | 20 + .../src/gopkg.in/fatih/set.v0/README.md | 245 ++ .../src/gopkg.in/fatih/set.v0/set.go | 121 + .../src/gopkg.in/fatih/set.v0/set_nots.go | 195 ++ .../gopkg.in/fatih/set.v0/set_nots_test.go | 282 ++ .../src/gopkg.in/fatih/set.v0/set_test.go | 188 ++ .../src/gopkg.in/fatih/set.v0/set_ts.go | 200 ++ .../src/gopkg.in/fatih/set.v0/set_ts_test.go | 321 +++ .../gopkg.in/tylerb/graceful.v1/.gitignore | 23 + .../src/gopkg.in/tylerb/graceful.v1/LICENSE | 21 + .../src/gopkg.in/tylerb/graceful.v1/README.md | 137 + .../gopkg.in/tylerb/graceful.v1/graceful.go | 319 +++ .../tylerb/graceful.v1/graceful_test.go | 379 +++ .../gopkg.in/tylerb/graceful.v1/tests/main.go | 40 + 32 files changed, 8364 insertions(+) create mode 100644 Godeps/Godeps.json create mode 100644 Godeps/Readme create mode 100644 Godeps/_workspace/.gitignore create mode 100644 Godeps/_workspace/src/golang.org/x/net/context/context.go create mode 100644 Godeps/_workspace/src/golang.org/x/net/context/context_test.go create mode 100644 Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go create mode 100644 Godeps/_workspace/src/golang.org/x/net/netutil/listen.go create mode 100644 Godeps/_workspace/src/golang.org/x/net/netutil/listen_test.go create mode 100644 Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/.gitignore create mode 100644 Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/.travis.yml create mode 100644 Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/LICENSE create mode 100644 Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/README.md create mode 100644 Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/baked_in.go create mode 100644 Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/doc.go create mode 100644 Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/regexes.go create mode 100644 Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/validator.go create mode 100644 Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/validator_test.go create mode 100644 Godeps/_workspace/src/gopkg.in/fatih/set.v0/.travis.yml create mode 100644 Godeps/_workspace/src/gopkg.in/fatih/set.v0/LICENSE.md create mode 100644 Godeps/_workspace/src/gopkg.in/fatih/set.v0/README.md create mode 100644 Godeps/_workspace/src/gopkg.in/fatih/set.v0/set.go create mode 100644 Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_nots.go create mode 100644 Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_nots_test.go create mode 100644 Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_test.go create mode 100644 Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_ts.go create mode 100644 Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_ts_test.go create mode 100644 Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/.gitignore create mode 100644 Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/LICENSE create mode 100644 Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/README.md create mode 100644 Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/graceful.go create mode 100644 Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/graceful_test.go create mode 100644 Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/tests/main.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json new file mode 100644 index 00000000..d3c54ebe --- /dev/null +++ b/Godeps/Godeps.json @@ -0,0 +1,194 @@ +{ + "ImportPath": "github.com/eris-ltd/eris-db", + "GoVersion": "go1.4.2", + "Packages": [ + "./..." + ], + "Deps": [ + { + "ImportPath": "code.google.com/p/go.crypto/ripemd160", + "Comment": "null-236", + "Rev": "69e2a90ed92d03812364aeb947b7068dc42e561e" + }, + { + "ImportPath": "code.google.com/p/mxk/go1/flowcontrol", + "Comment": "null-12", + "Rev": "5ff2502e25566863e8a0136c7aae8838e4c7de39" + }, + { + "ImportPath": "github.com/agl/ed25519/edwards25519", + "Rev": "d2b94fd789ea21d12fac1a4443dd3a3f79cda72c" + }, + { + "ImportPath": "github.com/gin-gonic/gin", + "Comment": "v1.0rc1-99-gd6425f1", + "Rev": "d6425f1692d4aa170ece887b62bb75c9c4da5829" + }, + { + "ImportPath": "github.com/gorilla/websocket", + "Rev": "6fd0f867fef40c540fa05c59f86396de10a632a6" + }, + { + "ImportPath": "github.com/inconshreveable/log15/stack", + "Comment": "v2.3-38-g352fceb", + "Rev": "352fceb48e895bd1dd0b9f5d3ae8f8516c49af0f" + }, + { + "ImportPath": "github.com/inconshreveable/log15/term", + "Comment": "v2.3-38-g352fceb", + "Rev": "352fceb48e895bd1dd0b9f5d3ae8f8516c49af0f" + }, + { + "ImportPath": "github.com/manucorporat/sse", + "Rev": "c142f0f1baea5cef7f98a8a6c222f6134368c1f5" + }, + { + "ImportPath": "github.com/mattn/go-colorable", + "Rev": "d67e0b7d1797975196499f79bcc322c08b9f218b" + }, + { + "ImportPath": "github.com/naoina/go-stringutil", + "Rev": "360db0db4b01d34e12a2ec042c09e7d37fece761" + }, + { + "ImportPath": "github.com/naoina/toml", + "Rev": "7b2dffbeaee47506726f29e36d19cf4ee90d361b" + }, + { + "ImportPath": "github.com/sfreiberg/gotwilio", + "Rev": "b7230c284bd0c1614c94d00b9998c49f9a2737d8" + }, + { + "ImportPath": "github.com/stretchr/testify/assert", + "Rev": "de7fcff264cd05cc0c90c509ea789a436a0dd206" + }, + { + "ImportPath": "github.com/stretchr/testify/suite", + "Rev": "de7fcff264cd05cc0c90c509ea789a436a0dd206" + }, + { + "ImportPath": "github.com/syndtr/goleveldb/leveldb", + "Rev": "315fcfb05d4d46d4354b313d146ef688dda272a9" + }, + { + "ImportPath": "github.com/syndtr/gosnappy/snappy", + "Rev": "156a073208e131d7d2e212cb749feae7c339e846" + }, + { + "ImportPath": "github.com/tendermint/ed25519", + "Rev": "533fb6548e2071076888eda3c38749d707ba49bc" + }, + { + "ImportPath": "github.com/tendermint/log15", + "Comment": "v2.3-33-g105a1be", + "Rev": "105a1beefe3379227a0d61733a29fd2c34f7080c" + }, + { + "ImportPath": "github.com/tendermint/tendermint/account", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/alert", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/binary", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/blockchain", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/common", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/config", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/consensus", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/db", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/events", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/logger", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/mempool", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/merkle", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/node", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/p2p", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/rpc/core", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/rpc/server", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/rpc/types", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/state", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/types", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tendermint/tendermint/vm", + "Rev": "46bd0e5d51692347647bb2e250fd0e637fc63a11" + }, + { + "ImportPath": "github.com/tommy351/gin-cors", + "Rev": "f7219a698f21b23d73ae9330d3c5842951ce0b6a" + }, + { + "ImportPath": "golang.org/x/net/context", + "Rev": "3cffabab72adf04f8e3b01c5baf775361837b5fe" + }, + { + "ImportPath": "golang.org/x/net/netutil", + "Rev": "3cffabab72adf04f8e3b01c5baf775361837b5fe" + }, + { + "ImportPath": "gopkg.in/bluesuncorp/validator.v5", + "Comment": "v5.7.1", + "Rev": "df95f9de27b8022e57f8ce09448cee6b1170bb3d" + }, + { + "ImportPath": "gopkg.in/fatih/set.v0", + "Comment": "v0.1.0-3-g27c4092", + "Rev": "27c40922c40b43fe04554d8223a402af3ea333f3" + }, + { + "ImportPath": "gopkg.in/tylerb/graceful.v1", + "Comment": "v1.1", + "Rev": "ff79a7f00848198d26054b472aafb1043c4b3507" + } + ] +} diff --git a/Godeps/Readme b/Godeps/Readme new file mode 100644 index 00000000..4cdaa53d --- /dev/null +++ b/Godeps/Readme @@ -0,0 +1,5 @@ +This directory tree is generated automatically by godep. + +Please do not edit. + +See https://github.com/tools/godep for more information. diff --git a/Godeps/_workspace/.gitignore b/Godeps/_workspace/.gitignore new file mode 100644 index 00000000..f037d684 --- /dev/null +++ b/Godeps/_workspace/.gitignore @@ -0,0 +1,2 @@ +/pkg +/bin diff --git a/Godeps/_workspace/src/golang.org/x/net/context/context.go b/Godeps/_workspace/src/golang.org/x/net/context/context.go new file mode 100644 index 00000000..ef2f3e86 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/context/context.go @@ -0,0 +1,447 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package context defines the Context type, which carries deadlines, +// cancelation signals, and other request-scoped values across API boundaries +// and between processes. +// +// Incoming requests to a server should create a Context, and outgoing calls to +// servers should accept a Context. The chain of function calls between must +// propagate the Context, optionally replacing it with a modified copy created +// using WithDeadline, WithTimeout, WithCancel, or WithValue. +// +// Programs that use Contexts should follow these rules to keep interfaces +// consistent across packages and enable static analysis tools to check context +// propagation: +// +// Do not store Contexts inside a struct type; instead, pass a Context +// explicitly to each function that needs it. The Context should be the first +// parameter, typically named ctx: +// +// func DoSomething(ctx context.Context, arg Arg) error { +// // ... use ctx ... +// } +// +// Do not pass a nil Context, even if a function permits it. Pass context.TODO +// if you are unsure about which Context to use. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +// +// The same Context may be passed to functions running in different goroutines; +// Contexts are safe for simultaneous use by multiple goroutines. +// +// See http://blog.golang.org/context for example code for a server that uses +// Contexts. +package context + +import ( + "errors" + "fmt" + "sync" + "time" +) + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context interface { + // Deadline returns the time when work done on behalf of this context + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. + Deadline() (deadline time.Time, ok bool) + + // Done returns a channel that's closed when work done on behalf of this + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. + // + // WithCancel arranges for Done to be closed when cancel is called; + // WithDeadline arranges for Done to be closed when the deadline + // expires; WithTimeout arranges for Done to be closed when the timeout + // elapses. + // + // Done is provided for use in select statements: + // + // // Stream generates values with DoSomething and sends them to out + // // until DoSomething returns an error or ctx.Done is closed. + // func Stream(ctx context.Context, out <-chan Value) error { + // for { + // v, err := DoSomething(ctx) + // if err != nil { + // return err + // } + // select { + // case <-ctx.Done(): + // return ctx.Err() + // case out <- v: + // } + // } + // } + // + // See http://blog.golang.org/pipelines for more examples of how to use + // a Done channel for cancelation. + Done() <-chan struct{} + + // Err returns a non-nil error value after Done is closed. Err returns + // Canceled if the context was canceled or DeadlineExceeded if the + // context's deadline passed. No other values for Err are defined. + // After Done is closed, successive calls to Err return the same value. + Err() error + + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + // + // A key identifies a specific value in a Context. Functions that wish + // to store values in Context typically allocate a key in a global + // variable then use that key as the argument to context.WithValue and + // Context.Value. A key can be any type that supports equality; + // packages should define keys as an unexported type to avoid + // collisions. + // + // Packages that define a Context key should provide type-safe accessors + // for the values stores using that key: + // + // // Package user defines a User type that's stored in Contexts. + // package user + // + // import "golang.org/x/net/context" + // + // // User is the type of value stored in the Contexts. + // type User struct {...} + // + // // key is an unexported type for keys defined in this package. + // // This prevents collisions with keys defined in other packages. + // type key int + // + // // userKey is the key for user.User values in Contexts. It is + // // unexported; clients use user.NewContext and user.FromContext + // // instead of using this key directly. + // var userKey key = 0 + // + // // NewContext returns a new Context that carries value u. + // func NewContext(ctx context.Context, u *User) context.Context { + // return context.WithValue(ctx, userKey, u) + // } + // + // // FromContext returns the User value stored in ctx, if any. + // func FromContext(ctx context.Context) (*User, bool) { + // u, ok := ctx.Value(userKey).(*User) + // return u, ok + // } + Value(key interface{}) interface{} +} + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = errors.New("context canceled") + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = errors.New("context deadline exceeded") + +// An emptyCtx is never canceled, has no values, and has no deadline. It is not +// struct{}, since vars of this type must have distinct addresses. +type emptyCtx int + +func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (*emptyCtx) Done() <-chan struct{} { + return nil +} + +func (*emptyCtx) Err() error { + return nil +} + +func (*emptyCtx) Value(key interface{}) interface{} { + return nil +} + +func (e *emptyCtx) String() string { + switch e { + case background: + return "context.Background" + case todo: + return "context.TODO" + } + return "unknown empty Context" +} + +var ( + background = new(emptyCtx) + todo = new(emptyCtx) +) + +// Background returns a non-nil, empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming +// requests. +func Background() Context { + return background +} + +// TODO returns a non-nil, empty Context. Code should use context.TODO when +// it's unclear which Context to use or it's is not yet available (because the +// surrounding function has not yet been extended to accept a Context +// parameter). TODO is recognized by static analysis tools that determine +// whether Contexts are propagated correctly in a program. +func TODO() Context { + return todo +} + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc func() + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + c := newCancelCtx(parent) + propagateCancel(parent, &c) + return &c, func() { c.cancel(true, Canceled) } +} + +// newCancelCtx returns an initialized cancelCtx. +func newCancelCtx(parent Context) cancelCtx { + return cancelCtx{ + Context: parent, + done: make(chan struct{}), + } +} + +// propagateCancel arranges for child to be canceled when parent is. +func propagateCancel(parent Context, child canceler) { + if parent.Done() == nil { + return // parent is never canceled + } + if p, ok := parentCancelCtx(parent); ok { + p.mu.Lock() + if p.err != nil { + // parent has already been canceled + child.cancel(false, p.err) + } else { + if p.children == nil { + p.children = make(map[canceler]bool) + } + p.children[child] = true + } + p.mu.Unlock() + } else { + go func() { + select { + case <-parent.Done(): + child.cancel(false, parent.Err()) + case <-child.Done(): + } + }() + } +} + +// parentCancelCtx follows a chain of parent references until it finds a +// *cancelCtx. This function understands how each of the concrete types in this +// package represents its parent. +func parentCancelCtx(parent Context) (*cancelCtx, bool) { + for { + switch c := parent.(type) { + case *cancelCtx: + return c, true + case *timerCtx: + return &c.cancelCtx, true + case *valueCtx: + parent = c.Context + default: + return nil, false + } + } +} + +// removeChild removes a context from its parent. +func removeChild(parent Context, child canceler) { + p, ok := parentCancelCtx(parent) + if !ok { + return + } + p.mu.Lock() + if p.children != nil { + delete(p.children, child) + } + p.mu.Unlock() +} + +// A canceler is a context type that can be canceled directly. The +// implementations are *cancelCtx and *timerCtx. +type canceler interface { + cancel(removeFromParent bool, err error) + Done() <-chan struct{} +} + +// A cancelCtx can be canceled. When canceled, it also cancels any children +// that implement canceler. +type cancelCtx struct { + Context + + done chan struct{} // closed by the first cancel call. + + mu sync.Mutex + children map[canceler]bool // set to nil by the first cancel call + err error // set to non-nil by the first cancel call +} + +func (c *cancelCtx) Done() <-chan struct{} { + return c.done +} + +func (c *cancelCtx) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +func (c *cancelCtx) String() string { + return fmt.Sprintf("%v.WithCancel", c.Context) +} + +// cancel closes c.done, cancels each of c's children, and, if +// removeFromParent is true, removes c from its parent's children. +func (c *cancelCtx) cancel(removeFromParent bool, err error) { + if err == nil { + panic("context: internal error: missing cancel error") + } + c.mu.Lock() + if c.err != nil { + c.mu.Unlock() + return // already canceled + } + c.err = err + close(c.done) + for child := range c.children { + // NOTE: acquiring the child's lock while holding parent's lock. + child.cancel(false, err) + } + c.children = nil + c.mu.Unlock() + + if removeFromParent { + removeChild(c.Context, c) + } +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { + // The current deadline is already sooner than the new one. + return WithCancel(parent) + } + c := &timerCtx{ + cancelCtx: newCancelCtx(parent), + deadline: deadline, + } + propagateCancel(parent, c) + d := deadline.Sub(time.Now()) + if d <= 0 { + c.cancel(true, DeadlineExceeded) // deadline has already passed + return c, func() { c.cancel(true, Canceled) } + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { + c.timer = time.AfterFunc(d, func() { + c.cancel(true, DeadlineExceeded) + }) + } + return c, func() { c.cancel(true, Canceled) } +} + +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then +// delegating to cancelCtx.cancel. +type timerCtx struct { + cancelCtx + timer *time.Timer // Under cancelCtx.mu. + + deadline time.Time +} + +func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { + return c.deadline, true +} + +func (c *timerCtx) String() string { + return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) +} + +func (c *timerCtx) cancel(removeFromParent bool, err error) { + c.cancelCtx.cancel(false, err) + if removeFromParent { + // Remove this timerCtx from its parent cancelCtx's children. + removeChild(c.cancelCtx.Context, c) + } + c.mu.Lock() + if c.timer != nil { + c.timer.Stop() + c.timer = nil + } + c.mu.Unlock() +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return &valueCtx{parent, key, val} +} + +// A valueCtx carries a key-value pair. It implements Value for that key and +// delegates all other calls to the embedded Context. +type valueCtx struct { + Context + key, val interface{} +} + +func (c *valueCtx) String() string { + return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) +} + +func (c *valueCtx) Value(key interface{}) interface{} { + if c.key == key { + return c.val + } + return c.Context.Value(key) +} diff --git a/Godeps/_workspace/src/golang.org/x/net/context/context_test.go b/Godeps/_workspace/src/golang.org/x/net/context/context_test.go new file mode 100644 index 00000000..faf67722 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/context/context_test.go @@ -0,0 +1,575 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context + +import ( + "fmt" + "math/rand" + "runtime" + "strings" + "sync" + "testing" + "time" +) + +// otherContext is a Context that's not one of the types defined in context.go. +// This lets us test code paths that differ based on the underlying type of the +// Context. +type otherContext struct { + Context +} + +func TestBackground(t *testing.T) { + c := Background() + if c == nil { + t.Fatalf("Background returned nil") + } + select { + case x := <-c.Done(): + t.Errorf("<-c.Done() == %v want nothing (it should block)", x) + default: + } + if got, want := fmt.Sprint(c), "context.Background"; got != want { + t.Errorf("Background().String() = %q want %q", got, want) + } +} + +func TestTODO(t *testing.T) { + c := TODO() + if c == nil { + t.Fatalf("TODO returned nil") + } + select { + case x := <-c.Done(): + t.Errorf("<-c.Done() == %v want nothing (it should block)", x) + default: + } + if got, want := fmt.Sprint(c), "context.TODO"; got != want { + t.Errorf("TODO().String() = %q want %q", got, want) + } +} + +func TestWithCancel(t *testing.T) { + c1, cancel := WithCancel(Background()) + + if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want { + t.Errorf("c1.String() = %q want %q", got, want) + } + + o := otherContext{c1} + c2, _ := WithCancel(o) + contexts := []Context{c1, o, c2} + + for i, c := range contexts { + if d := c.Done(); d == nil { + t.Errorf("c[%d].Done() == %v want non-nil", i, d) + } + if e := c.Err(); e != nil { + t.Errorf("c[%d].Err() == %v want nil", i, e) + } + + select { + case x := <-c.Done(): + t.Errorf("<-c.Done() == %v want nothing (it should block)", x) + default: + } + } + + cancel() + time.Sleep(100 * time.Millisecond) // let cancelation propagate + + for i, c := range contexts { + select { + case <-c.Done(): + default: + t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i) + } + if e := c.Err(); e != Canceled { + t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled) + } + } +} + +func TestParentFinishesChild(t *testing.T) { + // Context tree: + // parent -> cancelChild + // parent -> valueChild -> timerChild + parent, cancel := WithCancel(Background()) + cancelChild, stop := WithCancel(parent) + defer stop() + valueChild := WithValue(parent, "key", "value") + timerChild, stop := WithTimeout(valueChild, 10000*time.Hour) + defer stop() + + select { + case x := <-parent.Done(): + t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) + case x := <-cancelChild.Done(): + t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x) + case x := <-timerChild.Done(): + t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x) + case x := <-valueChild.Done(): + t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x) + default: + } + + // The parent's children should contain the two cancelable children. + pc := parent.(*cancelCtx) + cc := cancelChild.(*cancelCtx) + tc := timerChild.(*timerCtx) + pc.mu.Lock() + if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] { + t.Errorf("bad linkage: pc.children = %v, want %v and %v", + pc.children, cc, tc) + } + pc.mu.Unlock() + + if p, ok := parentCancelCtx(cc.Context); !ok || p != pc { + t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc) + } + if p, ok := parentCancelCtx(tc.Context); !ok || p != pc { + t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc) + } + + cancel() + + pc.mu.Lock() + if len(pc.children) != 0 { + t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children) + } + pc.mu.Unlock() + + // parent and children should all be finished. + check := func(ctx Context, name string) { + select { + case <-ctx.Done(): + default: + t.Errorf("<-%s.Done() blocked, but shouldn't have", name) + } + if e := ctx.Err(); e != Canceled { + t.Errorf("%s.Err() == %v want %v", name, e, Canceled) + } + } + check(parent, "parent") + check(cancelChild, "cancelChild") + check(valueChild, "valueChild") + check(timerChild, "timerChild") + + // WithCancel should return a canceled context on a canceled parent. + precanceledChild := WithValue(parent, "key", "value") + select { + case <-precanceledChild.Done(): + default: + t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have") + } + if e := precanceledChild.Err(); e != Canceled { + t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled) + } +} + +func TestChildFinishesFirst(t *testing.T) { + cancelable, stop := WithCancel(Background()) + defer stop() + for _, parent := range []Context{Background(), cancelable} { + child, cancel := WithCancel(parent) + + select { + case x := <-parent.Done(): + t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) + case x := <-child.Done(): + t.Errorf("<-child.Done() == %v want nothing (it should block)", x) + default: + } + + cc := child.(*cancelCtx) + pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background() + if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) { + t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok) + } + + if pcok { + pc.mu.Lock() + if len(pc.children) != 1 || !pc.children[cc] { + t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc) + } + pc.mu.Unlock() + } + + cancel() + + if pcok { + pc.mu.Lock() + if len(pc.children) != 0 { + t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children) + } + pc.mu.Unlock() + } + + // child should be finished. + select { + case <-child.Done(): + default: + t.Errorf("<-child.Done() blocked, but shouldn't have") + } + if e := child.Err(); e != Canceled { + t.Errorf("child.Err() == %v want %v", e, Canceled) + } + + // parent should not be finished. + select { + case x := <-parent.Done(): + t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) + default: + } + if e := parent.Err(); e != nil { + t.Errorf("parent.Err() == %v want nil", e) + } + } +} + +func testDeadline(c Context, wait time.Duration, t *testing.T) { + select { + case <-time.After(wait): + t.Fatalf("context should have timed out") + case <-c.Done(): + } + if e := c.Err(); e != DeadlineExceeded { + t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded) + } +} + +func TestDeadline(t *testing.T) { + c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { + t.Errorf("c.String() = %q want prefix %q", got, prefix) + } + testDeadline(c, 200*time.Millisecond, t) + + c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + o := otherContext{c} + testDeadline(o, 200*time.Millisecond, t) + + c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + o = otherContext{c} + c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond)) + testDeadline(c, 200*time.Millisecond, t) +} + +func TestTimeout(t *testing.T) { + c, _ := WithTimeout(Background(), 100*time.Millisecond) + if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { + t.Errorf("c.String() = %q want prefix %q", got, prefix) + } + testDeadline(c, 200*time.Millisecond, t) + + c, _ = WithTimeout(Background(), 100*time.Millisecond) + o := otherContext{c} + testDeadline(o, 200*time.Millisecond, t) + + c, _ = WithTimeout(Background(), 100*time.Millisecond) + o = otherContext{c} + c, _ = WithTimeout(o, 300*time.Millisecond) + testDeadline(c, 200*time.Millisecond, t) +} + +func TestCanceledTimeout(t *testing.T) { + c, _ := WithTimeout(Background(), 200*time.Millisecond) + o := otherContext{c} + c, cancel := WithTimeout(o, 400*time.Millisecond) + cancel() + time.Sleep(100 * time.Millisecond) // let cancelation propagate + select { + case <-c.Done(): + default: + t.Errorf("<-c.Done() blocked, but shouldn't have") + } + if e := c.Err(); e != Canceled { + t.Errorf("c.Err() == %v want %v", e, Canceled) + } +} + +type key1 int +type key2 int + +var k1 = key1(1) +var k2 = key2(1) // same int as k1, different type +var k3 = key2(3) // same type as k2, different int + +func TestValues(t *testing.T) { + check := func(c Context, nm, v1, v2, v3 string) { + if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 { + t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0) + } + if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 { + t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0) + } + if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 { + t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0) + } + } + + c0 := Background() + check(c0, "c0", "", "", "") + + c1 := WithValue(Background(), k1, "c1k1") + check(c1, "c1", "c1k1", "", "") + + if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want { + t.Errorf("c.String() = %q want %q", got, want) + } + + c2 := WithValue(c1, k2, "c2k2") + check(c2, "c2", "c1k1", "c2k2", "") + + c3 := WithValue(c2, k3, "c3k3") + check(c3, "c2", "c1k1", "c2k2", "c3k3") + + c4 := WithValue(c3, k1, nil) + check(c4, "c4", "", "c2k2", "c3k3") + + o0 := otherContext{Background()} + check(o0, "o0", "", "", "") + + o1 := otherContext{WithValue(Background(), k1, "c1k1")} + check(o1, "o1", "c1k1", "", "") + + o2 := WithValue(o1, k2, "o2k2") + check(o2, "o2", "c1k1", "o2k2", "") + + o3 := otherContext{c4} + check(o3, "o3", "", "c2k2", "c3k3") + + o4 := WithValue(o3, k3, nil) + check(o4, "o4", "", "c2k2", "") +} + +func TestAllocs(t *testing.T) { + bg := Background() + for _, test := range []struct { + desc string + f func() + limit float64 + gccgoLimit float64 + }{ + { + desc: "Background()", + f: func() { Background() }, + limit: 0, + gccgoLimit: 0, + }, + { + desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1), + f: func() { + c := WithValue(bg, k1, nil) + c.Value(k1) + }, + limit: 3, + gccgoLimit: 3, + }, + { + desc: "WithTimeout(bg, 15*time.Millisecond)", + f: func() { + c, _ := WithTimeout(bg, 15*time.Millisecond) + <-c.Done() + }, + limit: 8, + gccgoLimit: 13, + }, + { + desc: "WithCancel(bg)", + f: func() { + c, cancel := WithCancel(bg) + cancel() + <-c.Done() + }, + limit: 5, + gccgoLimit: 8, + }, + { + desc: "WithTimeout(bg, 100*time.Millisecond)", + f: func() { + c, cancel := WithTimeout(bg, 100*time.Millisecond) + cancel() + <-c.Done() + }, + limit: 8, + gccgoLimit: 25, + }, + } { + limit := test.limit + if runtime.Compiler == "gccgo" { + // gccgo does not yet do escape analysis. + // TOOD(iant): Remove this when gccgo does do escape analysis. + limit = test.gccgoLimit + } + if n := testing.AllocsPerRun(100, test.f); n > limit { + t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit)) + } + } +} + +func TestSimultaneousCancels(t *testing.T) { + root, cancel := WithCancel(Background()) + m := map[Context]CancelFunc{root: cancel} + q := []Context{root} + // Create a tree of contexts. + for len(q) != 0 && len(m) < 100 { + parent := q[0] + q = q[1:] + for i := 0; i < 4; i++ { + ctx, cancel := WithCancel(parent) + m[ctx] = cancel + q = append(q, ctx) + } + } + // Start all the cancels in a random order. + var wg sync.WaitGroup + wg.Add(len(m)) + for _, cancel := range m { + go func(cancel CancelFunc) { + cancel() + wg.Done() + }(cancel) + } + // Wait on all the contexts in a random order. + for ctx := range m { + select { + case <-ctx.Done(): + case <-time.After(1 * time.Second): + buf := make([]byte, 10<<10) + n := runtime.Stack(buf, true) + t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n]) + } + } + // Wait for all the cancel functions to return. + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + select { + case <-done: + case <-time.After(1 * time.Second): + buf := make([]byte, 10<<10) + n := runtime.Stack(buf, true) + t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n]) + } +} + +func TestInterlockedCancels(t *testing.T) { + parent, cancelParent := WithCancel(Background()) + child, cancelChild := WithCancel(parent) + go func() { + parent.Done() + cancelChild() + }() + cancelParent() + select { + case <-child.Done(): + case <-time.After(1 * time.Second): + buf := make([]byte, 10<<10) + n := runtime.Stack(buf, true) + t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n]) + } +} + +func TestLayersCancel(t *testing.T) { + testLayers(t, time.Now().UnixNano(), false) +} + +func TestLayersTimeout(t *testing.T) { + testLayers(t, time.Now().UnixNano(), true) +} + +func testLayers(t *testing.T, seed int64, testTimeout bool) { + rand.Seed(seed) + errorf := func(format string, a ...interface{}) { + t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...) + } + const ( + timeout = 200 * time.Millisecond + minLayers = 30 + ) + type value int + var ( + vals []*value + cancels []CancelFunc + numTimers int + ctx = Background() + ) + for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ { + switch rand.Intn(3) { + case 0: + v := new(value) + ctx = WithValue(ctx, v, v) + vals = append(vals, v) + case 1: + var cancel CancelFunc + ctx, cancel = WithCancel(ctx) + cancels = append(cancels, cancel) + case 2: + var cancel CancelFunc + ctx, cancel = WithTimeout(ctx, timeout) + cancels = append(cancels, cancel) + numTimers++ + } + } + checkValues := func(when string) { + for _, key := range vals { + if val := ctx.Value(key).(*value); key != val { + errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key) + } + } + } + select { + case <-ctx.Done(): + errorf("ctx should not be canceled yet") + default: + } + if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) { + t.Errorf("ctx.String() = %q want prefix %q", s, prefix) + } + t.Log(ctx) + checkValues("before cancel") + if testTimeout { + select { + case <-ctx.Done(): + case <-time.After(timeout + timeout/10): + errorf("ctx should have timed out") + } + checkValues("after timeout") + } else { + cancel := cancels[rand.Intn(len(cancels))] + cancel() + select { + case <-ctx.Done(): + default: + errorf("ctx should be canceled") + } + checkValues("after cancel") + } +} + +func TestCancelRemoves(t *testing.T) { + checkChildren := func(when string, ctx Context, want int) { + if got := len(ctx.(*cancelCtx).children); got != want { + t.Errorf("%s: context has %d children, want %d", when, got, want) + } + } + + ctx, _ := WithCancel(Background()) + checkChildren("after creation", ctx, 0) + _, cancel := WithCancel(ctx) + checkChildren("with WithCancel child ", ctx, 1) + cancel() + checkChildren("after cancelling WithCancel child", ctx, 0) + + ctx, _ = WithCancel(Background()) + checkChildren("after creation", ctx, 0) + _, cancel = WithTimeout(ctx, 60*time.Minute) + checkChildren("with WithTimeout child ", ctx, 1) + cancel() + checkChildren("after cancelling WithTimeout child", ctx, 0) +} diff --git a/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go b/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go new file mode 100644 index 00000000..a6754dc3 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go @@ -0,0 +1,26 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context_test + +import ( + "fmt" + "time" + + "golang.org/x/net/context" +) + +func ExampleWithTimeout() { + // Pass a context with a timeout to tell a blocking function that it + // should abandon its work after the timeout elapses. + ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) + select { + case <-time.After(200 * time.Millisecond): + fmt.Println("overslept") + case <-ctx.Done(): + fmt.Println(ctx.Err()) // prints "context deadline exceeded" + } + // Output: + // context deadline exceeded +} diff --git a/Godeps/_workspace/src/golang.org/x/net/netutil/listen.go b/Godeps/_workspace/src/golang.org/x/net/netutil/listen.go new file mode 100644 index 00000000..a2591f83 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/netutil/listen.go @@ -0,0 +1,48 @@ +// 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 netutil provides network utility functions, complementing the more +// common ones in the net package. +package netutil + +import ( + "net" + "sync" +) + +// LimitListener returns a Listener that accepts at most n simultaneous +// connections from the provided Listener. +func LimitListener(l net.Listener, n int) net.Listener { + return &limitListener{l, make(chan struct{}, n)} +} + +type limitListener struct { + net.Listener + sem chan struct{} +} + +func (l *limitListener) acquire() { l.sem <- struct{}{} } +func (l *limitListener) release() { <-l.sem } + +func (l *limitListener) Accept() (net.Conn, error) { + l.acquire() + c, err := l.Listener.Accept() + if err != nil { + l.release() + return nil, err + } + return &limitListenerConn{Conn: c, release: l.release}, nil +} + +type limitListenerConn struct { + net.Conn + releaseOnce sync.Once + release func() +} + +func (l *limitListenerConn) Close() error { + err := l.Conn.Close() + l.releaseOnce.Do(l.release) + return err +} diff --git a/Godeps/_workspace/src/golang.org/x/net/netutil/listen_test.go b/Godeps/_workspace/src/golang.org/x/net/netutil/listen_test.go new file mode 100644 index 00000000..ac87e0ee --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/netutil/listen_test.go @@ -0,0 +1,103 @@ +// 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. + +// +build go1.3 + +// (We only run this test on Go 1.3 because the HTTP client timeout behavior +// was bad in previous releases, causing occasional deadlocks.) + +package netutil + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "sync" + "sync/atomic" + "testing" + "time" +) + +func TestLimitListener(t *testing.T) { + const ( + max = 5 + num = 200 + ) + + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Listen: %v", err) + } + defer l.Close() + l = LimitListener(l, max) + + var open int32 + go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if n := atomic.AddInt32(&open, 1); n > max { + t.Errorf("%d open connections, want <= %d", n, max) + } + defer atomic.AddInt32(&open, -1) + time.Sleep(10 * time.Millisecond) + fmt.Fprint(w, "some body") + })) + + var wg sync.WaitGroup + var failed int32 + for i := 0; i < num; i++ { + wg.Add(1) + go func() { + defer wg.Done() + c := http.Client{Timeout: 3 * time.Second} + r, err := c.Get("http://" + l.Addr().String()) + if err != nil { + t.Logf("Get: %v", err) + atomic.AddInt32(&failed, 1) + return + } + defer r.Body.Close() + io.Copy(ioutil.Discard, r.Body) + }() + } + wg.Wait() + + // We expect some Gets to fail as the kernel's accept queue is filled, + // but most should succeed. + if failed >= num/2 { + t.Errorf("too many Gets failed: %v", failed) + } +} + +type errorListener struct { + net.Listener +} + +func (errorListener) Accept() (net.Conn, error) { + return nil, errFake +} + +var errFake = errors.New("fake error from errorListener") + +// This used to hang. +func TestLimitListenerError(t *testing.T) { + donec := make(chan bool, 1) + go func() { + const n = 2 + ll := LimitListener(errorListener{}, n) + for i := 0; i < n+1; i++ { + _, err := ll.Accept() + if err != errFake { + t.Fatalf("Accept error = %v; want errFake", err) + } + } + donec <- true + }() + select { + case <-donec: + case <-time.After(5 * time.Second): + t.Fatal("timeout. deadlock?") + } +} diff --git a/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/.gitignore b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/.gitignore new file mode 100644 index 00000000..41590204 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/.gitignore @@ -0,0 +1,26 @@ +# 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 +*.test +*.prof +*.test +*.out \ No newline at end of file diff --git a/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/.travis.yml b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/.travis.yml new file mode 100644 index 00000000..68398d9f --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/.travis.yml @@ -0,0 +1,13 @@ +language: go + +notificaitons: + email: + recipients: bluesuncorp01@gmail.com + on_success: change + on_failure: always + +go: + - 1.2 + - 1.3 + - 1.4 + - tip \ No newline at end of file diff --git a/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/LICENSE b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/LICENSE new file mode 100644 index 00000000..6a2ae9aa --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Dean Karn + +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/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/README.md b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/README.md new file mode 100644 index 00000000..c1e9d00c --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/README.md @@ -0,0 +1,44 @@ +Package validator +================ +[](https://travis-ci.org/bluesuncorp/validator) +[](https://godoc.org/gopkg.in/bluesuncorp/validator.v5) + +Package validator implements value validations for structs and individual fields based on tags. +It is also capable of Cross Field and Cross Struct validations. + +Installation +============ + +Use go get. + + go get gopkg.in/bluesuncorp/validator.v5 + +or to update + + go get -u gopkg.in/bluesuncorp/validator.v5 + +Then import the validator package into your own code. + + import "gopkg.in/bluesuncorp/validator.v5" + +Usage and documentation +======================= + +Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v5 for detailed usage docs. + +How to Contribute +================= + +There will always be a development branch for each version i.e. `v1-development`. In order to contribute, +please make your pull requests against those branches. + +If the changes being proposed or requested are breaking changes, please create an issue, for discussion +or create a pull request against the highest development branch for example this package has a +v1 and v1-development branch however, there will also be a v2-development brach even though v2 doesn't exist yet. + +I strongly encourage everyone whom creates a custom validation function to contribute them and +help make this package even better. + +License +======= +Distributed under MIT License, please see license file in code for more details. diff --git a/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/baked_in.go b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/baked_in.go new file mode 100644 index 00000000..ed69eb30 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/baked_in.go @@ -0,0 +1,864 @@ +package validator + +import ( + "fmt" + "net/url" + "reflect" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +// BakedInValidators is the default map of ValidationFunc +// you can add, remove or even replace items to suite your needs, +// or even disregard and use your own map if so desired. +var BakedInValidators = map[string]Func{ + "required": hasValue, + "len": hasLengthOf, + "min": hasMinOf, + "max": hasMaxOf, + "eq": isEq, + "ne": isNe, + "lt": isLt, + "lte": isLte, + "gt": isGt, + "gte": isGte, + "eqfield": isEqField, + "nefield": isNeField, + "gtefield": isGteField, + "gtfield": isGtField, + "ltefield": isLteField, + "ltfield": isLtField, + "alpha": isAlpha, + "alphanum": isAlphanum, + "numeric": isNumeric, + "number": isNumber, + "hexadecimal": isHexadecimal, + "hexcolor": isHexcolor, + "rgb": isRgb, + "rgba": isRgba, + "hsl": isHsl, + "hsla": isHsla, + "email": isEmail, + "url": isURL, + "uri": isURI, + "base64": isBase64, + "contains": contains, + "containsany": containsAny, + "containsrune": containsRune, + "excludes": excludes, + "excludesall": excludesAll, + "excludesrune": excludesRune, +} + +func excludesRune(top interface{}, current interface{}, field interface{}, param string) bool { + return !containsRune(top, current, field, param) +} + +func excludesAll(top interface{}, current interface{}, field interface{}, param string) bool { + return !containsAny(top, current, field, param) +} + +func excludes(top interface{}, current interface{}, field interface{}, param string) bool { + return !contains(top, current, field, param) +} + +func containsRune(top interface{}, current interface{}, field interface{}, param string) bool { + r, _ := utf8.DecodeRuneInString(param) + + return strings.ContainsRune(field.(string), r) +} + +func containsAny(top interface{}, current interface{}, field interface{}, param string) bool { + return strings.ContainsAny(field.(string), param) +} + +func contains(top interface{}, current interface{}, field interface{}, param string) bool { + return strings.Contains(field.(string), param) +} + +func isNeField(top interface{}, current interface{}, field interface{}, param string) bool { + return !isEqField(top, current, field, param) +} + +func isNe(top interface{}, current interface{}, field interface{}, param string) bool { + return !isEq(top, current, field, param) +} + +func isEqField(top interface{}, current interface{}, field interface{}, param string) bool { + + if current == nil { + panic("struct not passed for cross validation") + } + + currentVal := reflect.ValueOf(current) + + if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { + currentVal = reflect.ValueOf(currentVal.Elem().Interface()) + } + + var currentFielVal reflect.Value + + switch currentVal.Kind() { + + case reflect.Struct: + + if currentVal.Type() == reflect.TypeOf(time.Time{}) { + currentFielVal = currentVal + break + } + + f := currentVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + currentFielVal = f + + default: + + currentFielVal = currentVal + } + + if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + + currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.String: + return fv.String() == currentFielVal.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() == currentFielVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() == currentFielVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() == currentFielVal.Float() + case reflect.Slice, reflect.Map, reflect.Array: + + return int64(fv.Len()) == int64(currentFielVal.Len()) + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := currentFielVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.Equal(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isEq(top interface{}, current interface{}, field interface{}, param string) bool { + + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.String: + + return st.String() == param + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(st.Len()) == p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return st.Int() == p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return st.Uint() == p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return st.Float() == p + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isBase64(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(base64Regex, field) +} + +func isURI(top interface{}, current interface{}, field interface{}, param string) bool { + + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.String: + _, err := url.ParseRequestURI(field.(string)) + + return err == nil + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isURL(top interface{}, current interface{}, field interface{}, param string) bool { + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.String: + url, err := url.ParseRequestURI(field.(string)) + + if err != nil { + return false + } + + if len(url.Scheme) == 0 { + return false + } + + return err == nil + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isEmail(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(emailRegex, field) +} + +func isHsla(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(hslaRegex, field) +} + +func isHsl(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(hslRegex, field) +} + +func isRgba(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(rgbaRegex, field) +} + +func isRgb(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(rgbRegex, field) +} + +func isHexcolor(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(hexcolorRegex, field) +} + +func isHexadecimal(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(hexadecimalRegex, field) +} + +func isNumber(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(numberRegex, field) +} + +func isNumeric(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(numericRegex, field) +} + +func isAlphanum(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(alphaNumericRegex, field) +} + +func isAlpha(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(alphaRegex, field) +} + +func hasValue(top interface{}, current interface{}, field interface{}, param string) bool { + + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.Slice, reflect.Map, reflect.Array: + return field != nil && int64(st.Len()) > 0 + + default: + return field != nil && field != reflect.Zero(reflect.TypeOf(field)).Interface() + } +} + +func isGteField(top interface{}, current interface{}, field interface{}, param string) bool { + + if current == nil { + panic("struct not passed for cross validation") + } + + currentVal := reflect.ValueOf(current) + + if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { + currentVal = reflect.ValueOf(currentVal.Elem().Interface()) + } + + var currentFielVal reflect.Value + + switch currentVal.Kind() { + + case reflect.Struct: + + if currentVal.Type() == reflect.TypeOf(time.Time{}) { + currentFielVal = currentVal + break + } + + f := currentVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + currentFielVal = f + + default: + + currentFielVal = currentVal + } + + if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + + currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() >= currentFielVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() >= currentFielVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() >= currentFielVal.Float() + + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := currentFielVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.After(t) || fieldTime.Equal(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isGtField(top interface{}, current interface{}, field interface{}, param string) bool { + + if current == nil { + panic("struct not passed for cross validation") + } + + currentVal := reflect.ValueOf(current) + + if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { + currentVal = reflect.ValueOf(currentVal.Elem().Interface()) + } + + var currentFielVal reflect.Value + + switch currentVal.Kind() { + + case reflect.Struct: + + if currentVal.Type() == reflect.TypeOf(time.Time{}) { + currentFielVal = currentVal + break + } + + f := currentVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + currentFielVal = f + + default: + + currentFielVal = currentVal + } + + if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + + currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() > currentFielVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() > currentFielVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() > currentFielVal.Float() + + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := currentFielVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.After(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isGte(top interface{}, current interface{}, field interface{}, param string) bool { + + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(len(st.String())) >= p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(st.Len()) >= p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return st.Int() >= p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return st.Uint() >= p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return st.Float() >= p + + case reflect.Struct: + + if st.Type() == reflect.TypeOf(time.Time{}) { + + now := time.Now().UTC() + t := field.(time.Time) + + return t.After(now) || t.Equal(now) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isGt(top interface{}, current interface{}, field interface{}, param string) bool { + + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(len(st.String())) > p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(st.Len()) > p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return st.Int() > p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return st.Uint() > p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return st.Float() > p + case reflect.Struct: + + if st.Type() == reflect.TypeOf(time.Time{}) { + + return field.(time.Time).After(time.Now().UTC()) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +// length tests whether a variable's length is equal to a given +// value. For strings it tests the number of characters whereas +// for maps and slices it tests the number of items. +func hasLengthOf(top interface{}, current interface{}, field interface{}, param string) bool { + + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(len(st.String())) == p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(st.Len()) == p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return st.Int() == p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return st.Uint() == p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return st.Float() == p + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +// min tests whether a variable value is larger or equal to a given +// number. For number types, it's a simple lesser-than test; for +// strings it tests the number of characters whereas for maps +// and slices it tests the number of items. +func hasMinOf(top interface{}, current interface{}, field interface{}, param string) bool { + + return isGte(top, current, field, param) +} + +func isLteField(top interface{}, current interface{}, field interface{}, param string) bool { + + if current == nil { + panic("struct not passed for cross validation") + } + + currentVal := reflect.ValueOf(current) + + if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { + currentVal = reflect.ValueOf(currentVal.Elem().Interface()) + } + + var currentFielVal reflect.Value + + switch currentVal.Kind() { + + case reflect.Struct: + + if currentVal.Type() == reflect.TypeOf(time.Time{}) { + currentFielVal = currentVal + break + } + + f := currentVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + currentFielVal = f + + default: + + currentFielVal = currentVal + } + + if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + + currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() <= currentFielVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() <= currentFielVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() <= currentFielVal.Float() + + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := currentFielVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.Before(t) || fieldTime.Equal(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isLtField(top interface{}, current interface{}, field interface{}, param string) bool { + + if current == nil { + panic("struct not passed for cross validation") + } + + currentVal := reflect.ValueOf(current) + + if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { + currentVal = reflect.ValueOf(currentVal.Elem().Interface()) + } + + var currentFielVal reflect.Value + + switch currentVal.Kind() { + + case reflect.Struct: + + if currentVal.Type() == reflect.TypeOf(time.Time{}) { + currentFielVal = currentVal + break + } + + f := currentVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + currentFielVal = f + + default: + + currentFielVal = currentVal + } + + if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + + currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() < currentFielVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() < currentFielVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() < currentFielVal.Float() + + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := currentFielVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.Before(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isLte(top interface{}, current interface{}, field interface{}, param string) bool { + + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(len(st.String())) <= p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(st.Len()) <= p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return st.Int() <= p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return st.Uint() <= p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return st.Float() <= p + + case reflect.Struct: + + if st.Type() == reflect.TypeOf(time.Time{}) { + + now := time.Now().UTC() + t := field.(time.Time) + + return t.Before(now) || t.Equal(now) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isLt(top interface{}, current interface{}, field interface{}, param string) bool { + + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(len(st.String())) < p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(st.Len()) < p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return st.Int() < p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return st.Uint() < p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return st.Float() < p + + case reflect.Struct: + + if st.Type() == reflect.TypeOf(time.Time{}) { + + return field.(time.Time).Before(time.Now().UTC()) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +// max tests whether a variable value is lesser than a given +// value. For numbers, it's a simple lesser-than test; for +// strings it tests the number of characters whereas for maps +// and slices it tests the number of items. +func hasMaxOf(top interface{}, current interface{}, field interface{}, param string) bool { + + return isLte(top, current, field, param) +} + +// asInt retuns the parameter as a int64 +// or panics if it can't convert +func asInt(param string) int64 { + + i, err := strconv.ParseInt(param, 0, 64) + panicIf(err) + + return i +} + +// asUint returns the parameter as a uint64 +// or panics if it can't convert +func asUint(param string) uint64 { + + i, err := strconv.ParseUint(param, 0, 64) + panicIf(err) + + return i +} + +// asFloat returns the parameter as a float64 +// or panics if it can't convert +func asFloat(param string) float64 { + + i, err := strconv.ParseFloat(param, 64) + panicIf(err) + + return i +} + +func panicIf(err error) { + if err != nil { + panic(err.Error()) + } +} diff --git a/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/doc.go b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/doc.go new file mode 100644 index 00000000..5282cf53 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/doc.go @@ -0,0 +1,395 @@ +/* +Package validator implements value validations for structs and individual fields based on tags. It can also handle Cross Field and Cross Struct validation for nested structs. + +Validate + + validate := validator.New("validate", validator.BakedInValidators) + + errs := validate.Struct(//your struct) + valErr := validate.Field(field, "omitempty,min=1,max=10") + +A simple example usage: + + type UserDetail struct { + Details string `validate:"-"` + } + + type User struct { + Name string `validate:"required,max=60"` + PreferedName string `validate:"omitempty,max=60"` + Sub UserDetail + } + + user := &User { + Name: "", + } + + // errs will contain a hierarchical list of errors + // using the StructErrors struct + // or nil if no errors exist + errs := validate.Struct(user) + + // in this case 1 error Name is required + errs.Struct will be "User" + errs.StructErrors will be empty <-- fields that were structs + errs.Errors will have 1 error of type FieldError + + NOTE: Anonymous Structs - they don't have names so expect the Struct name + within StructErrors to be blank. + +Error Handling + +The error can be used like so + + fieldErr, _ := errs["Name"] + fieldErr.Field // "Name" + fieldErr.ErrorTag // "required" + +Both StructErrors and FieldError implement the Error interface but it's +intended use is for development + debugging, not a production error message. + + fieldErr.Error() // Field validation for "Name" failed on the "required" tag + errs.Error() + // Struct: User + // Field validation for "Name" failed on the "required" tag + +Why not a better error message? because this library intends for you to handle your own error messages + +Why should I handle my own errors? Many reasons, for us building an internationalized application +I needed to know the field and what validation failed so that I could provide an error in the users specific language. + + if fieldErr.Field == "Name" { + switch fieldErr.ErrorTag + case "required": + return "Translated string based on field + error" + default: + return "Translated string based on field" + } + +The hierarchical error structure is hard to work with sometimes.. Agreed Flatten function to the rescue! +Flatten will return a map of FieldError's but the field name will be namespaced. + + // if UserDetail Details field failed validation + Field will be "Sub.Details" + + // for Name + Field will be "Name" + +Custom Functions + +Custom functions can be added + + //Structure + func customFunc(top interface{}, current interface{}, field interface{}, param string) bool { + + if whatever { + return false + } + + return true + } + + validate.AddFunction("custom tag name", customFunc) + // NOTES: using the same tag name as an existing function + // will overwrite the existing one + +Cross Field Validation + +Cross Field Validation can be implemented, for example Start & End Date range validation + + // NOTE: when calling validate.Struct(val) val will be the top level struct passed + // into the function + // when calling validate.FieldWithValue(val, field, tag) val will be + // whatever you pass, struct, field... + // when calling validate.Field(field, tag) val will be nil + // + // Because of the specific requirements and field names within each persons project that + // uses this library it is likely that custom functions will need to be created for your + // Cross Field Validation needs, however there are some build in Generic Cross Field validations, + // see Baked In Validators and Tags below + + func isDateRangeValid(val interface{}, field interface{}, param string) bool { + + myStruct := val.(myStructType) + + if myStruct.Start.After(field.(time.Time)) { + return false + } + + return true + } + +Multiple Validators + +Multiple validators on a field will process in the order defined + + type Test struct { + Field `validate:"max=10,min=1"` + } + + // max will be checked then min + +Bad Validator definitions are not handled by the library + + type Test struct { + Field `validate:"min=10,max=0"` + } + + // this definition of min max will never validate + +Baked In Validators and Tags + +NOTE: Baked In Cross field validation only compares fields on the same struct, +if cross field + cross struct validation is needed your own custom validator +should be implemented. + +NOTE2: comma is the default separator of validation tags, if you wish to have a comma +included within the parameter i.e. excludesall=, you will need to use the UTF-8 hex +representation 0x2C, which is replaced in the code as a comma, so the above will +become excludesall=0x2C + +Here is a list of the current built in validators: + + - + Tells the validation to skip this struct field; this is particularily + handy in ignoring embedded structs from being validated. (Usage: -) + + | + This is the 'or' operator allowing multiple validators to be used and + accepted. (Usage: rbg|rgba) <-- this would allow either rgb or rgba + colors to be accepted. This can also be combined with 'and' for example + ( Usage: omitempty,rgb|rgba) + + structonly + When a field that is a nest struct in encountered and contains this flag + any validation on the nested struct such as "required" will be run, but + none of the nested struct fields will be validated. This is usefull if + inside of you program you know the struct will be valid, but need to + verify it has been assigned. + + omitempty + Allows conitional validation, for example if a field is not set with + a value (Determined by the required validator) then other validation + such as min or max won't run, but if a value is set validation will run. + (Usage: omitempty) + + required + This validates that the value is not the data types default value. + For numbers ensures value is not zero. For strings ensures value is + not "". For slices, arrays, and maps, ensures the length is not zero. + (Usage: required) + + len + For numbers, max will ensure that the value is + equal to the parameter given. For strings, it checks that + the string length is exactly that number of characters. For slices, + arrays, and maps, validates the number of items. (Usage: len=10) + + max + For numbers, max will ensure that the value is + less than or equal to the parameter given. For strings, it checks + that the string length is at most that number of characters. For + slices, arrays, and maps, validates the number of items. (Usage: max=10) + + min + For numbers, min will ensure that the value is + greater or equal to the parameter given. For strings, it checks that + the string length is at least that number of characters. For slices, + arrays, and maps, validates the number of items. (Usage: min=10) + + eq + For strings & numbers, eq will ensure that the value is + equal to the parameter given. For slices, arrays, and maps, + validates the number of items. (Usage: eq=10) + + ne + For strings & numbers, eq will ensure that the value is not + equal to the parameter given. For slices, arrays, and maps, + validates the number of items. (Usage: eq=10) + + gt + For numbers, this will ensure that the value is greater than the + parameter given. For strings, it checks that the string length + is greater than that number of characters. For slices, arrays + and maps it validates the number of items. (Usage: gt=10) + For time.Time ensures the time value is greater than time.Now.UTC() + (Usage: gt) + + gte + Same as 'min' above. Kept both to make terminology with 'len' easier + (Usage: gte=10) + For time.Time ensures the time value is greater than or equal to time.Now.UTC() + (Usage: gte) + + lt + For numbers, this will ensure that the value is + less than the parameter given. For strings, it checks + that the string length is less than that number of characters. + For slices, arrays, and maps it validates the number of items. + (Usage: lt=10) + For time.Time ensures the time value is less than time.Now.UTC() + (Usage: lt) + + lte + Same as 'max' above. Kept both to make terminology with 'len' easier + (Usage: lte=10) + For time.Time ensures the time value is less than or equal to time.Now.UTC() + (Usage: lte) + + eqfield + This will validate the field value against another fields value either within + a struct or passed in field. + usage examples are for validation of a password and confirm password: + Validation on Password field using validate.Struct Usage(eqfield=ConfirmPassword) + Validating by field validate.FieldWithValue(password, confirmpassword, "eqfield") + + nefield + This will validate the field value against another fields value either within + a struct or passed in field. + usage examples are for ensuring two colors are not the same: + Validation on Color field using validate.Struct Usage(nefield=Color2) + Validating by field validate.FieldWithValue(color1, color2, "nefield") + + gtfield + Only valid for Numbers and time.Time types, this will validate the field value + against another fields value either within a struct or passed in field. + usage examples are for validation of a Start and End date: + Validation on End field using validate.Struct Usage(gtfield=Start) + Validating by field validate.FieldWithValue(start, end, "gtfield") + + gtefield + Only valid for Numbers and time.Time types, this will validate the field value + against another fields value either within a struct or passed in field. + usage examples are for validation of a Start and End date: + Validation on End field using validate.Struct Usage(gtefield=Start) + Validating by field validate.FieldWithValue(start, end, "gtefield") + + ltfield + Only valid for Numbers and time.Time types, this will validate the field value + against another fields value either within a struct or passed in field. + usage examples are for validation of a Start and End date: + Validation on End field using validate.Struct Usage(ltfield=Start) + Validating by field validate.FieldWithValue(start, end, "ltfield") + + ltefield + Only valid for Numbers and time.Time types, this will validate the field value + against another fields value either within a struct or passed in field. + usage examples are for validation of a Start and End date: + Validation on End field using validate.Struct Usage(ltefield=Start) + Validating by field validate.FieldWithValue(start, end, "ltefield") + + alpha + This validates that a string value contains alpha characters only + (Usage: alpha) + + alphanum + This validates that a string value contains alphanumeric characters only + (Usage: alphanum) + + numeric + This validates that a string value contains a basic numeric value. + basic excludes exponents etc... + (Usage: numeric) + + hexadecimal + This validates that a string value contains a valid hexadecimal. + (Usage: hexadecimal) + + hexcolor + This validates that a string value contains a valid hex color including + hashtag (#) + (Usage: hexcolor) + + rgb + This validates that a string value contains a valid rgb color + (Usage: rgb) + + rgba + This validates that a string value contains a valid rgba color + (Usage: rgba) + + hsl + This validates that a string value contains a valid hsl color + (Usage: hsl) + + hsla + This validates that a string value contains a valid hsla color + (Usage: hsla) + + email + This validates that a string value contains a valid email + This may not conform to all possibilities of any rfc standard, but neither + does any email provider accept all posibilities... + (Usage: email) + + url + This validates that a string value contains a valid url + This will accept any url the golang request uri accepts but must contain + a schema for example http:// or rtmp:// + (Usage: url) + + uri + This validates that a string value contains a valid uri + This will accept any uri the golang request uri accepts (Usage: uri) + + base64 + This validates that a string value contains a valid base64 value. + Although an empty string is valid base64 this will report an empty string + as an error, if you wish to accept an empty string as valid you can use + this with the omitempty tag. (Usage: base64) + + contains + This validates that a string value contains the substring value. + (Usage: contains=@) + + containsany + This validates that a string value contains any Unicode code points + in the substring value. (Usage: containsany=!@#?) + + containsrune + This validates that a string value contains the supplied rune value. + (Usage: containsrune=@) + + excludes + This validates that a string value does not contain the substring value. + (Usage: excludes=@) + + excludesall + This validates that a string value does not contain any Unicode code + points in the substring value. (Usage: excludesall=!@#?) + + excludesrune + This validates that a string value does not contain the supplied rune value. + (Usage: excludesrune=@) + +Validator notes: + + regex + a regex validator won't be added because commas and = signs can be part of + a regex which conflict with the validation definitions, although workarounds + can be made, they take away from using pure regex's. Furthermore it's quick + and dirty but the regex's become harder to maintain and are not reusable, so + it's as much a programming philosiphy as anything. + + In place of this new validator functions should be created; a regex can be + used within the validator function and even be precompiled for better efficiency + within regexes.go. + + And the best reason, you can submit a pull request and we can keep on adding to the + validation library of this package! + +Panics + +This package panics when bad input is provided, this is by design, bad code like that should not make it to production. + + type Test struct { + TestField string `validate:"nonexistantfunction=1"` + } + + t := &Test{ + TestField: "Test" + } + + validate.Struct(t) // this will panic +*/ +package validator diff --git a/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/regexes.go b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/regexes.go new file mode 100644 index 00000000..e5ed0fae --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/regexes.go @@ -0,0 +1,38 @@ +package validator + +import "regexp" + +const ( + alphaRegexString = "^[a-zA-Z]+$" + alphaNumericRegexString = "^[a-zA-Z0-9]+$" + numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" + numberRegexString = "^[0-9]+$" + hexadecimalRegexString = "^[0-9a-fA-F]+$" + hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + rgbRegexString = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$" + rgbaRegexString = "^rgba\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*((0.[1-9]*)|[01])\\s*\\)$" + hslRegexString = "^hsl\\(\\s*(0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*\\)$" + hslaRegexString = "^hsla\\(\\s*(0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0.[1-9]*)|[01])\\s*\\)$" + emailRegexString = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" + base64RegexString = "(?:^(?:[A-Za-z0-9+\\/]{4}\\n?)*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=)$)" +) + +var ( + alphaRegex = regexp.MustCompile(alphaRegexString) + alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString) + numericRegex = regexp.MustCompile(numericRegexString) + numberRegex = regexp.MustCompile(numberRegexString) + hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString) + hexcolorRegex = regexp.MustCompile(hexcolorRegexString) + rgbRegex = regexp.MustCompile(rgbRegexString) + rgbaRegex = regexp.MustCompile(rgbaRegexString) + hslRegex = regexp.MustCompile(hslRegexString) + hslaRegex = regexp.MustCompile(hslaRegexString) + emailRegex = regexp.MustCompile(emailRegexString) + base64Regex = regexp.MustCompile(base64RegexString) +) + +func matchesRegex(regex *regexp.Regexp, field interface{}) bool { + fieldAsString := field.(string) //this will panic inherently + return regex.MatchString(fieldAsString) +} diff --git a/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/validator.go b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/validator.go new file mode 100644 index 00000000..33110f68 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/validator.go @@ -0,0 +1,571 @@ +/** + * Package validator + * + * MISC: + * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank + * + */ + +package validator + +import ( + "bytes" + "errors" + "fmt" + "reflect" + "strings" + "sync" + "time" + "unicode" +) + +const ( + utf8HexComma = "0x2C" + tagSeparator = "," + orSeparator = "|" + noValidationTag = "-" + tagKeySeparator = "=" + structOnlyTag = "structonly" + omitempty = "omitempty" + fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag" + structErrMsg = "Struct:%s\n" +) + +var structPool *pool + +// Pool holds a channelStructErrors. +type pool struct { + pool chan *StructErrors +} + +// NewPool creates a new pool of Clients. +func newPool(max int) *pool { + return &pool{ + pool: make(chan *StructErrors, max), + } +} + +// Borrow a StructErrors from the pool. +func (p *pool) Borrow() *StructErrors { + var c *StructErrors + + select { + case c = <-p.pool: + default: + c = &StructErrors{ + Errors: map[string]*FieldError{}, + StructErrors: map[string]*StructErrors{}, + } + } + + return c +} + +// Return returns a StructErrors to the pool. +func (p *pool) Return(c *StructErrors) { + + // c.Struct = "" + + select { + case p.pool <- c: + default: + // let it go, let it go... + } +} + +type cachedTags struct { + keyVals [][]string + isOrVal bool +} + +type cachedField struct { + index int + name string + tags []*cachedTags + tag string + kind reflect.Kind + typ reflect.Type + isTime bool +} + +type cachedStruct struct { + children int + name string + kind reflect.Kind + fields []*cachedField +} + +type structsCacheMap struct { + lock sync.RWMutex + m map[reflect.Type]*cachedStruct +} + +func (s *structsCacheMap) Get(key reflect.Type) (*cachedStruct, bool) { + s.lock.RLock() + defer s.lock.RUnlock() + value, ok := s.m[key] + return value, ok +} + +func (s *structsCacheMap) Set(key reflect.Type, value *cachedStruct) { + s.lock.Lock() + defer s.lock.Unlock() + s.m[key] = value +} + +var structCache = &structsCacheMap{m: map[reflect.Type]*cachedStruct{}} + +type fieldsCacheMap struct { + lock sync.RWMutex + m map[string][]*cachedTags +} + +func (s *fieldsCacheMap) Get(key string) ([]*cachedTags, bool) { + s.lock.RLock() + defer s.lock.RUnlock() + value, ok := s.m[key] + return value, ok +} + +func (s *fieldsCacheMap) Set(key string, value []*cachedTags) { + s.lock.Lock() + defer s.lock.Unlock() + s.m[key] = value +} + +var fieldsCache = &fieldsCacheMap{m: map[string][]*cachedTags{}} + +// FieldError contains a single field's validation error along +// with other properties that may be needed for error message creation +type FieldError struct { + Field string + Tag string + Kind reflect.Kind + Type reflect.Type + Param string + Value interface{} +} + +// This is intended for use in development + debugging and not intended to be a production error message. +// it also allows FieldError to be used as an Error interface +func (e *FieldError) Error() string { + return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag) +} + +// StructErrors is hierarchical list of field and struct validation errors +// for a non hierarchical representation please see the Flatten method for StructErrors +type StructErrors struct { + // Name of the Struct + Struct string + // Struct Field Errors + Errors map[string]*FieldError + // Struct Fields of type struct and their errors + // key = Field Name of current struct, but internally Struct will be the actual struct name unless anonymous struct, it will be blank + StructErrors map[string]*StructErrors +} + +// This is intended for use in development + debugging and not intended to be a production error message. +// it also allows StructErrors to be used as an Error interface +func (e *StructErrors) Error() string { + buff := bytes.NewBufferString(fmt.Sprintf(structErrMsg, e.Struct)) + + for _, err := range e.Errors { + buff.WriteString(err.Error()) + buff.WriteString("\n") + } + + for _, err := range e.StructErrors { + buff.WriteString(err.Error()) + } + + return buff.String() +} + +// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name +// for those that want/need it +func (e *StructErrors) Flatten() map[string]*FieldError { + + if e == nil { + return nil + } + + errs := map[string]*FieldError{} + + for _, f := range e.Errors { + + errs[f.Field] = f + } + + for key, val := range e.StructErrors { + + otherErrs := val.Flatten() + + for _, f2 := range otherErrs { + + f2.Field = fmt.Sprintf("%s.%s", key, f2.Field) + errs[f2.Field] = f2 + } + } + + return errs +} + +// Func accepts all values needed for file and cross field validation +// top = top level struct when validating by struct otherwise nil +// current = current level struct when validating by struct otherwise optional comparison value +// f = field value for validation +// param = parameter used in validation i.e. gt=0 param would be 0 +type Func func(top interface{}, current interface{}, f interface{}, param string) bool + +// Validate implements the Validate Struct +// NOTE: Fields within are not thread safe and that is on purpose +// Functions and Tags should all be predifined before use, so subscribe to the philosiphy +// or make it thread safe on your end +type Validate struct { + // tagName being used. + tagName string + // validateFuncs is a map of validation functions and the tag keys + validationFuncs map[string]Func +} + +// New creates a new Validate instance for use. +func New(tagName string, funcs map[string]Func) *Validate { + + structPool = newPool(10) + + return &Validate{ + tagName: tagName, + validationFuncs: funcs, + } +} + +// SetTag sets tagName of the Validator to one of your choosing after creation +// perhaps to dodge a tag name conflict in a specific section of code +// NOTE: this method is not thread-safe +func (v *Validate) SetTag(tagName string) { + v.tagName = tagName +} + +// SetStructPoolMax sets the struct pools max size. this may be usefull for fine grained +// performance tuning towards your application, however, the default should be fine for +// nearly all cases. only increase if you have a deeply nested struct structure. +// NOTE: this method is not thread-safe +// NOTE: this is only here to keep compatibility with v5, in v6 the method will be removed +// and the max pool size will be passed into the New function +func (v *Validate) SetMaxStructPoolSize(max int) { + structPool = newPool(max) +} + +// AddFunction adds a validation Func to a Validate's map of validators denoted by the key +// NOTE: if the key already exists, it will get replaced. +// NOTE: this method is not thread-safe +func (v *Validate) AddFunction(key string, f Func) error { + + if len(key) == 0 { + return errors.New("Function Key cannot be empty") + } + + if f == nil { + return errors.New("Function cannot be empty") + } + + v.validationFuncs[key] = f + + return nil +} + +// Struct validates a struct, even it's nested structs, and returns a struct containing the errors +// NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good +// way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to +// the Array or Map. +func (v *Validate) Struct(s interface{}) *StructErrors { + + return v.structRecursive(s, s, s) +} + +// structRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors +func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}) *StructErrors { + + structValue := reflect.ValueOf(s) + + if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { + return v.structRecursive(top, current, structValue.Elem().Interface()) + } + + if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface { + panic("interface passed for validation is not a struct") + } + + structType := reflect.TypeOf(s) + + var structName string + var numFields int + var cs *cachedStruct + var isCached bool + + cs, isCached = structCache.Get(structType) + + if isCached { + structName = cs.name + numFields = cs.children + } else { + structName = structType.Name() + numFields = structValue.NumField() + cs = &cachedStruct{name: structName, children: numFields} + structCache.Set(structType, cs) + } + + validationErrors := structPool.Borrow() + validationErrors.Struct = structName + + for i := 0; i < numFields; i++ { + + var valueField reflect.Value + var cField *cachedField + var typeField reflect.StructField + + if isCached { + cField = cs.fields[i] + valueField = structValue.Field(cField.index) + + if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { + valueField = valueField.Elem() + } + } else { + valueField = structValue.Field(i) + + if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { + valueField = valueField.Elem() + } + + typeField = structType.Field(i) + + cField = &cachedField{index: i, tag: typeField.Tag.Get(v.tagName)} + + if cField.tag == noValidationTag { + cs.children-- + continue + } + + // if no validation and not a struct (which may containt fields for validation) + if cField.tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) { + cs.children-- + continue + } + + cField.name = typeField.Name + cField.kind = valueField.Kind() + cField.typ = valueField.Type() + } + + // this can happen if the first cache value was nil + // but the second actually has a value + if cField.kind == reflect.Ptr { + cField.kind = valueField.Kind() + } + + switch cField.kind { + + case reflect.Struct, reflect.Interface: + + if !unicode.IsUpper(rune(cField.name[0])) { + cs.children-- + continue + } + + if cField.isTime || valueField.Type() == reflect.TypeOf(time.Time{}) { + + cField.isTime = true + + if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { + validationErrors.Errors[fieldError.Field] = fieldError + // free up memory reference + fieldError = nil + } + + } else { + + if strings.Contains(cField.tag, structOnlyTag) { + cs.children-- + continue + } + + if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil { + validationErrors.StructErrors[cField.name] = structErrors + // free up memory map no longer needed + structErrors = nil + } + } + + default: + + if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { + validationErrors.Errors[fieldError.Field] = fieldError + // free up memory reference + fieldError = nil + } + } + + if !isCached { + cs.fields = append(cs.fields, cField) + } + } + + if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 { + structPool.Return(validationErrors) + return nil + } + + return validationErrors +} + +// Field allows validation of a single field, still using tag style validation to check multiple errors +func (v *Validate) Field(f interface{}, tag string) *FieldError { + + return v.FieldWithValue(nil, f, tag) +} + +// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors +func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError { + + return v.fieldWithNameAndValue(nil, val, f, tag, "", true, nil) +} + +func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, tag string, name string, isSingleField bool, cacheField *cachedField) *FieldError { + + var cField *cachedField + var isCached bool + + // This is a double check if coming from validate.Struct but need to be here in case function is called directly + if tag == noValidationTag { + return nil + } + + if strings.Contains(tag, omitempty) && !hasValue(val, current, f, "") { + return nil + } + + if cacheField == nil { + valueField := reflect.ValueOf(f) + + if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { + valueField = valueField.Elem() + f = valueField.Interface() + } + + cField = &cachedField{name: name, kind: valueField.Kind(), tag: tag, typ: valueField.Type()} + } else { + cField = cacheField + } + + switch cField.kind { + + case reflect.Struct, reflect.Interface, reflect.Invalid: + + if cField.typ != reflect.TypeOf(time.Time{}) { + panic("Invalid field passed to ValidateFieldWithTag") + } + } + + if len(cField.tags) == 0 { + + if isSingleField { + cField.tags, isCached = fieldsCache.Get(tag) + } + + if !isCached { + + for _, t := range strings.Split(tag, tagSeparator) { + + orVals := strings.Split(t, orSeparator) + cTag := &cachedTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))} + cField.tags = append(cField.tags, cTag) + + for i, val := range orVals { + vals := strings.SplitN(val, tagKeySeparator, 2) + + key := strings.TrimSpace(vals[0]) + + if len(key) == 0 { + panic(fmt.Sprintf("Invalid validation tag on field %s", name)) + } + + param := "" + if len(vals) > 1 { + param = strings.Replace(vals[1], utf8HexComma, ",", -1) + } + + cTag.keyVals[i] = []string{key, param} + } + } + + if isSingleField { + fieldsCache.Set(cField.tag, cField.tags) + } + } + } + + var fieldErr *FieldError + var err error + + for _, cTag := range cField.tags { + + if cTag.isOrVal { + + errTag := "" + + for _, val := range cTag.keyVals { + + fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, val[0], val[1], name) + + if err == nil { + return nil + } + + errTag += orSeparator + fieldErr.Tag + } + + errTag = strings.TrimLeft(errTag, orSeparator) + + fieldErr.Tag = errTag + fieldErr.Kind = cField.kind + fieldErr.Type = cField.typ + + return fieldErr + } + + if fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, cTag.keyVals[0][0], cTag.keyVals[0][1], name); err != nil { + + fieldErr.Kind = cField.kind + fieldErr.Type = cField.typ + + return fieldErr + } + } + + return nil +} + +func (v *Validate) fieldWithNameAndSingleTag(val interface{}, current interface{}, f interface{}, key string, param string, name string) (*FieldError, error) { + + // OK to continue because we checked it's existance before getting into this loop + if key == omitempty { + return nil, nil + } + + valFunc, ok := v.validationFuncs[key] + if !ok { + panic(fmt.Sprintf("Undefined validation function on field %s", name)) + } + + if err := valFunc(val, current, f, param); err { + return nil, nil + } + + return &FieldError{ + Field: name, + Tag: key, + Value: f, + Param: param, + }, errors.New(key) +} diff --git a/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/validator_test.go b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/validator_test.go new file mode 100644 index 00000000..aa8c7495 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/validator_test.go @@ -0,0 +1,2497 @@ +package validator + +import ( + "fmt" + "path" + "reflect" + "runtime" + "testing" + "time" +) + +// NOTES: +// - Run "go test" to run tests +// - Run "gocov test | gocov report" to report on test converage by file +// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called +// +// +// go test -cpuprofile cpu.out +// ./validator.test -test.bench=. -test.cpuprofile=cpu.prof +// go tool pprof validator.test cpu.prof +// +// +// go test -memprofile mem.out + +type I interface { + Foo() string +} + +type Impl struct { + F string `validate:"len=3"` +} + +func (i *Impl) Foo() string { + return i.F +} + +type SubTest struct { + Test string `validate:"required"` +} + +type TestInterface struct { + Iface I +} + +type TestString struct { + BlankTag string `validate:""` + Required string `validate:"required"` + Len string `validate:"len=10"` + Min string `validate:"min=1"` + Max string `validate:"max=10"` + MinMax string `validate:"min=1,max=10"` + Lt string `validate:"lt=10"` + Lte string `validate:"lte=10"` + Gt string `validate:"gt=10"` + Gte string `validate:"gte=10"` + OmitEmpty string `validate:"omitempty,min=1,max=10"` + Sub *SubTest + SubIgnore *SubTest `validate:"-"` + Anonymous struct { + A string `validate:"required"` + } + Iface I +} + +type TestInt32 struct { + Required int `validate:"required"` + Len int `validate:"len=10"` + Min int `validate:"min=1"` + Max int `validate:"max=10"` + MinMax int `validate:"min=1,max=10"` + Lt int `validate:"lt=10"` + Lte int `validate:"lte=10"` + Gt int `validate:"gt=10"` + Gte int `validate:"gte=10"` + OmitEmpty int `validate:"omitempty,min=1,max=10"` +} + +type TestUint64 struct { + Required uint64 `validate:"required"` + Len uint64 `validate:"len=10"` + Min uint64 `validate:"min=1"` + Max uint64 `validate:"max=10"` + MinMax uint64 `validate:"min=1,max=10"` + OmitEmpty uint64 `validate:"omitempty,min=1,max=10"` +} + +type TestFloat64 struct { + Required float64 `validate:"required"` + Len float64 `validate:"len=10"` + Min float64 `validate:"min=1"` + Max float64 `validate:"max=10"` + MinMax float64 `validate:"min=1,max=10"` + Lte float64 `validate:"lte=10"` + OmitEmpty float64 `validate:"omitempty,min=1,max=10"` +} + +type TestSlice struct { + Required []int `validate:"required"` + Len []int `validate:"len=10"` + Min []int `validate:"min=1"` + Max []int `validate:"max=10"` + MinMax []int `validate:"min=1,max=10"` + OmitEmpty []int `validate:"omitempty,min=1,max=10"` +} + +var validate = New("validate", BakedInValidators) + +func IsEqual(t *testing.T, val1, val2 interface{}) bool { + v1 := reflect.ValueOf(val1) + v2 := reflect.ValueOf(val2) + + if v1.Kind() == reflect.Ptr { + v1 = v1.Elem() + } + + if v2.Kind() == reflect.Ptr { + v2 = v2.Elem() + } + + if !v1.IsValid() && !v2.IsValid() { + return true + } + + v1Underlying := reflect.Zero(reflect.TypeOf(v1)).Interface() + v2Underlying := reflect.Zero(reflect.TypeOf(v2)).Interface() + + if v1 == v1Underlying { + if v2 == v2Underlying { + goto CASE4 + } else { + goto CASE3 + } + } else { + if v2 == v2Underlying { + goto CASE2 + } else { + goto CASE1 + } + } + +CASE1: + return reflect.DeepEqual(v1.Interface(), v2.Interface()) + +CASE2: + return reflect.DeepEqual(v1.Interface(), v2) +CASE3: + return reflect.DeepEqual(v1, v2.Interface()) +CASE4: + return reflect.DeepEqual(v1, v2) +} + +func Equal(t *testing.T, val1, val2 interface{}) { + EqualSkip(t, 2, val1, val2) +} + +func EqualSkip(t *testing.T, skip int, val1, val2 interface{}) { + + if !IsEqual(t, val1, val2) { + + _, file, line, _ := runtime.Caller(skip) + fmt.Printf("%s:%d %v does not equal %v\n", path.Base(file), line, val1, val2) + t.FailNow() + } +} + +func NotEqual(t *testing.T, val1, val2 interface{}) { + NotEqualSkip(t, 2, val1, val2) +} + +func NotEqualSkip(t *testing.T, skip int, val1, val2 interface{}) { + + if IsEqual(t, val1, val2) { + _, file, line, _ := runtime.Caller(skip) + fmt.Printf("%s:%d %v should not be equal %v\n", path.Base(file), line, val1, val2) + t.FailNow() + } +} + +func PanicMatches(t *testing.T, fn func(), matches string) { + PanicMatchesSkip(t, 2, fn, matches) +} + +func PanicMatchesSkip(t *testing.T, skip int, fn func(), matches string) { + + _, file, line, _ := runtime.Caller(skip) + + defer func() { + if r := recover(); r != nil { + err := fmt.Sprintf("%s", r) + + if err != matches { + fmt.Printf("%s:%d Panic... expected [%s] received [%s]", path.Base(file), line, matches, err) + t.FailNow() + } + } + }() + + fn() +} + +func AssertStruct(t *testing.T, s *StructErrors, structFieldName string, expectedStructName string) *StructErrors { + + val, ok := s.StructErrors[structFieldName] + EqualSkip(t, 2, ok, true) + NotEqualSkip(t, 2, val, nil) + EqualSkip(t, 2, val.Struct, expectedStructName) + + return val +} + +func AssertFieldError(t *testing.T, s *StructErrors, field string, expectedTag string) { + + val, ok := s.Errors[field] + EqualSkip(t, 2, ok, true) + NotEqualSkip(t, 2, val, nil) + EqualSkip(t, 2, val.Field, field) + EqualSkip(t, 2, val.Tag, expectedTag) +} + +func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, expectedTag string) { + + val, ok := s[field] + EqualSkip(t, 2, ok, true) + NotEqualSkip(t, 2, val, nil) + EqualSkip(t, 2, val.Field, field) + EqualSkip(t, 2, val.Tag, expectedTag) +} + +func TestExcludesRuneValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"excludesrune=☻"` + Tag string + ExpectedNil bool + }{ + {Value: "a☺b☻c☹d", Tag: "excludesrune=☻", ExpectedNil: false}, + {Value: "abcd", Tag: "excludesrune=☻", ExpectedNil: true}, + } + + for i, s := range tests { + err := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { + t.Fatalf("Index: %d failed Error: %s", i, err) + } + + errs := validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } +} + +func TestExcludesAllValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"excludesall=@!{}[]"` + Tag string + ExpectedNil bool + }{ + {Value: "abcd@!jfk", Tag: "excludesall=@!{}[]", ExpectedNil: false}, + {Value: "abcdefg", Tag: "excludesall=@!{}[]", ExpectedNil: true}, + } + + for i, s := range tests { + err := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { + t.Fatalf("Index: %d failed Error: %s", i, err) + } + + errs := validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } + + username := "joeybloggs " + + err := validate.Field(username, "excludesall=@ ") + NotEqual(t, err, nil) + + excluded := "," + + err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C?") + NotEqual(t, err, nil) + + excluded = "=" + + err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C=?") + NotEqual(t, err, nil) +} + +func TestExcludesValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"excludes=@"` + Tag string + ExpectedNil bool + }{ + {Value: "abcd@!jfk", Tag: "excludes=@", ExpectedNil: false}, + {Value: "abcdq!jfk", Tag: "excludes=@", ExpectedNil: true}, + } + + for i, s := range tests { + err := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { + t.Fatalf("Index: %d failed Error: %s", i, err) + } + + errs := validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } +} + +func TestContainsRuneValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"containsrune=☻"` + Tag string + ExpectedNil bool + }{ + {Value: "a☺b☻c☹d", Tag: "containsrune=☻", ExpectedNil: true}, + {Value: "abcd", Tag: "containsrune=☻", ExpectedNil: false}, + } + + for i, s := range tests { + err := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { + t.Fatalf("Index: %d failed Error: %s", i, err) + } + + errs := validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } +} + +func TestContainsAnyValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"containsany=@!{}[]"` + Tag string + ExpectedNil bool + }{ + {Value: "abcd@!jfk", Tag: "containsany=@!{}[]", ExpectedNil: true}, + {Value: "abcdefg", Tag: "containsany=@!{}[]", ExpectedNil: false}, + } + + for i, s := range tests { + err := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { + t.Fatalf("Index: %d failed Error: %s", i, err) + } + + errs := validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } +} + +func TestContainsValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"contains=@"` + Tag string + ExpectedNil bool + }{ + {Value: "abcd@!jfk", Tag: "contains=@", ExpectedNil: true}, + {Value: "abcdq!jfk", Tag: "contains=@", ExpectedNil: false}, + } + + for i, s := range tests { + err := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { + t.Fatalf("Index: %d failed Error: %s", i, err) + } + + errs := validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } +} + +func TestIsNeFieldValidation(t *testing.T) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + var j2 uint64 + var k2 float64 + s2 := "abcdef" + i2 := 3 + j2 = 2 + k2 = 1.5434456 + arr2 := []string{"test", "test2"} + arr3 := []string{"test"} + now2 := now + + err := validate.FieldWithValue(s, s2, "nefield") + Equal(t, err, nil) + + err = validate.FieldWithValue(i2, i, "nefield") + Equal(t, err, nil) + + err = validate.FieldWithValue(j2, j, "nefield") + Equal(t, err, nil) + + err = validate.FieldWithValue(k2, k, "nefield") + Equal(t, err, nil) + + err = validate.FieldWithValue(arr2, arr, "nefield") + Equal(t, err, nil) + + err = validate.FieldWithValue(now2, now, "nefield") + NotEqual(t, err, nil) + + err = validate.FieldWithValue(arr3, arr, "nefield") + NotEqual(t, err, nil) + + type Test struct { + Start *time.Time `validate:"nefield=End"` + End *time.Time + } + + sv := &Test{ + Start: &now, + End: &now, + } + + errs := validate.Struct(sv) + NotEqual(t, errs, nil) + + now3 := time.Now().UTC() + + sv = &Test{ + Start: &now, + End: &now3, + } + + errs = validate.Struct(sv) + Equal(t, errs, nil) + + channel := make(chan string) + + PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "nefield") }, "struct not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(5, channel, "nefield") }, "Bad field type chan string") + PanicMatches(t, func() { validate.FieldWithValue(5, now, "nefield") }, "Bad Top Level field type") + + type Test2 struct { + Start *time.Time `validate:"nefield=NonExistantField"` + End *time.Time + } + + sv2 := &Test2{ + Start: &now, + End: &now, + } + + PanicMatches(t, func() { validate.Struct(sv2) }, "Field \"NonExistantField\" not found in struct") +} + +func TestIsNeValidation(t *testing.T) { + + var j uint64 + var k float64 + s := "abcdef" + i := 3 + j = 2 + k = 1.5434 + arr := []string{"test"} + now := time.Now().UTC() + + err := validate.Field(s, "ne=abcd") + Equal(t, err, nil) + + err = validate.Field(i, "ne=1") + Equal(t, err, nil) + + err = validate.Field(j, "ne=1") + Equal(t, err, nil) + + err = validate.Field(k, "ne=1.543") + Equal(t, err, nil) + + err = validate.Field(arr, "ne=2") + Equal(t, err, nil) + + err = validate.Field(arr, "ne=1") + NotEqual(t, err, nil) + + PanicMatches(t, func() { validate.Field(now, "ne=now") }, "Bad field type time.Time") +} + +func TestIsEqFieldValidation(t *testing.T) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + var j2 uint64 + var k2 float64 + s2 := "abcd" + i2 := 1 + j2 = 1 + k2 = 1.543 + arr2 := []string{"test"} + arr3 := []string{"test", "test2"} + now2 := now + + err := validate.FieldWithValue(s, s2, "eqfield") + Equal(t, err, nil) + + err = validate.FieldWithValue(i2, i, "eqfield") + Equal(t, err, nil) + + err = validate.FieldWithValue(j2, j, "eqfield") + Equal(t, err, nil) + + err = validate.FieldWithValue(k2, k, "eqfield") + Equal(t, err, nil) + + err = validate.FieldWithValue(arr2, arr, "eqfield") + Equal(t, err, nil) + + err = validate.FieldWithValue(now2, now, "eqfield") + Equal(t, err, nil) + + err = validate.FieldWithValue(arr3, arr, "eqfield") + NotEqual(t, err, nil) + + type Test struct { + Start *time.Time `validate:"eqfield=End"` + End *time.Time + } + + sv := &Test{ + Start: &now, + End: &now, + } + + errs := validate.Struct(sv) + Equal(t, errs, nil) + + now3 := time.Now().UTC() + + sv = &Test{ + Start: &now, + End: &now3, + } + + errs = validate.Struct(sv) + NotEqual(t, errs, nil) + + channel := make(chan string) + + PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "eqfield") }, "struct not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(5, channel, "eqfield") }, "Bad field type chan string") + PanicMatches(t, func() { validate.FieldWithValue(5, now, "eqfield") }, "Bad Top Level field type") + + type Test2 struct { + Start *time.Time `validate:"eqfield=NonExistantField"` + End *time.Time + } + + sv2 := &Test2{ + Start: &now, + End: &now, + } + + PanicMatches(t, func() { validate.Struct(sv2) }, "Field \"NonExistantField\" not found in struct") +} + +func TestIsEqValidation(t *testing.T) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + err := validate.Field(s, "eq=abcd") + Equal(t, err, nil) + + err = validate.Field(i, "eq=1") + Equal(t, err, nil) + + err = validate.Field(j, "eq=1") + Equal(t, err, nil) + + err = validate.Field(k, "eq=1.543") + Equal(t, err, nil) + + err = validate.Field(arr, "eq=1") + Equal(t, err, nil) + + err = validate.Field(arr, "eq=2") + NotEqual(t, err, nil) + + PanicMatches(t, func() { validate.Field(now, "eq=now") }, "Bad field type time.Time") +} + +func TestBase64Validation(t *testing.T) { + + s := "dW5pY29ybg==" + + err := validate.Field(s, "base64") + Equal(t, err, nil) + + s = "dGhpIGlzIGEgdGVzdCBiYXNlNjQ=" + err = validate.Field(s, "base64") + Equal(t, err, nil) + + s = "" + err = validate.Field(s, "base64") + NotEqual(t, err, nil) + + s = "dW5pY29ybg== foo bar" + err = validate.Field(s, "base64") + NotEqual(t, err, nil) +} + +func TestStructOnlyValidation(t *testing.T) { + + type Inner struct { + Test string `validate:"len=5"` + } + + type Outer struct { + InnerStruct *Inner `validate:"required,structonly"` + } + + outer := &Outer{ + InnerStruct: nil, + } + + errs := validate.Struct(outer).Flatten() + NotEqual(t, errs, nil) + + inner := &Inner{ + Test: "1234", + } + + outer = &Outer{ + InnerStruct: inner, + } + + errs = validate.Struct(outer).Flatten() + NotEqual(t, errs, nil) + Equal(t, len(errs), 0) +} + +func TestGtField(t *testing.T) { + + type TimeTest struct { + Start *time.Time `validate:"required,gt"` + End *time.Time `validate:"required,gt,gtfield=Start"` + } + + now := time.Now() + start := now.Add(time.Hour * 24) + end := start.Add(time.Hour * 24) + + timeTest := &TimeTest{ + Start: &start, + End: &end, + } + + errs := validate.Struct(timeTest) + Equal(t, errs, nil) + + timeTest = &TimeTest{ + Start: &end, + End: &start, + } + + errs2 := validate.Struct(timeTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "End", "gtfield") + + err3 := validate.FieldWithValue(&start, &end, "gtfield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(&end, &start, "gtfield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "gtfield") + + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,gtfield=Val1"` + } + + intTest := &IntTest{ + Val1: 1, + Val2: 5, + } + + errs = validate.Struct(intTest) + Equal(t, errs, nil) + + intTest = &IntTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validate.Struct(intTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "gtfield") + + err3 = validate.FieldWithValue(int(1), int(5), "gtfield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(int(5), int(1), "gtfield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "gtfield") + + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,gtfield=Val1"` + } + + uIntTest := &UIntTest{ + Val1: 1, + Val2: 5, + } + + errs = validate.Struct(uIntTest) + Equal(t, errs, nil) + + uIntTest = &UIntTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validate.Struct(uIntTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "gtfield") + + err3 = validate.FieldWithValue(uint(1), uint(5), "gtfield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(uint(5), uint(1), "gtfield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "gtfield") + + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,gtfield=Val1"` + } + + floatTest := &FloatTest{ + Val1: 1, + Val2: 5, + } + + errs = validate.Struct(floatTest) + Equal(t, errs, nil) + + floatTest = &FloatTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validate.Struct(floatTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "gtfield") + + err3 = validate.FieldWithValue(float32(1), float32(5), "gtfield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(float32(5), float32(1), "gtfield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "gtfield") + + PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "gtfield") }, "struct not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(5, "T", "gtfield") }, "Bad field type string") + PanicMatches(t, func() { validate.FieldWithValue(5, start, "gtfield") }, "Bad Top Level field type") + + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,gtfield=NonExistantField"` + } + + timeTest2 := &TimeTest2{ + Start: &start, + End: &end, + } + + PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +} + +func TestLtField(t *testing.T) { + + type TimeTest struct { + Start *time.Time `validate:"required,lt,ltfield=End"` + End *time.Time `validate:"required,lt"` + } + + now := time.Now() + start := now.Add(time.Hour * 24 * -1 * 2) + end := start.Add(time.Hour * 24) + + timeTest := &TimeTest{ + Start: &start, + End: &end, + } + + errs := validate.Struct(timeTest) + Equal(t, errs, nil) + + timeTest = &TimeTest{ + Start: &end, + End: &start, + } + + errs2 := validate.Struct(timeTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Start", "ltfield") + + err3 := validate.FieldWithValue(&end, &start, "ltfield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(&start, &end, "ltfield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "ltfield") + + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,ltfield=Val1"` + } + + intTest := &IntTest{ + Val1: 5, + Val2: 1, + } + + errs = validate.Struct(intTest) + Equal(t, errs, nil) + + intTest = &IntTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validate.Struct(intTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "ltfield") + + err3 = validate.FieldWithValue(int(5), int(1), "ltfield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(int(1), int(5), "ltfield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "ltfield") + + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,ltfield=Val1"` + } + + uIntTest := &UIntTest{ + Val1: 5, + Val2: 1, + } + + errs = validate.Struct(uIntTest) + Equal(t, errs, nil) + + uIntTest = &UIntTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validate.Struct(uIntTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "ltfield") + + err3 = validate.FieldWithValue(uint(5), uint(1), "ltfield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(uint(1), uint(5), "ltfield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "ltfield") + + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,ltfield=Val1"` + } + + floatTest := &FloatTest{ + Val1: 5, + Val2: 1, + } + + errs = validate.Struct(floatTest) + Equal(t, errs, nil) + + floatTest = &FloatTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validate.Struct(floatTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "ltfield") + + err3 = validate.FieldWithValue(float32(5), float32(1), "ltfield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(float32(1), float32(5), "ltfield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "ltfield") + + PanicMatches(t, func() { validate.FieldWithValue(nil, 5, "ltfield") }, "struct not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(1, "T", "ltfield") }, "Bad field type string") + PanicMatches(t, func() { validate.FieldWithValue(1, end, "ltfield") }, "Bad Top Level field type") + + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,ltfield=NonExistantField"` + } + + timeTest2 := &TimeTest2{ + Start: &end, + End: &start, + } + + PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +} + +func TestLteField(t *testing.T) { + + type TimeTest struct { + Start *time.Time `validate:"required,lte,ltefield=End"` + End *time.Time `validate:"required,lte"` + } + + now := time.Now() + start := now.Add(time.Hour * 24 * -1 * 2) + end := start.Add(time.Hour * 24) + + timeTest := &TimeTest{ + Start: &start, + End: &end, + } + + errs := validate.Struct(timeTest) + Equal(t, errs, nil) + + timeTest = &TimeTest{ + Start: &end, + End: &start, + } + + errs2 := validate.Struct(timeTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Start", "ltefield") + + err3 := validate.FieldWithValue(&end, &start, "ltefield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(&start, &end, "ltefield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "ltefield") + + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,ltefield=Val1"` + } + + intTest := &IntTest{ + Val1: 5, + Val2: 1, + } + + errs = validate.Struct(intTest) + Equal(t, errs, nil) + + intTest = &IntTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validate.Struct(intTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "ltefield") + + err3 = validate.FieldWithValue(int(5), int(1), "ltefield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(int(1), int(5), "ltefield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "ltefield") + + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,ltefield=Val1"` + } + + uIntTest := &UIntTest{ + Val1: 5, + Val2: 1, + } + + errs = validate.Struct(uIntTest) + Equal(t, errs, nil) + + uIntTest = &UIntTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validate.Struct(uIntTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "ltefield") + + err3 = validate.FieldWithValue(uint(5), uint(1), "ltefield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(uint(1), uint(5), "ltefield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "ltefield") + + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,ltefield=Val1"` + } + + floatTest := &FloatTest{ + Val1: 5, + Val2: 1, + } + + errs = validate.Struct(floatTest) + Equal(t, errs, nil) + + floatTest = &FloatTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validate.Struct(floatTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "ltefield") + + err3 = validate.FieldWithValue(float32(5), float32(1), "ltefield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(float32(1), float32(5), "ltefield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "ltefield") + + PanicMatches(t, func() { validate.FieldWithValue(nil, 5, "ltefield") }, "struct not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(1, "T", "ltefield") }, "Bad field type string") + PanicMatches(t, func() { validate.FieldWithValue(1, end, "ltefield") }, "Bad Top Level field type") + + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,ltefield=NonExistantField"` + } + + timeTest2 := &TimeTest2{ + Start: &end, + End: &start, + } + + PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +} + +func TestGteField(t *testing.T) { + + type TimeTest struct { + Start *time.Time `validate:"required,gte"` + End *time.Time `validate:"required,gte,gtefield=Start"` + } + + now := time.Now() + start := now.Add(time.Hour * 24) + end := start.Add(time.Hour * 24) + + timeTest := &TimeTest{ + Start: &start, + End: &end, + } + + errs := validate.Struct(timeTest) + Equal(t, errs, nil) + + timeTest = &TimeTest{ + Start: &end, + End: &start, + } + + errs2 := validate.Struct(timeTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "End", "gtefield") + + err3 := validate.FieldWithValue(&start, &end, "gtefield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(&end, &start, "gtefield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "gtefield") + + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,gtefield=Val1"` + } + + intTest := &IntTest{ + Val1: 1, + Val2: 5, + } + + errs = validate.Struct(intTest) + Equal(t, errs, nil) + + intTest = &IntTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validate.Struct(intTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "gtefield") + + err3 = validate.FieldWithValue(int(1), int(5), "gtefield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(int(5), int(1), "gtefield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "gtefield") + + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,gtefield=Val1"` + } + + uIntTest := &UIntTest{ + Val1: 1, + Val2: 5, + } + + errs = validate.Struct(uIntTest) + Equal(t, errs, nil) + + uIntTest = &UIntTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validate.Struct(uIntTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "gtefield") + + err3 = validate.FieldWithValue(uint(1), uint(5), "gtefield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(uint(5), uint(1), "gtefield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "gtefield") + + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,gtefield=Val1"` + } + + floatTest := &FloatTest{ + Val1: 1, + Val2: 5, + } + + errs = validate.Struct(floatTest) + Equal(t, errs, nil) + + floatTest = &FloatTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validate.Struct(floatTest).Flatten() + NotEqual(t, errs2, nil) + AssertMapFieldError(t, errs2, "Val2", "gtefield") + + err3 = validate.FieldWithValue(float32(1), float32(5), "gtefield") + Equal(t, err3, nil) + + err3 = validate.FieldWithValue(float32(5), float32(1), "gtefield") + NotEqual(t, err3, nil) + Equal(t, err3.Tag, "gtefield") + + PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "gtefield") }, "struct not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(5, "T", "gtefield") }, "Bad field type string") + PanicMatches(t, func() { validate.FieldWithValue(5, start, "gtefield") }, "Bad Top Level field type") + + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,gtefield=NonExistantField"` + } + + timeTest2 := &TimeTest2{ + Start: &start, + End: &end, + } + + PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +} + +func TestValidateByTagAndValue(t *testing.T) { + + val := "test" + field := "test" + err := validate.FieldWithValue(val, field, "required") + Equal(t, err, nil) + + fn := func(val interface{}, current interface{}, field interface{}, param string) bool { + + return current.(string) == field.(string) + } + + validate.AddFunction("isequaltestfunc", fn) + + err = validate.FieldWithValue(val, field, "isequaltestfunc") + Equal(t, err, nil) + + val = "unequal" + + err = validate.FieldWithValue(val, field, "isequaltestfunc") + NotEqual(t, err, nil) + Equal(t, err.Tag, "isequaltestfunc") +} + +func TestAddFunctions(t *testing.T) { + + fn := func(val interface{}, current interface{}, field interface{}, param string) bool { + + return true + } + + validate := New("validateme", BakedInValidators) + + err := validate.AddFunction("new", fn) + Equal(t, err, nil) + + err = validate.AddFunction("", fn) + NotEqual(t, err, nil) + + validate.AddFunction("new", nil) + NotEqual(t, err, nil) + + err = validate.AddFunction("new", fn) + Equal(t, err, nil) +} + +func TestChangeTag(t *testing.T) { + + validate := New("validateme", BakedInValidators) + validate.SetTag("val") + + type Test struct { + Name string `val:"len=4"` + } + s := &Test{ + Name: "TEST", + } + + err := validate.Struct(s) + Equal(t, err, nil) +} + +func TestUnexposedStruct(t *testing.T) { + + type Test struct { + Name string + unexposed struct { + A string `validate:"required"` + } + } + + s := &Test{ + Name: "TEST", + } + + err := validate.Struct(s) + Equal(t, err, nil) +} + +func TestBadParams(t *testing.T) { + + i := 1 + err := validate.Field(i, "-") + Equal(t, err, nil) + + PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") + PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") + + var ui uint = 1 + PanicMatches(t, func() { validate.Field(ui, "len=a") }, "strconv.ParseUint: parsing \"a\": invalid syntax") + + f := 1.23 + PanicMatches(t, func() { validate.Field(f, "len=a") }, "strconv.ParseFloat: parsing \"a\": invalid syntax") +} + +func TestLength(t *testing.T) { + + i := true + PanicMatches(t, func() { validate.Field(i, "len") }, "Bad field type bool") +} + +func TestIsGt(t *testing.T) { + + myMap := map[string]string{} + err := validate.Field(myMap, "gt=0") + NotEqual(t, err, nil) + + f := 1.23 + err = validate.Field(f, "gt=5") + NotEqual(t, err, nil) + + var ui uint = 5 + err = validate.Field(ui, "gt=10") + NotEqual(t, err, nil) + + i := true + PanicMatches(t, func() { validate.Field(i, "gt") }, "Bad field type bool") + + tm := time.Now().UTC() + tm = tm.Add(time.Hour * 24) + + err = validate.Field(tm, "gt") + Equal(t, err, nil) + + t2 := time.Now().UTC() + + err = validate.Field(t2, "gt") + NotEqual(t, err, nil) + Equal(t, err.Tag, "gt") + + type Test struct { + Now *time.Time `validate:"gt"` + } + s := &Test{ + Now: &tm, + } + + errs := validate.Struct(s) + Equal(t, errs, nil) + + s = &Test{ + Now: &t2, + } + + errs = validate.Struct(s) + NotEqual(t, errs, nil) +} + +func TestIsGte(t *testing.T) { + + i := true + PanicMatches(t, func() { validate.Field(i, "gte") }, "Bad field type bool") + + t1 := time.Now().UTC() + t1 = t1.Add(time.Hour * 24) + + err := validate.Field(t1, "gte") + Equal(t, err, nil) + + t2 := time.Now().UTC() + + err = validate.Field(t2, "gte") + NotEqual(t, err, nil) + Equal(t, err.Tag, "gte") + Equal(t, err.Type, reflect.TypeOf(time.Time{})) + + type Test struct { + Now *time.Time `validate:"gte"` + } + s := &Test{ + Now: &t1, + } + + errs := validate.Struct(s) + Equal(t, errs, nil) + + s = &Test{ + Now: &t2, + } + + errs = validate.Struct(s) + NotEqual(t, errs, nil) +} + +func TestIsLt(t *testing.T) { + + myMap := map[string]string{} + err := validate.Field(myMap, "lt=0") + NotEqual(t, err, nil) + + f := 1.23 + err = validate.Field(f, "lt=0") + NotEqual(t, err, nil) + + var ui uint = 5 + err = validate.Field(ui, "lt=0") + NotEqual(t, err, nil) + + i := true + PanicMatches(t, func() { validate.Field(i, "lt") }, "Bad field type bool") + + t1 := time.Now().UTC() + + err = validate.Field(t1, "lt") + Equal(t, err, nil) + + t2 := time.Now().UTC() + t2 = t2.Add(time.Hour * 24) + + err = validate.Field(t2, "lt") + NotEqual(t, err, nil) + Equal(t, err.Tag, "lt") + + type Test struct { + Now *time.Time `validate:"lt"` + } + + s := &Test{ + Now: &t1, + } + + errs := validate.Struct(s) + Equal(t, errs, nil) + + s = &Test{ + Now: &t2, + } + + errs = validate.Struct(s) + NotEqual(t, errs, nil) +} + +func TestIsLte(t *testing.T) { + + i := true + PanicMatches(t, func() { validate.Field(i, "lte") }, "Bad field type bool") + + t1 := time.Now().UTC() + + err := validate.Field(t1, "lte") + Equal(t, err, nil) + + t2 := time.Now().UTC() + t2 = t2.Add(time.Hour * 24) + + err = validate.Field(t2, "lte") + NotEqual(t, err, nil) + Equal(t, err.Tag, "lte") + + type Test struct { + Now *time.Time `validate:"lte"` + } + + s := &Test{ + Now: &t1, + } + + errs := validate.Struct(s) + Equal(t, errs, nil) + + s = &Test{ + Now: &t2, + } + + errs = validate.Struct(s) + NotEqual(t, errs, nil) +} + +func TestUrl(t *testing.T) { + + var tests = []struct { + param string + expected bool + }{ + {"http://foo.bar#com", true}, + {"http://foobar.com", true}, + {"https://foobar.com", true}, + {"foobar.com", false}, + {"http://foobar.coffee/", true}, + {"http://foobar.ä¸Â文网/", true}, + {"http://foobar.org/", true}, + {"http://foobar.org:8080/", true}, + {"ftp://foobar.ru/", true}, + {"http://user:pass@www.foobar.com/", true}, + {"http://127.0.0.1/", true}, + {"http://duckduckgo.com/?q=%2F", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com/?foo=bar#baz=qux", true}, + {"http://foobar.com?foo=bar", true}, + {"http://www.xn--froschgrn-x9a.net/", true}, + {"", false}, + {"xyz://foobar.com", true}, + {"invalid.", false}, + {".com", false}, + {"rtmp://foobar.com", true}, + {"http://www.foo_bar.com/", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com#baz=qux", true}, + {"http://foobar.com/t$-_.+!*\\'(),", true}, + {"http://www.foobar.com/~foobar", true}, + {"http://www.-foobar.com/", true}, + {"http://www.foo---bar.com/", true}, + {"mailto:someone@example.com", true}, + {"irc://irc.server.org/channel", true}, + {"irc://#channel@network", true}, + {"/abs/test/dir", false}, + {"./rel/test/dir", false}, + } + for i, test := range tests { + + err := validate.Field(test.param, "url") + + if test.expected == true { + if !IsEqual(t, err, nil) { + t.Fatalf("Index: %d URL failed Error: %s", i, err) + } + } else { + if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "url") { + t.Fatalf("Index: %d URL failed Error: %s", i, err) + } + } + } + + i := 1 + PanicMatches(t, func() { validate.Field(i, "url") }, "Bad field type int") +} + +func TestUri(t *testing.T) { + + var tests = []struct { + param string + expected bool + }{ + {"http://foo.bar#com", true}, + {"http://foobar.com", true}, + {"https://foobar.com", true}, + {"foobar.com", false}, + {"http://foobar.coffee/", true}, + {"http://foobar.ä¸Â文网/", true}, + {"http://foobar.org/", true}, + {"http://foobar.org:8080/", true}, + {"ftp://foobar.ru/", true}, + {"http://user:pass@www.foobar.com/", true}, + {"http://127.0.0.1/", true}, + {"http://duckduckgo.com/?q=%2F", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com/?foo=bar#baz=qux", true}, + {"http://foobar.com?foo=bar", true}, + {"http://www.xn--froschgrn-x9a.net/", true}, + {"", false}, + {"xyz://foobar.com", true}, + {"invalid.", false}, + {".com", false}, + {"rtmp://foobar.com", true}, + {"http://www.foo_bar.com/", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com#baz=qux", true}, + {"http://foobar.com/t$-_.+!*\\'(),", true}, + {"http://www.foobar.com/~foobar", true}, + {"http://www.-foobar.com/", true}, + {"http://www.foo---bar.com/", true}, + {"mailto:someone@example.com", true}, + {"irc://irc.server.org/channel", true}, + {"irc://#channel@network", true}, + {"/abs/test/dir", true}, + {"./rel/test/dir", false}, + } + for i, test := range tests { + + err := validate.Field(test.param, "uri") + + if test.expected == true { + if !IsEqual(t, err, nil) { + t.Fatalf("Index: %d URI failed Error: %s", i, err) + } + } else { + if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uri") { + t.Fatalf("Index: %d URI failed Error: %s", i, err) + } + } + } + + i := 1 + PanicMatches(t, func() { validate.Field(i, "uri") }, "Bad field type int") +} + +func TestOrTag(t *testing.T) { + s := "rgba(0,31,255,0.5)" + err := validate.Field(s, "rgb|rgba") + Equal(t, err, nil) + + s = "rgba(0,31,255,0.5)" + err = validate.Field(s, "rgb|rgba|len=18") + Equal(t, err, nil) + + s = "this ain't right" + err = validate.Field(s, "rgb|rgba") + NotEqual(t, err, nil) + Equal(t, err.Tag, "rgb|rgba") + + s = "this ain't right" + err = validate.Field(s, "rgb|rgba|len=10") + NotEqual(t, err, nil) + Equal(t, err.Tag, "rgb|rgba|len") + + s = "this is right" + err = validate.Field(s, "rgb|rgba|len=13") + Equal(t, err, nil) + + s = "" + err = validate.Field(s, "omitempty,rgb|rgba") + Equal(t, err, nil) +} + +func TestHsla(t *testing.T) { + + s := "hsla(360,100%,100%,1)" + err := validate.Field(s, "hsla") + Equal(t, err, nil) + + s = "hsla(360,100%,100%,0.5)" + err = validate.Field(s, "hsla") + Equal(t, err, nil) + + s = "hsla(0,0%,0%, 0)" + err = validate.Field(s, "hsla") + Equal(t, err, nil) + + s = "hsl(361,100%,50%,1)" + err = validate.Field(s, "hsla") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hsla") + + s = "hsl(361,100%,50%)" + err = validate.Field(s, "hsla") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hsla") + + s = "hsla(361,100%,50%)" + err = validate.Field(s, "hsla") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hsla") + + s = "hsla(360,101%,50%)" + err = validate.Field(s, "hsla") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hsla") + + s = "hsla(360,100%,101%)" + err = validate.Field(s, "hsla") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hsla") + + i := 1 + PanicMatches(t, func() { validate.Field(i, "hsla") }, "interface conversion: interface is int, not string") +} + +func TestHsl(t *testing.T) { + + s := "hsl(360,100%,50%)" + err := validate.Field(s, "hsl") + Equal(t, err, nil) + + s = "hsl(0,0%,0%)" + err = validate.Field(s, "hsl") + Equal(t, err, nil) + + s = "hsl(361,100%,50%)" + err = validate.Field(s, "hsl") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hsl") + + s = "hsl(361,101%,50%)" + err = validate.Field(s, "hsl") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hsl") + + s = "hsl(361,100%,101%)" + err = validate.Field(s, "hsl") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hsl") + + s = "hsl(-10,100%,100%)" + err = validate.Field(s, "hsl") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hsl") + + i := 1 + PanicMatches(t, func() { validate.Field(i, "hsl") }, "interface conversion: interface is int, not string") +} + +func TestRgba(t *testing.T) { + + s := "rgba(0,31,255,0.5)" + err := validate.Field(s, "rgba") + Equal(t, err, nil) + + s = "rgba(0,31,255,0.12)" + err = validate.Field(s, "rgba") + Equal(t, err, nil) + + s = "rgba( 0, 31, 255, 0.5)" + err = validate.Field(s, "rgba") + Equal(t, err, nil) + + s = "rgb(0, 31, 255)" + err = validate.Field(s, "rgba") + NotEqual(t, err, nil) + Equal(t, err.Tag, "rgba") + + s = "rgb(1,349,275,0.5)" + err = validate.Field(s, "rgba") + NotEqual(t, err, nil) + Equal(t, err.Tag, "rgba") + + s = "rgb(01,31,255,0.5)" + err = validate.Field(s, "rgba") + NotEqual(t, err, nil) + Equal(t, err.Tag, "rgba") + + i := 1 + PanicMatches(t, func() { validate.Field(i, "rgba") }, "interface conversion: interface is int, not string") +} + +func TestRgb(t *testing.T) { + + s := "rgb(0,31,255)" + err := validate.Field(s, "rgb") + Equal(t, err, nil) + + s = "rgb(0, 31, 255)" + err = validate.Field(s, "rgb") + Equal(t, err, nil) + + s = "rgb(1,349,275)" + err = validate.Field(s, "rgb") + NotEqual(t, err, nil) + Equal(t, err.Tag, "rgb") + + s = "rgb(01,31,255)" + err = validate.Field(s, "rgb") + NotEqual(t, err, nil) + Equal(t, err.Tag, "rgb") + + s = "rgba(0,31,255)" + err = validate.Field(s, "rgb") + NotEqual(t, err, nil) + Equal(t, err.Tag, "rgb") + + i := 1 + PanicMatches(t, func() { validate.Field(i, "rgb") }, "interface conversion: interface is int, not string") +} + +func TestEmail(t *testing.T) { + + s := "test@mail.com" + err := validate.Field(s, "email") + Equal(t, err, nil) + + s = "" + err = validate.Field(s, "email") + NotEqual(t, err, nil) + Equal(t, err.Tag, "email") + + s = "test@email" + err = validate.Field(s, "email") + NotEqual(t, err, nil) + Equal(t, err.Tag, "email") + + s = "test@email." + err = validate.Field(s, "email") + NotEqual(t, err, nil) + Equal(t, err.Tag, "email") + + s = "@email.com" + err = validate.Field(s, "email") + NotEqual(t, err, nil) + Equal(t, err.Tag, "email") + + i := true + PanicMatches(t, func() { validate.Field(i, "email") }, "interface conversion: interface is bool, not string") +} + +func TestHexColor(t *testing.T) { + + s := "#fff" + err := validate.Field(s, "hexcolor") + Equal(t, err, nil) + + s = "#c2c2c2" + err = validate.Field(s, "hexcolor") + Equal(t, err, nil) + + s = "fff" + err = validate.Field(s, "hexcolor") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hexcolor") + + s = "fffFF" + err = validate.Field(s, "hexcolor") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hexcolor") + + i := true + PanicMatches(t, func() { validate.Field(i, "hexcolor") }, "interface conversion: interface is bool, not string") +} + +func TestHexadecimal(t *testing.T) { + + s := "ff0044" + err := validate.Field(s, "hexadecimal") + Equal(t, err, nil) + + s = "abcdefg" + err = validate.Field(s, "hexadecimal") + NotEqual(t, err, nil) + Equal(t, err.Tag, "hexadecimal") + + i := true + PanicMatches(t, func() { validate.Field(i, "hexadecimal") }, "interface conversion: interface is bool, not string") +} + +func TestNumber(t *testing.T) { + + s := "1" + err := validate.Field(s, "number") + Equal(t, err, nil) + + s = "+1" + err = validate.Field(s, "number") + NotEqual(t, err, nil) + Equal(t, err.Tag, "number") + + s = "-1" + err = validate.Field(s, "number") + NotEqual(t, err, nil) + Equal(t, err.Tag, "number") + + s = "1.12" + err = validate.Field(s, "number") + NotEqual(t, err, nil) + Equal(t, err.Tag, "number") + + s = "+1.12" + err = validate.Field(s, "number") + NotEqual(t, err, nil) + Equal(t, err.Tag, "number") + + s = "-1.12" + err = validate.Field(s, "number") + NotEqual(t, err, nil) + Equal(t, err.Tag, "number") + + s = "1." + err = validate.Field(s, "number") + NotEqual(t, err, nil) + Equal(t, err.Tag, "number") + + s = "1.o" + err = validate.Field(s, "number") + NotEqual(t, err, nil) + Equal(t, err.Tag, "number") + + i := 1 + PanicMatches(t, func() { validate.Field(i, "number") }, "interface conversion: interface is int, not string") +} + +func TestNumeric(t *testing.T) { + + s := "1" + err := validate.Field(s, "numeric") + Equal(t, err, nil) + + s = "+1" + err = validate.Field(s, "numeric") + Equal(t, err, nil) + + s = "-1" + err = validate.Field(s, "numeric") + Equal(t, err, nil) + + s = "1.12" + err = validate.Field(s, "numeric") + Equal(t, err, nil) + + s = "+1.12" + err = validate.Field(s, "numeric") + Equal(t, err, nil) + + s = "-1.12" + err = validate.Field(s, "numeric") + Equal(t, err, nil) + + s = "1." + err = validate.Field(s, "numeric") + NotEqual(t, err, nil) + Equal(t, err.Tag, "numeric") + + s = "1.o" + err = validate.Field(s, "numeric") + NotEqual(t, err, nil) + Equal(t, err.Tag, "numeric") + + i := 1 + PanicMatches(t, func() { validate.Field(i, "numeric") }, "interface conversion: interface is int, not string") +} + +func TestAlphaNumeric(t *testing.T) { + + s := "abcd123" + err := validate.Field(s, "alphanum") + Equal(t, err, nil) + + s = "abc!23" + err = validate.Field(s, "alphanum") + NotEqual(t, err, nil) + Equal(t, err.Tag, "alphanum") + + PanicMatches(t, func() { validate.Field(1, "alphanum") }, "interface conversion: interface is int, not string") +} + +func TestAlpha(t *testing.T) { + + s := "abcd" + err := validate.Field(s, "alpha") + Equal(t, err, nil) + + s = "abc1" + err = validate.Field(s, "alpha") + NotEqual(t, err, nil) + Equal(t, err.Tag, "alpha") + + PanicMatches(t, func() { validate.Field(1, "alpha") }, "interface conversion: interface is int, not string") +} + +func TestFlattening(t *testing.T) { + + tSuccess := &TestString{ + Required: "Required", + Len: "length==10", + Min: "min=1", + Max: "1234567890", + MinMax: "12345", + Lt: "012345678", + Lte: "0123456789", + Gt: "01234567890", + Gte: "0123456789", + OmitEmpty: "", + Sub: &SubTest{ + Test: "1", + }, + SubIgnore: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "1", + }, + Iface: &Impl{ + F: "123", + }, + } + + err1 := validate.Struct(tSuccess).Flatten() + Equal(t, len(err1), 0) + + tFail := &TestString{ + Required: "", + Len: "", + Min: "", + Max: "12345678901", + MinMax: "", + OmitEmpty: "12345678901", + Sub: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "", + }, + Iface: &Impl{ + F: "12", + }, + } + + err2 := validate.Struct(tFail).Flatten() + + // Assert Top Level + NotEqual(t, err2, nil) + + // Assert Fields + AssertMapFieldError(t, err2, "Len", "len") + AssertMapFieldError(t, err2, "Gt", "gt") + AssertMapFieldError(t, err2, "Gte", "gte") + + // Assert Struct Field + AssertMapFieldError(t, err2, "Sub.Test", "required") + + // Assert Anonymous Struct Field + AssertMapFieldError(t, err2, "Anonymous.A", "required") + + // Assert Interface Field + AssertMapFieldError(t, err2, "Iface.F", "len") +} + +func TestStructStringValidation(t *testing.T) { + + validate.SetMaxStructPoolSize(11) + + tSuccess := &TestString{ + Required: "Required", + Len: "length==10", + Min: "min=1", + Max: "1234567890", + MinMax: "12345", + Lt: "012345678", + Lte: "0123456789", + Gt: "01234567890", + Gte: "0123456789", + OmitEmpty: "", + Sub: &SubTest{ + Test: "1", + }, + SubIgnore: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "1", + }, + Iface: &Impl{ + F: "123", + }, + } + + err := validate.Struct(tSuccess) + Equal(t, err, nil) + + tFail := &TestString{ + Required: "", + Len: "", + Min: "", + Max: "12345678901", + MinMax: "", + Lt: "0123456789", + Lte: "01234567890", + Gt: "1", + Gte: "1", + OmitEmpty: "12345678901", + Sub: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "", + }, + Iface: &Impl{ + F: "12", + }, + } + + err = validate.Struct(tFail) + + // Assert Top Level + NotEqual(t, err, nil) + Equal(t, err.Struct, "TestString") + Equal(t, len(err.Errors), 10) + Equal(t, len(err.StructErrors), 3) + + // Assert Fields + AssertFieldError(t, err, "Required", "required") + AssertFieldError(t, err, "Len", "len") + AssertFieldError(t, err, "Min", "min") + AssertFieldError(t, err, "Max", "max") + AssertFieldError(t, err, "MinMax", "min") + AssertFieldError(t, err, "Gt", "gt") + AssertFieldError(t, err, "Gte", "gte") + AssertFieldError(t, err, "OmitEmpty", "max") + + // Assert Anonymous embedded struct + AssertStruct(t, err, "Anonymous", "") + + // Assert SubTest embedded struct + val := AssertStruct(t, err, "Sub", "SubTest") + Equal(t, len(val.Errors), 1) + Equal(t, len(val.StructErrors), 0) + + AssertFieldError(t, val, "Test", "required") + + errors := err.Error() + NotEqual(t, errors, nil) +} + +func TestStructInt32Validation(t *testing.T) { + + tSuccess := &TestInt32{ + Required: 1, + Len: 10, + Min: 1, + Max: 10, + MinMax: 5, + Lt: 9, + Lte: 10, + Gt: 11, + Gte: 10, + OmitEmpty: 0, + } + + err := validate.Struct(tSuccess) + Equal(t, err, nil) + + tFail := &TestInt32{ + Required: 0, + Len: 11, + Min: -1, + Max: 11, + MinMax: -1, + Lt: 10, + Lte: 11, + Gt: 10, + Gte: 9, + OmitEmpty: 11, + } + + err = validate.Struct(tFail) + + // Assert Top Level + NotEqual(t, err, nil) + Equal(t, err.Struct, "TestInt32") + Equal(t, len(err.Errors), 10) + Equal(t, len(err.StructErrors), 0) + + // Assert Fields + AssertFieldError(t, err, "Required", "required") + AssertFieldError(t, err, "Len", "len") + AssertFieldError(t, err, "Min", "min") + AssertFieldError(t, err, "Max", "max") + AssertFieldError(t, err, "MinMax", "min") + AssertFieldError(t, err, "Lt", "lt") + AssertFieldError(t, err, "Lte", "lte") + AssertFieldError(t, err, "Gt", "gt") + AssertFieldError(t, err, "Gte", "gte") + AssertFieldError(t, err, "OmitEmpty", "max") +} + +func TestStructUint64Validation(t *testing.T) { + + tSuccess := &TestUint64{ + Required: 1, + Len: 10, + Min: 1, + Max: 10, + MinMax: 5, + OmitEmpty: 0, + } + + err := validate.Struct(tSuccess) + Equal(t, err, nil) + + tFail := &TestUint64{ + Required: 0, + Len: 11, + Min: 0, + Max: 11, + MinMax: 0, + OmitEmpty: 11, + } + + err = validate.Struct(tFail) + + // Assert Top Level + NotEqual(t, err, nil) + Equal(t, err.Struct, "TestUint64") + Equal(t, len(err.Errors), 6) + Equal(t, len(err.StructErrors), 0) + + // Assert Fields + AssertFieldError(t, err, "Required", "required") + AssertFieldError(t, err, "Len", "len") + AssertFieldError(t, err, "Min", "min") + AssertFieldError(t, err, "Max", "max") + AssertFieldError(t, err, "MinMax", "min") + AssertFieldError(t, err, "OmitEmpty", "max") +} + +func TestStructFloat64Validation(t *testing.T) { + + tSuccess := &TestFloat64{ + Required: 1, + Len: 10, + Min: 1, + Max: 10, + MinMax: 5, + OmitEmpty: 0, + } + + err := validate.Struct(tSuccess) + Equal(t, err, nil) + + tFail := &TestFloat64{ + Required: 0, + Len: 11, + Min: 0, + Max: 11, + MinMax: 0, + OmitEmpty: 11, + } + + err = validate.Struct(tFail) + + // Assert Top Level + NotEqual(t, err, nil) + Equal(t, err.Struct, "TestFloat64") + Equal(t, len(err.Errors), 6) + Equal(t, len(err.StructErrors), 0) + + // Assert Fields + AssertFieldError(t, err, "Required", "required") + AssertFieldError(t, err, "Len", "len") + AssertFieldError(t, err, "Min", "min") + AssertFieldError(t, err, "Max", "max") + AssertFieldError(t, err, "MinMax", "min") + AssertFieldError(t, err, "OmitEmpty", "max") +} + +func TestStructSliceValidation(t *testing.T) { + + tSuccess := &TestSlice{ + Required: []int{1}, + Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + Min: []int{1, 2}, + Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + MinMax: []int{1, 2, 3, 4, 5}, + OmitEmpty: []int{}, + } + + err := validate.Struct(tSuccess) + Equal(t, err, nil) + + tFail := &TestSlice{ + Required: []int{}, + Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, + Min: []int{}, + Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, + MinMax: []int{}, + OmitEmpty: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, + } + + err = validate.Struct(tFail) + + // Assert Top Level + NotEqual(t, err, nil) + Equal(t, err.Struct, "TestSlice") + Equal(t, len(err.Errors), 6) + Equal(t, len(err.StructErrors), 0) + + // Assert Fields + AssertFieldError(t, err, "Required", "required") + AssertFieldError(t, err, "Len", "len") + AssertFieldError(t, err, "Min", "min") + AssertFieldError(t, err, "Max", "max") + AssertFieldError(t, err, "MinMax", "min") + AssertFieldError(t, err, "OmitEmpty", "max") +} + +func TestInvalidStruct(t *testing.T) { + s := &SubTest{ + Test: "1", + } + + PanicMatches(t, func() { validate.Struct(s.Test) }, "interface passed for validation is not a struct") +} + +func TestInvalidField(t *testing.T) { + s := &SubTest{ + Test: "1", + } + + PanicMatches(t, func() { validate.Field(s, "required") }, "Invalid field passed to ValidateFieldWithTag") +} + +func TestInvalidTagField(t *testing.T) { + s := &SubTest{ + Test: "1", + } + + PanicMatches(t, func() { validate.Field(s.Test, "") }, fmt.Sprintf("Invalid validation tag on field %s", "")) +} + +func TestInvalidValidatorFunction(t *testing.T) { + s := &SubTest{ + Test: "1", + } + + PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, fmt.Sprintf("Undefined validation function on field %s", "")) +} + +func BenchmarkValidateField(b *testing.B) { + for n := 0; n < b.N; n++ { + validate.Field("1", "len=1") + } +} + +func BenchmarkValidateStructSimple(b *testing.B) { + + type Foo struct { + StringValue string `validate:"min=5,max=10"` + IntValue int `validate:"min=5,max=10"` + } + + validFoo := &Foo{StringValue: "Foobar", IntValue: 7} + invalidFoo := &Foo{StringValue: "Fo", IntValue: 3} + + for n := 0; n < b.N; n++ { + validate.Struct(validFoo) + validate.Struct(invalidFoo) + } +} + +// func BenchmarkTemplateParallelSimple(b *testing.B) { + +// type Foo struct { +// StringValue string `validate:"min=5,max=10"` +// IntValue int `validate:"min=5,max=10"` +// } + +// validFoo := &Foo{StringValue: "Foobar", IntValue: 7} +// invalidFoo := &Foo{StringValue: "Fo", IntValue: 3} + +// b.RunParallel(func(pb *testing.PB) { +// for pb.Next() { +// validate.Struct(validFoo) +// validate.Struct(invalidFoo) +// } +// }) +// } + +func BenchmarkValidateStructLarge(b *testing.B) { + + tFail := &TestString{ + Required: "", + Len: "", + Min: "", + Max: "12345678901", + MinMax: "", + Lt: "0123456789", + Lte: "01234567890", + Gt: "1", + Gte: "1", + OmitEmpty: "12345678901", + Sub: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "", + }, + Iface: &Impl{ + F: "12", + }, + } + + tSuccess := &TestString{ + Required: "Required", + Len: "length==10", + Min: "min=1", + Max: "1234567890", + MinMax: "12345", + Lt: "012345678", + Lte: "0123456789", + Gt: "01234567890", + Gte: "0123456789", + OmitEmpty: "", + Sub: &SubTest{ + Test: "1", + }, + SubIgnore: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "1", + }, + Iface: &Impl{ + F: "123", + }, + } + + for n := 0; n < b.N; n++ { + validate.Struct(tSuccess) + validate.Struct(tFail) + } +} + +// func BenchmarkTemplateParallelLarge(b *testing.B) { + +// tFail := &TestString{ +// Required: "", +// Len: "", +// Min: "", +// Max: "12345678901", +// MinMax: "", +// Lt: "0123456789", +// Lte: "01234567890", +// Gt: "1", +// Gte: "1", +// OmitEmpty: "12345678901", +// Sub: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "", +// }, +// Iface: &Impl{ +// F: "12", +// }, +// } + +// tSuccess := &TestString{ +// Required: "Required", +// Len: "length==10", +// Min: "min=1", +// Max: "1234567890", +// MinMax: "12345", +// Lt: "012345678", +// Lte: "0123456789", +// Gt: "01234567890", +// Gte: "0123456789", +// OmitEmpty: "", +// Sub: &SubTest{ +// Test: "1", +// }, +// SubIgnore: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "1", +// }, +// Iface: &Impl{ +// F: "123", +// }, +// } + +// b.RunParallel(func(pb *testing.PB) { +// for pb.Next() { +// validate.Struct(tSuccess) +// validate.Struct(tFail) +// } +// }) +// } diff --git a/Godeps/_workspace/src/gopkg.in/fatih/set.v0/.travis.yml b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/.travis.yml new file mode 100644 index 00000000..b05e4c53 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: 1.2 + diff --git a/Godeps/_workspace/src/gopkg.in/fatih/set.v0/LICENSE.md b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/LICENSE.md new file mode 100644 index 00000000..25fdaf63 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Fatih Arslan + +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/Godeps/_workspace/src/gopkg.in/fatih/set.v0/README.md b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/README.md new file mode 100644 index 00000000..23afdd98 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/README.md @@ -0,0 +1,245 @@ +# Set [](https://godoc.org/gopkg.in/fatih/set.v0) [](https://travis-ci.org/fatih/set) + +Set is a basic and simple, hash-based, **Set** data structure implementation +in Go (Golang). + +Set provides both threadsafe and non-threadsafe implementations of a generic +set data structure. The thread safety encompasses all operations on one set. +Operations on multiple sets are consistent in that the elements of each set +used was valid at exactly one point in time between the start and the end of +the operation. Because it's thread safe, you can use it concurrently with your +goroutines. + +For usage see examples below or click on the godoc badge. + +## Install and Usage + +Install the package with: + +```bash +go get gopkg.in/fatih/set.v0 +``` + +Import it with: + +```go +import "gopkg.in/fatih/set.v0" +``` + +and use `set` as the package name inside the code. + +## Examples + +#### Initialization of a new Set + +```go + +// create a set with zero items +s := set.New() +s := set.NewNonTS() // non thread-safe version + +// ... or with some initial values +s := set.New("istanbul", "frankfurt", 30.123, "san francisco", 1234) +s := set.NewNonTS("kenya", "ethiopia", "sumatra") + +``` + +#### Basic Operations + +```go +// add items +s.Add("istanbul") +s.Add("istanbul") // nothing happens if you add duplicate item + +// add multiple items +s.Add("ankara", "san francisco", 3.14) + +// remove item +s.Remove("frankfurt") +s.Remove("frankfurt") // nothing happes if you remove a nonexisting item + +// remove multiple items +s.Remove("barcelona", 3.14, "ankara") + +// removes an arbitary item and return it +item := s.Pop() + +// create a new copy +other := s.Copy() + +// remove all items +s.Clear() + +// number of items in the set +len := s.Size() + +// return a list of items +items := s.List() + +// string representation of set +fmt.Printf("set is %s", s.String()) + +``` + +#### Check Operations + +```go +// check for set emptiness, returns true if set is empty +s.IsEmpty() + +// check for a single item exist +s.Has("istanbul") + +// ... or for multiple items. This will return true if all of the items exist. +s.Has("istanbul", "san francisco", 3.14) + +// create two sets for the following checks... +s := s.New("1", "2", "3", "4", "5") +t := s.New("1", "2", "3") + + +// check if they are the same +if !s.IsEqual(t) { + fmt.Println("s is not equal to t") +} + +// if s contains all elements of t +if s.IsSubset(t) { + fmt.Println("t is a subset of s") +} + +// ... or if s is a superset of t +if t.IsSuperset(s) { + fmt.Println("s is a superset of t") +} + + +``` + +#### Set Operations + + +```go +// let us initialize two sets with some values +a := set.New("ankara", "berlin", "san francisco") +b := set.New("frankfurt", "berlin") + +// creates a new set with the items in a and b combined. +// [frankfurt, berlin, ankara, san francisco] +c := set.Union(a, b) + +// contains items which is in both a and b +// [berlin] +c := set.Intersection(a, b) + +// contains items which are in a but not in b +// [ankara, san francisco] +c := set.Difference(a, b) + +// contains items which are in one of either, but not in both. +// [frankfurt, ankara, san francisco] +c := set.SymmetricDifference(a, b) + +``` + +```go +// like Union but saves the result back into a. +a.Merge(b) + +// removes the set items which are in b from a and saves the result back into a. +a.Separate(b) + +``` + +#### Multiple Set Operations + +```go +a := set.New("1", "3", "4", "5") +b := set.New("2", "3", "4", "5") +c := set.New("4", "5", "6", "7") + +// creates a new set with items in a, b and c +// [1 2 3 4 5 6 7] +u := set.Union(a, b, c) + +// creates a new set with items in a but not in b and c +// [1] +u := set.Difference(a, b, c) + +// creates a new set with items that are common to a, b and c +// [5] +u := set.Intersection(a, b, c) +``` + +#### Helper methods + +The Slice functions below are a convenient way to extract or convert your Set data +into basic data types. + + +```go +// create a set of mixed types +s := set.New("ankara", "5", "8", "san francisco", 13, 21) + + +// convert s into a slice of strings (type is []string) +// [ankara 5 8 san francisco] +t := set.StringSlice(s) + + +// u contains a slice of ints (type is []int) +// [13, 21] +u := set.IntSlice(s) + +``` + +#### Concurrent safe usage + +Below is an example of a concurrent way that uses set. We call ten functions +concurrently and wait until they are finished. It basically creates a new +string for each goroutine and adds it to our set. + +```go +package main + +import ( + "fmt" + "github.com/fatih/set" + "strconv" + "sync" +) + +func main() { + var wg sync.WaitGroup // this is just for waiting until all goroutines finish + + // Initialize our thread safe Set + s := set.New() + + // Add items concurrently (item1, item2, and so on) + for i := 0; i < 10; i++ { + wg.Add(1) + go func(i int) { + item := "item" + strconv.Itoa(i) + fmt.Println("adding", item) + s.Add(item) + wg.Done() + }(i) + } + + // Wait until all concurrent calls finished and print our set + wg.Wait() + fmt.Println(s) +} +``` + +## Credits + + * [Fatih Arslan](https://github.com/fatih) + * [Arne Hormann](https://github.com/arnehormann) + * [Sam Boyer](https://github.com/sdboyer) + * [Ralph Loizzo](https://github.com/friartech) + +## License + +The MIT License (MIT) - see LICENSE.md for more details + diff --git a/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set.go b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set.go new file mode 100644 index 00000000..ac0240ce --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set.go @@ -0,0 +1,121 @@ +// Package set provides both threadsafe and non-threadsafe implementations of +// a generic set data structure. In the threadsafe set, safety encompasses all +// operations on one set. Operations on multiple sets are consistent in that +// the elements of each set used was valid at exactly one point in time +// between the start and the end of the operation. +package set + +// Interface is describing a Set. Sets are an unordered, unique list of values. +type Interface interface { + New(items ...interface{}) Interface + Add(items ...interface{}) + Remove(items ...interface{}) + Pop() interface{} + Has(items ...interface{}) bool + Size() int + Clear() + IsEmpty() bool + IsEqual(s Interface) bool + IsSubset(s Interface) bool + IsSuperset(s Interface) bool + Each(func(interface{}) bool) + String() string + List() []interface{} + Copy() Interface + Merge(s Interface) + Separate(s Interface) +} + +// helpful to not write everywhere struct{}{} +var keyExists = struct{}{} + +// Union is the merger of multiple sets. It returns a new set with all the +// elements present in all the sets that are passed. +// +// The dynamic type of the returned set is determined by the first passed set's +// implementation of the New() method. +func Union(set1, set2 Interface, sets ...Interface) Interface { + u := set1.Copy() + set2.Each(func(item interface{}) bool { + u.Add(item) + return true + }) + for _, set := range sets { + set.Each(func(item interface{}) bool { + u.Add(item) + return true + }) + } + + return u +} + +// Difference returns a new set which contains items which are in in the first +// set but not in the others. Unlike the Difference() method you can use this +// function separately with multiple sets. +func Difference(set1, set2 Interface, sets ...Interface) Interface { + s := set1.Copy() + s.Separate(set2) + for _, set := range sets { + s.Separate(set) // seperate is thread safe + } + return s +} + +// Intersection returns a new set which contains items that only exist in all given sets. +func Intersection(set1, set2 Interface, sets ...Interface) Interface { + all := Union(set1, set2, sets...) + result := Union(set1, set2, sets...) + + all.Each(func(item interface{}) bool { + if !set1.Has(item) || !set2.Has(item) { + result.Remove(item) + } + + for _, set := range sets { + if !set.Has(item) { + result.Remove(item) + } + } + return true + }) + return result +} + +// SymmetricDifference returns a new set which s is the difference of items which are in +// one of either, but not in both. +func SymmetricDifference(s Interface, t Interface) Interface { + u := Difference(s, t) + v := Difference(t, s) + return Union(u, v) +} + +// StringSlice is a helper function that returns a slice of strings of s. If +// the set contains mixed types of items only items of type string are returned. +func StringSlice(s Interface) []string { + slice := make([]string, 0) + for _, item := range s.List() { + v, ok := item.(string) + if !ok { + continue + } + + slice = append(slice, v) + } + return slice +} + +// IntSlice is a helper function that returns a slice of ints of s. If +// the set contains mixed types of items only items of type int are returned. +func IntSlice(s Interface) []int { + slice := make([]int, 0) + for _, item := range s.List() { + v, ok := item.(int) + if !ok { + continue + } + + slice = append(slice, v) + } + return slice +} diff --git a/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_nots.go b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_nots.go new file mode 100644 index 00000000..ec1ab228 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_nots.go @@ -0,0 +1,195 @@ +package set + +import ( + "fmt" + "strings" +) + +// Provides a common set baseline for both threadsafe and non-ts Sets. +type set struct { + m map[interface{}]struct{} // struct{} doesn't take up space +} + +// SetNonTS defines a non-thread safe set data structure. +type SetNonTS struct { + set +} + +// NewNonTS creates and initialize a new non-threadsafe Set. +// It accepts a variable number of arguments to populate the initial set. +// If nothing is passed a SetNonTS with zero size is created. +func NewNonTS(items ...interface{}) *SetNonTS { + s := &SetNonTS{} + s.m = make(map[interface{}]struct{}) + + // Ensure interface compliance + var _ Interface = s + + s.Add(items...) + return s +} + +// New creates and initalizes a new Set interface. It accepts a variable +// number of arguments to populate the initial set. If nothing is passed a +// zero size Set based on the struct is created. +func (s *set) New(items ...interface{}) Interface { + return NewNonTS(items...) +} + +// Add includes the specified items (one or more) to the set. The underlying +// Set s is modified. If passed nothing it silently returns. +func (s *set) Add(items ...interface{}) { + if len(items) == 0 { + return + } + + for _, item := range items { + s.m[item] = keyExists + } +} + +// Remove deletes the specified items from the set. The underlying Set s is +// modified. If passed nothing it silently returns. +func (s *set) Remove(items ...interface{}) { + if len(items) == 0 { + return + } + + for _, item := range items { + delete(s.m, item) + } +} + +// Pop deletes and return an item from the set. The underlying Set s is +// modified. If set is empty, nil is returned. +func (s *set) Pop() interface{} { + for item := range s.m { + delete(s.m, item) + return item + } + return nil +} + +// Has looks for the existence of items passed. It returns false if nothing is +// passed. For multiple items it returns true only if all of the items exist. +func (s *set) Has(items ...interface{}) bool { + // assume checked for empty item, which not exist + if len(items) == 0 { + return false + } + + has := true + for _, item := range items { + if _, has = s.m[item]; !has { + break + } + } + return has +} + +// Size returns the number of items in a set. +func (s *set) Size() int { + return len(s.m) +} + +// Clear removes all items from the set. +func (s *set) Clear() { + s.m = make(map[interface{}]struct{}) +} + +// IsEmpty reports whether the Set is empty. +func (s *set) IsEmpty() bool { + return s.Size() == 0 +} + +// IsEqual test whether s and t are the same in size and have the same items. +func (s *set) IsEqual(t Interface) bool { + // Force locking only if given set is threadsafe. + if conv, ok := t.(*Set); ok { + conv.l.RLock() + defer conv.l.RUnlock() + } + + // return false if they are no the same size + if sameSize := len(s.m) == t.Size(); !sameSize { + return false + } + + equal := true + t.Each(func(item interface{}) bool { + _, equal = s.m[item] + return equal // if false, Each() will end + }) + + return equal +} + +// IsSubset tests whether t is a subset of s. +func (s *set) IsSubset(t Interface) (subset bool) { + subset = true + + t.Each(func(item interface{}) bool { + _, subset = s.m[item] + return subset + }) + + return +} + +// IsSuperset tests whether t is a superset of s. +func (s *set) IsSuperset(t Interface) bool { + return t.IsSubset(s) +} + +// Each traverses the items in the Set, calling the provided function for each +// set member. Traversal will continue until all items in the Set have been +// visited, or if the closure returns false. +func (s *set) Each(f func(item interface{}) bool) { + for item := range s.m { + if !f(item) { + break + } + } +} + +// String returns a string representation of s +func (s *set) String() string { + t := make([]string, 0, len(s.List())) + for _, item := range s.List() { + t = append(t, fmt.Sprintf("%v", item)) + } + + return fmt.Sprintf("[%s]", strings.Join(t, ", ")) +} + +// List returns a slice of all items. There is also StringSlice() and +// IntSlice() methods for returning slices of type string or int. +func (s *set) List() []interface{} { + list := make([]interface{}, 0, len(s.m)) + + for item := range s.m { + list = append(list, item) + } + + return list +} + +// Copy returns a new Set with a copy of s. +func (s *set) Copy() Interface { + return NewNonTS(s.List()...) +} + +// Merge is like Union, however it modifies the current set it's applied on +// with the given t set. +func (s *set) Merge(t Interface) { + t.Each(func(item interface{}) bool { + s.m[item] = keyExists + return true + }) +} + +// it's not the opposite of Merge. +// Separate removes the set items containing in t from set s. Please aware that +func (s *set) Separate(t Interface) { + s.Remove(t.List()...) +} diff --git a/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_nots_test.go b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_nots_test.go new file mode 100644 index 00000000..fd599699 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_nots_test.go @@ -0,0 +1,282 @@ +package set + +import ( + "reflect" + "strings" + "testing" +) + +func TestSetNonTS_NewNonTS_parameters(t *testing.T) { + s := NewNonTS("string", "another_string", 1, 3.14) + + if s.Size() != 4 { + t.Error("NewNonTS: calling with parameters should create a set with size of four") + } +} + +func TestSetNonTS_Add(t *testing.T) { + s := NewNonTS() + s.Add(1) + s.Add(2) + s.Add(2) // duplicate + s.Add("fatih") + s.Add("zeynep") + s.Add("zeynep") // another duplicate + + if s.Size() != 4 { + t.Error("Add: items are not unique. The set size should be four") + } + + if !s.Has(1, 2, "fatih", "zeynep") { + t.Error("Add: added items are not availabile in the set.") + } +} + +func TestSetNonTS_Add_multiple(t *testing.T) { + s := NewNonTS() + s.Add("ankara", "san francisco", 3.14) + + if s.Size() != 3 { + t.Error("Add: items are not unique. The set size should be three") + } + + if !s.Has("ankara", "san francisco", 3.14) { + t.Error("Add: added items are not availabile in the set.") + } +} + +func TestSetNonTS_Remove(t *testing.T) { + s := NewNonTS() + s.Add(1) + s.Add(2) + s.Add("fatih") + + s.Remove(1) + if s.Size() != 2 { + t.Error("Remove: set size should be two after removing") + } + + s.Remove(1) + if s.Size() != 2 { + t.Error("Remove: set size should be not change after trying to remove a non-existing item") + } + + s.Remove(2) + s.Remove("fatih") + if s.Size() != 0 { + t.Error("Remove: set size should be zero") + } + + s.Remove("fatih") // try to remove something from a zero length set +} + +func TestSetNonTS_Remove_multiple(t *testing.T) { + s := NewNonTS() + s.Add("ankara", "san francisco", 3.14, "istanbul") + s.Remove("ankara", "san francisco", 3.14) + + if s.Size() != 1 { + t.Error("Remove: items are not unique. The set size should be four") + } + + if !s.Has("istanbul") { + t.Error("Add: added items are not availabile in the set.") + } +} + +func TestSetNonTS_Pop(t *testing.T) { + s := NewNonTS() + s.Add(1) + s.Add(2) + s.Add("fatih") + + a := s.Pop() + if s.Size() != 2 { + t.Error("Pop: set size should be two after popping out") + } + + if s.Has(a) { + t.Error("Pop: returned item should not exist") + } + + s.Pop() + s.Pop() + b := s.Pop() + if b != nil { + t.Error("Pop: should return nil because set is empty") + } + + s.Pop() // try to remove something from a zero length set +} + +func TestSetNonTS_Has(t *testing.T) { + s := NewNonTS("1", "2", "3", "4") + + if !s.Has("1") { + t.Error("Has: the item 1 exist, but 'Has' is returning false") + } + + if !s.Has("1", "2", "3", "4") { + t.Error("Has: the items all exist, but 'Has' is returning false") + } +} + +func TestSetNonTS_Clear(t *testing.T) { + s := NewNonTS() + s.Add(1) + s.Add("istanbul") + s.Add("san francisco") + + s.Clear() + if s.Size() != 0 { + t.Error("Clear: set size should be zero") + } +} + +func TestSetNonTS_IsEmpty(t *testing.T) { + s := NewNonTS() + + empty := s.IsEmpty() + if !empty { + t.Error("IsEmpty: set is empty, it should be true") + } + + s.Add(2) + s.Add(3) + notEmpty := s.IsEmpty() + + if notEmpty { + t.Error("IsEmpty: set is filled, it should be false") + } +} + +func TestSetNonTS_IsEqual(t *testing.T) { + s := NewNonTS("1", "2", "3") + u := NewNonTS("1", "2", "3") + + ok := s.IsEqual(u) + if !ok { + t.Error("IsEqual: set s and t are equal. However it returns false") + } + + // same size, different content + a := NewNonTS("1", "2", "3") + b := NewNonTS("4", "5", "6") + + ok = a.IsEqual(b) + if ok { + t.Error("IsEqual: set a and b are now equal (1). However it returns true") + } + + // different size, similar content + a = NewNonTS("1", "2", "3") + b = NewNonTS("1", "2", "3", "4") + + ok = a.IsEqual(b) + if ok { + t.Error("IsEqual: set s and t are now equal (2). However it returns true") + } + +} + +func TestSetNonTS_IsSubset(t *testing.T) { + s := NewNonTS("1", "2", "3", "4") + u := NewNonTS("1", "2", "3") + + ok := s.IsSubset(u) + if !ok { + t.Error("IsSubset: u is a subset of s. However it returns false") + } + + ok = u.IsSubset(s) + if ok { + t.Error("IsSubset: s is not a subset of u. However it returns true") + } + +} + +func TestSetNonTS_IsSuperset(t *testing.T) { + s := NewNonTS("1", "2", "3", "4") + u := NewNonTS("1", "2", "3") + + ok := u.IsSuperset(s) + if !ok { + t.Error("IsSuperset: s is a superset of u. However it returns false") + } + + ok = s.IsSuperset(u) + if ok { + t.Error("IsSuperset: u is not a superset of u. However it returns true") + } + +} + +func TestSetNonTS_String(t *testing.T) { + s := NewNonTS() + if s.String() != "[]" { + t.Errorf("String: output is not what is excepted '%s'", s.String()) + } + + s.Add("1", "2", "3", "4") + if !strings.HasPrefix(s.String(), "[") { + t.Error("String: output should begin with a square bracket") + } + + if !strings.HasSuffix(s.String(), "]") { + t.Error("String: output should end with a square bracket") + } + +} + +func TestSetNonTS_List(t *testing.T) { + s := NewNonTS("1", "2", "3", "4") + + // this returns a slice of interface{} + if len(s.List()) != 4 { + t.Error("List: slice size should be four.") + } + + for _, item := range s.List() { + r := reflect.TypeOf(item) + if r.Kind().String() != "string" { + t.Error("List: slice item should be a string") + } + } +} + +func TestSetNonTS_Copy(t *testing.T) { + s := NewNonTS("1", "2", "3", "4") + r := s.Copy() + + if !s.IsEqual(r) { + t.Error("Copy: set s and r are not equal") + } +} + +func TestSetNonTS_Merge(t *testing.T) { + s := NewNonTS("1", "2", "3") + r := NewNonTS("3", "4", "5") + s.Merge(r) + + if s.Size() != 5 { + t.Error("Merge: the set doesn't have all items in it.") + } + + if !s.Has("1", "2", "3", "4", "5") { + t.Error("Merge: merged items are not availabile in the set.") + } +} + +func TestSetNonTS_Separate(t *testing.T) { + s := NewNonTS("1", "2", "3") + r := NewNonTS("3", "5") + s.Separate(r) + + if s.Size() != 2 { + t.Error("Separate: the set doesn't have all items in it.") + } + + if !s.Has("1", "2") { + t.Error("Separate: items after separation are not availabile in the set.") + } +} diff --git a/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_test.go b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_test.go new file mode 100644 index 00000000..83dd5806 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_test.go @@ -0,0 +1,188 @@ +package set + +import ( + "reflect" + "testing" +) + +func Test_Union(t *testing.T) { + s := New("1", "2", "3") + r := New("3", "4", "5") + x := NewNonTS("5", "6", "7") + + u := Union(s, r, x) + if settype := reflect.TypeOf(u).String(); settype != "*set.Set" { + t.Error("Union should derive its set type from the first passed set, got", settype) + } + if u.Size() != 7 { + t.Error("Union: the merged set doesn't have all items in it.") + } + + if !u.Has("1", "2", "3", "4", "5", "6", "7") { + t.Error("Union: merged items are not availabile in the set.") + } + + z := Union(x, r) + if z.Size() != 5 { + t.Error("Union: Union of 2 sets doesn't have the proper number of items.") + } + if settype := reflect.TypeOf(z).String(); settype != "*set.SetNonTS" { + t.Error("Union should derive its set type from the first passed set, got", settype) + } + +} + +func Test_Difference(t *testing.T) { + s := New("1", "2", "3") + r := New("3", "4", "5") + x := New("5", "6", "7") + u := Difference(s, r, x) + + if u.Size() != 2 { + t.Error("Difference: the set doesn't have all items in it.") + } + + if !u.Has("1", "2") { + t.Error("Difference: items are not availabile in the set.") + } + + y := Difference(r, r) + if y.Size() != 0 { + t.Error("Difference: size should be zero") + } + +} + +func Test_Intersection(t *testing.T) { + s1 := New("1", "3", "4", "5") + s2 := New("2", "3", "5", "6") + s3 := New("4", "5", "6", "7") + u := Intersection(s1, s2, s3) + + if u.Size() != 1 { + t.Error("Intersection: the set doesn't have all items in it.") + } + + if !u.Has("5") { + t.Error("Intersection: items after intersection are not availabile in the set.") + } +} + +func Test_SymmetricDifference(t *testing.T) { + s := New("1", "2", "3") + r := New("3", "4", "5") + u := SymmetricDifference(s, r) + + if u.Size() != 4 { + t.Error("SymmetricDifference: the set doesn't have all items in it.") + } + + if !u.Has("1", "2", "4", "5") { + t.Error("SymmetricDifference: items are not availabile in the set.") + } +} + +func Test_StringSlice(t *testing.T) { + s := New("san francisco", "istanbul", 3.14, 1321, "ankara") + u := StringSlice(s) + + if len(u) != 3 { + t.Error("StringSlice: slice should only have three items") + } + + for _, item := range u { + r := reflect.TypeOf(item) + if r.Kind().String() != "string" { + t.Error("StringSlice: slice item should be a string") + } + } +} + +func Test_IntSlice(t *testing.T) { + s := New("san francisco", "istanbul", 3.14, 1321, "ankara", 8876) + u := IntSlice(s) + + if len(u) != 2 { + t.Error("IntSlice: slice should only have two items") + } + + for _, item := range u { + r := reflect.TypeOf(item) + if r.Kind().String() != "int" { + t.Error("Intslice: slice item should be a int") + } + } +} + +func BenchmarkSetEquality(b *testing.B) { + s := New() + u := New() + + for i := 0; i < b.N; i++ { + s.Add(i) + u.Add(i) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + s.IsEqual(u) + } +} + +func BenchmarkSubset(b *testing.B) { + s := New() + u := New() + + for i := 0; i < b.N; i++ { + s.Add(i) + u.Add(i) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + s.IsSubset(u) + } +} + +func benchmarkIntersection(b *testing.B, numberOfItems int) { + s1 := New() + s2 := New() + + for i := 0; i < numberOfItems/2; i++ { + s1.Add(i) + } + for i := 0; i < numberOfItems; i++ { + s2.Add(i) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + Intersection(s1, s2) + } +} + +func BenchmarkIntersection10(b *testing.B) { + benchmarkIntersection(b, 10) +} + +func BenchmarkIntersection100(b *testing.B) { + benchmarkIntersection(b, 100) +} + +func BenchmarkIntersection1000(b *testing.B) { + benchmarkIntersection(b, 1000) +} + +func BenchmarkIntersection10000(b *testing.B) { + benchmarkIntersection(b, 10000) +} + +func BenchmarkIntersection100000(b *testing.B) { + benchmarkIntersection(b, 100000) +} + +func BenchmarkIntersection1000000(b *testing.B) { + benchmarkIntersection(b, 1000000) +} diff --git a/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_ts.go b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_ts.go new file mode 100644 index 00000000..50f53256 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_ts.go @@ -0,0 +1,200 @@ +package set + +import ( + "sync" +) + +// Set defines a thread safe set data structure. +type Set struct { + set + l sync.RWMutex // we name it because we don't want to expose it +} + +// New creates and initialize a new Set. It's accept a variable number of +// arguments to populate the initial set. If nothing passed a Set with zero +// size is created. +func New(items ...interface{}) *Set { + s := &Set{} + s.m = make(map[interface{}]struct{}) + + // Ensure interface compliance + var _ Interface = s + + s.Add(items...) + return s +} + +// New creates and initalizes a new Set interface. It accepts a variable +// number of arguments to populate the initial set. If nothing is passed a +// zero size Set based on the struct is created. +func (s *Set) New(items ...interface{}) Interface { + return New(items...) +} + +// Add includes the specified items (one or more) to the set. The underlying +// Set s is modified. If passed nothing it silently returns. +func (s *Set) Add(items ...interface{}) { + if len(items) == 0 { + return + } + + s.l.Lock() + defer s.l.Unlock() + + for _, item := range items { + s.m[item] = keyExists + } +} + +// Remove deletes the specified items from the set. The underlying Set s is +// modified. If passed nothing it silently returns. +func (s *Set) Remove(items ...interface{}) { + if len(items) == 0 { + return + } + + s.l.Lock() + defer s.l.Unlock() + + for _, item := range items { + delete(s.m, item) + } +} + +// Pop deletes and return an item from the set. The underlying Set s is +// modified. If set is empty, nil is returned. +func (s *Set) Pop() interface{} { + s.l.RLock() + for item := range s.m { + s.l.RUnlock() + s.l.Lock() + delete(s.m, item) + s.l.Unlock() + return item + } + s.l.RUnlock() + return nil +} + +// Has looks for the existence of items passed. It returns false if nothing is +// passed. For multiple items it returns true only if all of the items exist. +func (s *Set) Has(items ...interface{}) bool { + // assume checked for empty item, which not exist + if len(items) == 0 { + return false + } + + s.l.RLock() + defer s.l.RUnlock() + + has := true + for _, item := range items { + if _, has = s.m[item]; !has { + break + } + } + return has +} + +// Size returns the number of items in a set. +func (s *Set) Size() int { + s.l.RLock() + defer s.l.RUnlock() + + l := len(s.m) + return l +} + +// Clear removes all items from the set. +func (s *Set) Clear() { + s.l.Lock() + defer s.l.Unlock() + + s.m = make(map[interface{}]struct{}) +} + +// IsEqual test whether s and t are the same in size and have the same items. +func (s *Set) IsEqual(t Interface) bool { + s.l.RLock() + defer s.l.RUnlock() + + // Force locking only if given set is threadsafe. + if conv, ok := t.(*Set); ok { + conv.l.RLock() + defer conv.l.RUnlock() + } + + // return false if they are no the same size + if sameSize := len(s.m) == t.Size(); !sameSize { + return false + } + + equal := true + t.Each(func(item interface{}) bool { + _, equal = s.m[item] + return equal // if false, Each() will end + }) + + return equal +} + +// IsSubset tests whether t is a subset of s. +func (s *Set) IsSubset(t Interface) (subset bool) { + s.l.RLock() + defer s.l.RUnlock() + + subset = true + + t.Each(func(item interface{}) bool { + _, subset = s.m[item] + return subset + }) + + return +} + +// Each traverses the items in the Set, calling the provided function for each +// set member. Traversal will continue until all items in the Set have been +// visited, or if the closure returns false. +func (s *Set) Each(f func(item interface{}) bool) { + s.l.RLock() + defer s.l.RUnlock() + + for item := range s.m { + if !f(item) { + break + } + } +} + +// List returns a slice of all items. There is also StringSlice() and +// IntSlice() methods for returning slices of type string or int. +func (s *Set) List() []interface{} { + s.l.RLock() + defer s.l.RUnlock() + + list := make([]interface{}, 0, len(s.m)) + + for item := range s.m { + list = append(list, item) + } + + return list +} + +// Copy returns a new Set with a copy of s. +func (s *Set) Copy() Interface { + return New(s.List()...) +} + +// Merge is like Union, however it modifies the current set it's applied on +// with the given t set. +func (s *Set) Merge(t Interface) { + s.l.Lock() + defer s.l.Unlock() + + t.Each(func(item interface{}) bool { + s.m[item] = keyExists + return true + }) +} diff --git a/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_ts_test.go b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_ts_test.go new file mode 100644 index 00000000..8d2f509d --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/fatih/set.v0/set_ts_test.go @@ -0,0 +1,321 @@ +package set + +import ( + "reflect" + "strconv" + "strings" + "testing" +) + +func TestSet_New(t *testing.T) { + s := New() + + if s.Size() != 0 { + t.Error("New: calling without any parameters should create a set with zero size") + } + + u := s.New() + if u.Size() != 0 { + t.Error("New: creating a new set via s.New() should create a set with zero size") + } +} + +func TestSet_New_parameters(t *testing.T) { + s := New("string", "another_string", 1, 3.14) + + if s.Size() != 4 { + t.Error("New: calling with parameters should create a set with size of four") + } +} + +func TestSet_Add(t *testing.T) { + s := New() + s.Add(1) + s.Add(2) + s.Add(2) // duplicate + s.Add("fatih") + s.Add("zeynep") + s.Add("zeynep") // another duplicate + + if s.Size() != 4 { + t.Error("Add: items are not unique. The set size should be four") + } + + if !s.Has(1, 2, "fatih", "zeynep") { + t.Error("Add: added items are not availabile in the set.") + } +} + +func TestSet_Add_multiple(t *testing.T) { + s := New() + s.Add("ankara", "san francisco", 3.14) + + if s.Size() != 3 { + t.Error("Add: items are not unique. The set size should be three") + } + + if !s.Has("ankara", "san francisco", 3.14) { + t.Error("Add: added items are not availabile in the set.") + } +} + +func TestSet_Remove(t *testing.T) { + s := New() + s.Add(1) + s.Add(2) + s.Add("fatih") + + s.Remove(1) + if s.Size() != 2 { + t.Error("Remove: set size should be two after removing") + } + + s.Remove(1) + if s.Size() != 2 { + t.Error("Remove: set size should be not change after trying to remove a non-existing item") + } + + s.Remove(2) + s.Remove("fatih") + if s.Size() != 0 { + t.Error("Remove: set size should be zero") + } + + s.Remove("fatih") // try to remove something from a zero length set +} + +func TestSet_Remove_multiple(t *testing.T) { + s := New() + s.Add("ankara", "san francisco", 3.14, "istanbul") + s.Remove("ankara", "san francisco", 3.14) + + if s.Size() != 1 { + t.Error("Remove: items are not unique. The set size should be four") + } + + if !s.Has("istanbul") { + t.Error("Add: added items are not availabile in the set.") + } +} + +func TestSet_Pop(t *testing.T) { + s := New() + s.Add(1) + s.Add(2) + s.Add("fatih") + + a := s.Pop() + if s.Size() != 2 { + t.Error("Pop: set size should be two after popping out") + } + + if s.Has(a) { + t.Error("Pop: returned item should not exist") + } + + s.Pop() + s.Pop() + b := s.Pop() + if b != nil { + t.Error("Pop: should return nil because set is empty") + } + + s.Pop() // try to remove something from a zero length set +} + +func TestSet_Has(t *testing.T) { + s := New("1", "2", "3", "4") + + if !s.Has("1") { + t.Error("Has: the item 1 exist, but 'Has' is returning false") + } + + if !s.Has("1", "2", "3", "4") { + t.Error("Has: the items all exist, but 'Has' is returning false") + } +} + +func TestSet_Clear(t *testing.T) { + s := New() + s.Add(1) + s.Add("istanbul") + s.Add("san francisco") + + s.Clear() + if s.Size() != 0 { + t.Error("Clear: set size should be zero") + } +} + +func TestSet_IsEmpty(t *testing.T) { + s := New() + + empty := s.IsEmpty() + if !empty { + t.Error("IsEmpty: set is empty, it should be true") + } + + s.Add(2) + s.Add(3) + notEmpty := s.IsEmpty() + + if notEmpty { + t.Error("IsEmpty: set is filled, it should be false") + } +} + +func TestSet_IsEqual(t *testing.T) { + s := New("1", "2", "3") + u := New("1", "2", "3") + + ok := s.IsEqual(u) + if !ok { + t.Error("IsEqual: set s and t are equal. However it returns false") + } + + // same size, different content + a := New("1", "2", "3") + b := New("4", "5", "6") + + ok = a.IsEqual(b) + if ok { + t.Error("IsEqual: set a and b are now equal (1). However it returns true") + } + + // different size, similar content + a = New("1", "2", "3") + b = New("1", "2", "3", "4") + + ok = a.IsEqual(b) + if ok { + t.Error("IsEqual: set s and t are now equal (2). However it returns true") + } + +} + +func TestSet_IsSubset(t *testing.T) { + s := New("1", "2", "3", "4") + u := New("1", "2", "3") + + ok := s.IsSubset(u) + if !ok { + t.Error("IsSubset: u is a subset of s. However it returns false") + } + + ok = u.IsSubset(s) + if ok { + t.Error("IsSubset: s is not a subset of u. However it returns true") + } + +} + +func TestSet_IsSuperset(t *testing.T) { + s := New("1", "2", "3", "4") + u := New("1", "2", "3") + + ok := u.IsSuperset(s) + if !ok { + t.Error("IsSuperset: s is a superset of u. However it returns false") + } + + ok = s.IsSuperset(u) + if ok { + t.Error("IsSuperset: u is not a superset of u. However it returns true") + } + +} + +func TestSet_String(t *testing.T) { + s := New() + if s.String() != "[]" { + t.Errorf("String: output is not what is excepted '%s'", s.String()) + } + + s.Add("1", "2", "3", "4") + if !strings.HasPrefix(s.String(), "[") { + t.Error("String: output should begin with a square bracket") + } + + if !strings.HasSuffix(s.String(), "]") { + t.Error("String: output should end with a square bracket") + } +} + +func TestSet_List(t *testing.T) { + s := New("1", "2", "3", "4") + + // this returns a slice of interface{} + if len(s.List()) != 4 { + t.Error("List: slice size should be four.") + } + + for _, item := range s.List() { + r := reflect.TypeOf(item) + if r.Kind().String() != "string" { + t.Error("List: slice item should be a string") + } + } +} + +func TestSet_Copy(t *testing.T) { + s := New("1", "2", "3", "4") + r := s.Copy() + + if !s.IsEqual(r) { + t.Error("Copy: set s and r are not equal") + } +} + +func TestSet_Merge(t *testing.T) { + s := New("1", "2", "3") + r := New("3", "4", "5") + s.Merge(r) + + if s.Size() != 5 { + t.Error("Merge: the set doesn't have all items in it.") + } + + if !s.Has("1", "2", "3", "4", "5") { + t.Error("Merge: merged items are not availabile in the set.") + } +} + +func TestSet_Separate(t *testing.T) { + s := New("1", "2", "3") + r := New("3", "5") + s.Separate(r) + + if s.Size() != 2 { + t.Error("Separate: the set doesn't have all items in it.") + } + + if !s.Has("1", "2") { + t.Error("Separate: items after separation are not availabile in the set.") + } +} + +func TestSet_RaceAdd(t *testing.T) { + // Create two sets. Add concurrently items to each of them. Remove from the + // other one. + // "go test -race" should detect this if the library is not thread-safe. + s := New() + u := New() + + go func() { + for i := 0; i < 1000; i++ { + item := "item" + strconv.Itoa(i) + go func(i int) { + s.Add(item) + u.Add(item) + }(i) + } + }() + + for i := 0; i < 1000; i++ { + item := "item" + strconv.Itoa(i) + go func(i int) { + s.Add(item) + u.Add(item) + }(i) + } +} diff --git a/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/.gitignore b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/.gitignore new file mode 100644 index 00000000..83656241 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/.gitignore @@ -0,0 +1,23 @@ +# 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 +*.test diff --git a/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/LICENSE b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/LICENSE new file mode 100644 index 00000000..a4f2f281 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Tyler Bunnell + +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/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/README.md b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/README.md new file mode 100644 index 00000000..531249a7 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/README.md @@ -0,0 +1,137 @@ +graceful [](http://godoc.org/github.com/tylerb/graceful) [](https://drone.io/github.com/tylerb/graceful/latest) [](https://coveralls.io/r/tylerb/graceful?branch=dronedebug) [](https://gitter.im/tylerb/graceful?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +======== + +Graceful is a Go 1.3+ package enabling graceful shutdown of http.Handler servers. + +## Installation + +To install, simply execute: + +``` +go get gopkg.in/tylerb/graceful.v1 +``` + +I am using [gopkg.in](http://labix.org/gopkg.in) to control releases. + +## Usage + +Using Graceful is easy. Simply create your http.Handler and pass it to the `Run` function: + +```go +package main + +import ( + "gopkg.in/tylerb/graceful.v1" + "net/http" + "fmt" + "time" +) + +func main() { + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + fmt.Fprintf(w, "Welcome to the home page!") + }) + + graceful.Run(":3001",10*time.Second,mux) +} +``` + +Another example, using [Negroni](https://github.com/codegangsta/negroni), functions in much the same manner: + +```go +package main + +import ( + "github.com/codegangsta/negroni" + "gopkg.in/tylerb/graceful.v1" + "net/http" + "fmt" + "time" +) + +func main() { + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + fmt.Fprintf(w, "Welcome to the home page!") + }) + + n := negroni.Classic() + n.UseHandler(mux) + //n.Run(":3000") + graceful.Run(":3001",10*time.Second,n) +} +``` + +In addition to Run there are the http.Server counterparts ListenAndServe, ListenAndServeTLS and Serve, which allow you to configure HTTPS, custom timeouts and error handling. +Graceful may also be used by instantiating its Server type directly, which embeds an http.Server: + +```go +mux := // ... + +srv := &graceful.Server{ + Timeout: 10 * time.Second, + + Server: &http.Server{ + Addr: ":1234", + Handler: mux, + }, +} + +srv.ListenAndServe() +``` + +This form allows you to set the ConnState callback, which works in the same way as in http.Server: + +```go +mux := // ... + +srv := &graceful.Server{ + Timeout: 10 * time.Second, + + ConnState: func(conn net.Conn, state http.ConnState) { + // conn has a new state + }, + + Server: &http.Server{ + Addr: ":1234", + Handler: mux, + }, +} + +srv.ListenAndServe() +``` + +## Behaviour + +When Graceful is sent a SIGINT or SIGTERM (possibly from ^C or a kill command), it: + +1. Disables keepalive connections. +2. Closes the listening socket, allowing another process to listen on that port immediately. +3. Starts a timer of `timeout` duration to give active requests a chance to finish. +4. When timeout expires, closes all active connections. +5. Closes the `stopChan`, waking up any blocking goroutines. +6. Returns from the function, allowing the server to terminate. + +## Notes + +If the `timeout` argument to `Run` is 0, the server never times out, allowing all active requests to complete. + +If you wish to stop the server in some way other than an OS signal, you may call the `Stop()` function. +This function stops the server, gracefully, using the new timeout value you provide. The `StopChan()` function +returns a channel on which you can block while waiting for the server to stop. This channel will be closed when +the server is stopped, allowing your execution to proceed. Multiple goroutines can block on this channel at the +same time and all will be signalled when stopping is complete. + +## Contributing + +If you would like to contribute, please: + +1. Create a GitHub issue regarding the contribution. Features and bugs should be discussed beforehand. +2. Fork the repository. +3. Create a pull request with your solution. This pull request should reference and close the issues (Fix #2). + +All pull requests should: + +1. Pass [gometalinter -t .](https://github.com/alecthomas/gometalinter) with no warnings. +2. Be `go fmt` formatted. diff --git a/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/graceful.go b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/graceful.go new file mode 100644 index 00000000..0951e08e --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/graceful.go @@ -0,0 +1,319 @@ +package graceful + +import ( + "crypto/tls" + "log" + "net" + "net/http" + "os" + "os/signal" + "sync" + "syscall" + "time" + + "golang.org/x/net/netutil" +) + +// Server wraps an http.Server with graceful connection handling. +// It may be used directly in the same way as http.Server, or may +// be constructed with the global functions in this package. +// +// Example: +// srv := &graceful.Server{ +// Timeout: 5 * time.Second, +// Server: &http.Server{Addr: ":1234", Handler: handler}, +// } +// srv.ListenAndServe() +type Server struct { + *http.Server + + // Timeout is the duration to allow outstanding requests to survive + // before forcefully terminating them. + Timeout time.Duration + + // Limit the number of outstanding requests + ListenLimit int + + // ConnState specifies an optional callback function that is + // called when a client connection changes state. This is a proxy + // to the underlying http.Server's ConnState, and the original + // must not be set directly. + ConnState func(net.Conn, http.ConnState) + + // ShutdownInitiated is an optional callback function that is called + // when shutdown is initiated. It can be used to notify the client + // side of long lived connections (e.g. websockets) to reconnect. + ShutdownInitiated func() + + // NoSignalHandling prevents graceful from automatically shutting down + // on SIGINT and SIGTERM. If set to true, you must shut down the server + // manually with Stop(). + NoSignalHandling bool + + // interrupt signals the listener to stop serving connections, + // and the server to shut down. + interrupt chan os.Signal + + // stopChan is the channel on which callers may block while waiting for + // the server to stop. + stopChan chan struct{} + + // stopLock is used to protect access to the stopChan. + stopLock sync.RWMutex + + // connections holds all connections managed by graceful + connections map[net.Conn]struct{} +} + +// Run serves the http.Handler with graceful shutdown enabled. +// +// timeout is the duration to wait until killing active requests and stopping the server. +// If timeout is 0, the server never times out. It waits for all active requests to finish. +func Run(addr string, timeout time.Duration, n http.Handler) { + srv := &Server{ + Timeout: timeout, + Server: &http.Server{Addr: addr, Handler: n}, + } + + if err := srv.ListenAndServe(); err != nil { + if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") { + logger := log.New(os.Stdout, "[graceful] ", 0) + logger.Fatal(err) + } + } +} + +// ListenAndServe is equivalent to http.Server.ListenAndServe with graceful shutdown enabled. +// +// timeout is the duration to wait until killing active requests and stopping the server. +// If timeout is 0, the server never times out. It waits for all active requests to finish. +func ListenAndServe(server *http.Server, timeout time.Duration) error { + srv := &Server{Timeout: timeout, Server: server} + return srv.ListenAndServe() +} + +// ListenAndServe is equivalent to http.Server.ListenAndServe with graceful shutdown enabled. +func (srv *Server) ListenAndServe() error { + // Create the listener so we can control their lifetime + addr := srv.Addr + if addr == "" { + addr = ":http" + } + l, err := net.Listen("tcp", addr) + if err != nil { + return err + } + + if srv.ListenLimit != 0 { + l = netutil.LimitListener(l, srv.ListenLimit) + } + return srv.Serve(l) +} + +// ListenAndServeTLS is equivalent to http.Server.ListenAndServeTLS with graceful shutdown enabled. +// +// timeout is the duration to wait until killing active requests and stopping the server. +// If timeout is 0, the server never times out. It waits for all active requests to finish. +func ListenAndServeTLS(server *http.Server, certFile, keyFile string, timeout time.Duration) error { + srv := &Server{Timeout: timeout, Server: server} + return srv.ListenAndServeTLS(certFile, keyFile) +} + +// ListenAndServeTLS is equivalent to http.Server.ListenAndServeTLS with graceful shutdown enabled. +func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error { + // Create the listener ourselves so we can control its lifetime + addr := srv.Addr + if addr == "" { + addr = ":https" + } + + config := &tls.Config{} + if srv.TLSConfig != nil { + *config = *srv.TLSConfig + } + if config.NextProtos == nil { + config.NextProtos = []string{"http/1.1"} + } + + var err error + config.Certificates = make([]tls.Certificate, 1) + config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return err + } + + conn, err := net.Listen("tcp", addr) + if err != nil { + return err + } + + tlsListener := tls.NewListener(conn, config) + return srv.Serve(tlsListener) +} + +// ListenAndServeTLSConfig can be used with an existing TLS config and is equivalent to +// http.Server.ListenAndServeTLS with graceful shutdown enabled, +func (srv *Server) ListenAndServeTLSConfig(config *tls.Config) error { + addr := srv.Addr + if addr == "" { + addr = ":https" + } + + conn, err := net.Listen("tcp", addr) + if err != nil { + return err + } + + tlsListener := tls.NewListener(conn, config) + return srv.Serve(tlsListener) +} + +// Serve is equivalent to http.Server.Serve with graceful shutdown enabled. +// +// timeout is the duration to wait until killing active requests and stopping the server. +// If timeout is 0, the server never times out. It waits for all active requests to finish. +func Serve(server *http.Server, l net.Listener, timeout time.Duration) error { + srv := &Server{Timeout: timeout, Server: server} + return srv.Serve(l) +} + +// Serve is equivalent to http.Server.Serve with graceful shutdown enabled. +func (srv *Server) Serve(listener net.Listener) error { + // Track connection state + add := make(chan net.Conn) + remove := make(chan net.Conn) + + srv.Server.ConnState = func(conn net.Conn, state http.ConnState) { + switch state { + case http.StateNew: + add <- conn + case http.StateClosed, http.StateHijacked: + remove <- conn + } + if srv.ConnState != nil { + srv.ConnState(conn, state) + } + } + + // Manage open connections + shutdown := make(chan chan struct{}) + kill := make(chan struct{}) + go srv.manageConnections(add, remove, shutdown, kill) + + interrupt := srv.interruptChan() + + // Set up the interrupt handler + if !srv.NoSignalHandling { + signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) + } + + go srv.handleInterrupt(interrupt, listener) + + // Serve with graceful listener. + // Execution blocks here until listener.Close() is called, above. + err := srv.Server.Serve(listener) + + srv.shutdown(shutdown, kill) + + return err +} + +// Stop instructs the type to halt operations and close +// the stop channel when it is finished. +// +// timeout is grace period for which to wait before shutting +// down the server. The timeout value passed here will override the +// timeout given when constructing the server, as this is an explicit +// command to stop the server. +func (srv *Server) Stop(timeout time.Duration) { + srv.Timeout = timeout + interrupt := srv.interruptChan() + interrupt <- syscall.SIGINT +} + +// StopChan gets the stop channel which will block until +// stopping has completed, at which point it is closed. +// Callers should never close the stop channel. +func (srv *Server) StopChan() <-chan struct{} { + srv.stopLock.Lock() + if srv.stopChan == nil { + srv.stopChan = make(chan struct{}) + } + srv.stopLock.Unlock() + return srv.stopChan +} + +func (srv *Server) manageConnections(add, remove chan net.Conn, shutdown chan chan struct{}, kill chan struct{}) { + { + var done chan struct{} + srv.connections = map[net.Conn]struct{}{} + for { + select { + case conn := <-add: + srv.connections[conn] = struct{}{} + case conn := <-remove: + delete(srv.connections, conn) + if done != nil && len(srv.connections) == 0 { + done <- struct{}{} + return + } + case done = <-shutdown: + if len(srv.connections) == 0 { + done <- struct{}{} + return + } + case <-kill: + for k := range srv.connections { + _ = k.Close() // nothing to do here if it errors + } + return + } + } + } +} + +func (srv *Server) interruptChan() chan os.Signal { + srv.stopLock.Lock() + if srv.interrupt == nil { + srv.interrupt = make(chan os.Signal, 1) + } + srv.stopLock.Unlock() + + return srv.interrupt +} + +func (srv *Server) handleInterrupt(interrupt chan os.Signal, listener net.Listener) { + <-interrupt + + srv.SetKeepAlivesEnabled(false) + _ = listener.Close() // we are shutting down anyway. ignore error. + + if srv.ShutdownInitiated != nil { + srv.ShutdownInitiated() + } + + signal.Stop(interrupt) + close(interrupt) +} + +func (srv *Server) shutdown(shutdown chan chan struct{}, kill chan struct{}) { + // Request done notification + done := make(chan struct{}) + shutdown <- done + + if srv.Timeout > 0 { + select { + case <-done: + case <-time.After(srv.Timeout): + close(kill) + } + } else { + <-done + } + // Close the stopChan to wake up any blocked goroutines. + srv.stopLock.Lock() + if srv.stopChan != nil { + close(srv.stopChan) + } + srv.stopLock.Unlock() +} diff --git a/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/graceful_test.go b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/graceful_test.go new file mode 100644 index 00000000..c7382736 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/graceful_test.go @@ -0,0 +1,379 @@ +package graceful + +import ( + "fmt" + "io" + "net" + "net/http" + "net/url" + "os" + "reflect" + "sync" + "syscall" + "testing" + "time" +) + +var ( + killTime = 500 * time.Millisecond + timeoutTime = 1000 * time.Millisecond + waitTime = 100 * time.Millisecond +) + +func runQuery(t *testing.T, expected int, shouldErr bool, wg *sync.WaitGroup, once *sync.Once) { + wg.Add(1) + defer wg.Done() + client := http.Client{} + r, err := client.Get("http://localhost:3000") + if shouldErr && err == nil { + once.Do(func() { + t.Fatal("Expected an error but none was encountered.") + }) + } else if shouldErr && err != nil { + if checkErr(t, err, once) { + return + } + } + if r != nil && r.StatusCode != expected { + once.Do(func() { + t.Fatalf("Incorrect status code on response. Expected %d. Got %d", expected, r.StatusCode) + }) + } else if r == nil { + once.Do(func() { + t.Fatal("No response when a response was expected.") + }) + } +} + +func checkErr(t *testing.T, err error, once *sync.Once) bool { + if err.(*url.Error).Err == io.EOF { + return true + } + errno := err.(*url.Error).Err.(*net.OpError).Err.(syscall.Errno) + if errno == syscall.ECONNREFUSED { + return true + } else if err != nil { + once.Do(func() { + t.Fatal("Error on Get:", err) + }) + } + return false +} + +func createListener(sleep time.Duration) (*http.Server, net.Listener, error) { + mux := http.NewServeMux() + mux.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { + time.Sleep(sleep) + rw.WriteHeader(http.StatusOK) + }) + + server := &http.Server{Addr: ":3000", Handler: mux} + l, err := net.Listen("tcp", ":3000") + if err != nil { + fmt.Println(err) + } + return server, l, err +} + +func runServer(timeout, sleep time.Duration, c chan os.Signal) error { + server, l, err := createListener(sleep) + if err != nil { + return err + } + + srv := &Server{Timeout: timeout, Server: server, interrupt: c} + return srv.Serve(l) +} + +func launchTestQueries(t *testing.T, wg *sync.WaitGroup, c chan os.Signal) { + var once sync.Once + for i := 0; i < 8; i++ { + go runQuery(t, http.StatusOK, false, wg, &once) + } + + time.Sleep(waitTime) + c <- os.Interrupt + time.Sleep(waitTime) + + for i := 0; i < 8; i++ { + go runQuery(t, 0, true, wg, &once) + } + + wg.Done() +} + +func TestGracefulRun(t *testing.T) { + c := make(chan os.Signal, 1) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + runServer(killTime, killTime/2, c) + wg.Done() + }() + + wg.Add(1) + go launchTestQueries(t, &wg, c) + wg.Wait() +} + +func TestGracefulRunTimesOut(t *testing.T) { + c := make(chan os.Signal, 1) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + runServer(killTime, killTime*10, c) + wg.Done() + }() + + var once sync.Once + wg.Add(1) + go func() { + for i := 0; i < 8; i++ { + go runQuery(t, 0, true, &wg, &once) + } + time.Sleep(waitTime) + c <- os.Interrupt + time.Sleep(waitTime) + for i := 0; i < 8; i++ { + go runQuery(t, 0, true, &wg, &once) + } + wg.Done() + }() + + wg.Wait() + +} + +func TestGracefulRunDoesntTimeOut(t *testing.T) { + c := make(chan os.Signal, 1) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + runServer(0, killTime*2, c) + wg.Done() + }() + + wg.Add(1) + go launchTestQueries(t, &wg, c) + wg.Wait() +} + +func TestGracefulRunNoRequests(t *testing.T) { + c := make(chan os.Signal, 1) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + runServer(0, killTime*2, c) + wg.Done() + }() + + c <- os.Interrupt + + wg.Wait() + +} + +func TestGracefulForwardsConnState(t *testing.T) { + c := make(chan os.Signal, 1) + states := make(map[http.ConnState]int) + var stateLock sync.Mutex + + connState := func(conn net.Conn, state http.ConnState) { + stateLock.Lock() + states[state]++ + stateLock.Unlock() + } + + var wg sync.WaitGroup + wg.Add(1) + + expected := map[http.ConnState]int{ + http.StateNew: 8, + http.StateActive: 8, + http.StateClosed: 8, + } + + go func() { + server, l, _ := createListener(killTime / 2) + srv := &Server{ + ConnState: connState, + Timeout: killTime, + Server: server, + interrupt: c, + } + srv.Serve(l) + + wg.Done() + }() + + wg.Add(1) + go launchTestQueries(t, &wg, c) + wg.Wait() + + stateLock.Lock() + if !reflect.DeepEqual(states, expected) { + t.Errorf("Incorrect connection state tracking.\n actual: %v\nexpected: %v\n", states, expected) + } + stateLock.Unlock() +} + +func TestGracefulExplicitStop(t *testing.T) { + server, l, err := createListener(1 * time.Millisecond) + if err != nil { + t.Fatal(err) + } + + srv := &Server{Timeout: killTime, Server: server} + + go func() { + go srv.Serve(l) + time.Sleep(waitTime) + srv.Stop(killTime) + }() + + // block on the stopChan until the server has shut down + select { + case <-srv.StopChan(): + case <-time.After(timeoutTime): + t.Fatal("Timed out while waiting for explicit stop to complete") + } +} + +func TestGracefulExplicitStopOverride(t *testing.T) { + server, l, err := createListener(1 * time.Millisecond) + if err != nil { + t.Fatal(err) + } + + srv := &Server{Timeout: killTime, Server: server} + + go func() { + go srv.Serve(l) + time.Sleep(waitTime) + srv.Stop(killTime / 2) + }() + + // block on the stopChan until the server has shut down + select { + case <-srv.StopChan(): + case <-time.After(killTime): + t.Fatal("Timed out while waiting for explicit stop to complete") + } +} + +func TestShutdownInitiatedCallback(t *testing.T) { + server, l, err := createListener(1 * time.Millisecond) + if err != nil { + t.Fatal(err) + } + + called := make(chan struct{}) + cb := func() { close(called) } + + srv := &Server{Server: server, ShutdownInitiated: cb} + + go func() { + go srv.Serve(l) + time.Sleep(waitTime) + srv.Stop(killTime) + }() + + select { + case <-called: + case <-time.After(killTime): + t.Fatal("Timed out while waiting for ShutdownInitiated callback to be called") + } +} +func hijackingListener(srv *Server) (*http.Server, net.Listener, error) { + mux := http.NewServeMux() + mux.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { + conn, bufrw, err := rw.(http.Hijacker).Hijack() + if err != nil { + http.Error(rw, "webserver doesn't support hijacking", http.StatusInternalServerError) + return + } + + defer conn.Close() + + bufrw.WriteString("HTTP/1.1 200 OK\r\n\r\n") + bufrw.Flush() + }) + + server := &http.Server{Addr: ":3000", Handler: mux} + l, err := net.Listen("tcp", ":3000") + return server, l, err +} + +func TestNotifyClosed(t *testing.T) { + c := make(chan os.Signal, 1) + + var wg sync.WaitGroup + wg.Add(1) + + srv := &Server{Timeout: killTime, interrupt: c} + server, l, err := hijackingListener(srv) + if err != nil { + t.Fatal(err) + } + + srv.Server = server + + go func() { + srv.Serve(l) + wg.Done() + }() + + var once sync.Once + for i := 0; i < 8; i++ { + runQuery(t, http.StatusOK, false, &wg, &once) + } + + srv.Stop(0) + + // block on the stopChan until the server has shut down + select { + case <-srv.StopChan(): + case <-time.After(timeoutTime): + t.Fatal("Timed out while waiting for explicit stop to complete") + } + + if len(srv.connections) > 0 { + t.Fatal("hijacked connections should not be managed") + } + +} + +func TestStopDeadlock(t *testing.T) { + c := make(chan struct{}) + + server, l, err := createListener(1 * time.Millisecond) + if err != nil { + t.Fatal(err) + } + + srv := &Server{Server: server, NoSignalHandling: true} + + go func() { + time.Sleep(waitTime) + srv.Serve(l) + }() + + go func() { + srv.Stop(0) + close(c) + }() + + select { + case <-c: + case <-time.After(timeoutTime): + t.Fatal("Timed out while waiting for explicit stop to complete") + } +} diff --git a/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/tests/main.go b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/tests/main.go new file mode 100644 index 00000000..8c8fa204 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/tylerb/graceful.v1/tests/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "sync" + + "github.com/codegangsta/negroni" + "github.com/tylerb/graceful" +) + +func main() { + + var wg sync.WaitGroup + + wg.Add(3) + go func() { + n := negroni.New() + fmt.Println("Launching server on :3000") + graceful.Run(":3000", 0, n) + fmt.Println("Terminated server on :3000") + wg.Done() + }() + go func() { + n := negroni.New() + fmt.Println("Launching server on :3001") + graceful.Run(":3001", 0, n) + fmt.Println("Terminated server on :3001") + wg.Done() + }() + go func() { + n := negroni.New() + fmt.Println("Launching server on :3002") + graceful.Run(":3002", 0, n) + fmt.Println("Terminated server on :3002") + wg.Done() + }() + fmt.Println("Press ctrl+c. All servers should terminate.") + wg.Wait() + +} -- GitLab