mirror of
https://github.com/beego/bee.git
synced 2024-11-22 05:00:54 +00:00
cleanup
This commit is contained in:
parent
20c6a26952
commit
dd3f690bf7
3
Beefile
3
Beefile
@ -1,7 +1,4 @@
|
|||||||
version: 0
|
version: 0
|
||||||
gopm:
|
|
||||||
enable: false
|
|
||||||
install: false
|
|
||||||
go_install: false
|
go_install: false
|
||||||
watch_ext: []
|
watch_ext: []
|
||||||
dir_structure:
|
dir_structure:
|
||||||
|
4
bee.json
4
bee.json
@ -1,9 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": 0,
|
"version": 0,
|
||||||
"gopm": {
|
|
||||||
"enable": false,
|
|
||||||
"install": false
|
|
||||||
},
|
|
||||||
"go_install": false,
|
"go_install": false,
|
||||||
"watch_ext": [],
|
"watch_ext": [],
|
||||||
"dir_structure": {
|
"dir_structure": {
|
||||||
|
@ -131,14 +131,10 @@ func AutoBuild(files []string, isgenerate bool) {
|
|||||||
os.Chdir(currpath)
|
os.Chdir(currpath)
|
||||||
|
|
||||||
cmdName := "go"
|
cmdName := "go"
|
||||||
if config.Conf.Gopm.Enable {
|
|
||||||
cmdName = "gopm"
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
stderr bytes.Buffer
|
stderr bytes.Buffer
|
||||||
stdout bytes.Buffer
|
|
||||||
)
|
)
|
||||||
// For applications use full import path like "github.com/.../.."
|
// For applications use full import path like "github.com/.../.."
|
||||||
// are able to use "go install" to reduce build time.
|
// are able to use "go install" to reduce build time.
|
||||||
@ -149,28 +145,6 @@ func AutoBuild(files []string, isgenerate bool) {
|
|||||||
icmd.Env = append(os.Environ(), "GOGC=off")
|
icmd.Env = append(os.Environ(), "GOGC=off")
|
||||||
icmd.Run()
|
icmd.Run()
|
||||||
}
|
}
|
||||||
if config.Conf.Gopm.Install {
|
|
||||||
icmd := exec.Command("go", "list", "./...")
|
|
||||||
icmd.Stdout = &stdout
|
|
||||||
icmd.Env = append(os.Environ(), "GOGC=off")
|
|
||||||
err = icmd.Run()
|
|
||||||
if err == nil {
|
|
||||||
list := strings.Split(stdout.String(), "\n")[1:]
|
|
||||||
for _, pkg := range list {
|
|
||||||
if len(pkg) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
icmd = exec.Command(cmdName, "install", pkg)
|
|
||||||
icmd.Stdout = os.Stdout
|
|
||||||
icmd.Stderr = os.Stderr
|
|
||||||
icmd.Env = append(os.Environ(), "GOGC=off")
|
|
||||||
err = icmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isgenerate {
|
if isgenerate {
|
||||||
beeLogger.Log.Info("Generating the docs...")
|
beeLogger.Log.Info("Generating the docs...")
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
beeLogger "github.com/beego/bee/logger"
|
beeLogger "github.com/beego/bee/logger"
|
||||||
@ -28,7 +27,6 @@ const confVer = 0
|
|||||||
|
|
||||||
var Conf = struct {
|
var Conf = struct {
|
||||||
Version int
|
Version int
|
||||||
Gopm gopm
|
|
||||||
GoInstall bool `json:"go_install" yaml:"go_install"` // Indicates whether execute "go install" before "go build".
|
GoInstall bool `json:"go_install" yaml:"go_install"` // Indicates whether execute "go install" before "go build".
|
||||||
DirStruct dirStruct `json:"dir_structure" yaml:"dir_structure"`
|
DirStruct dirStruct `json:"dir_structure" yaml:"dir_structure"`
|
||||||
CmdArgs []string `json:"cmd_args" yaml:"cmd_args"`
|
CmdArgs []string `json:"cmd_args" yaml:"cmd_args"`
|
||||||
@ -56,12 +54,6 @@ var Conf = struct {
|
|||||||
Scripts: map[string]string{},
|
Scripts: map[string]string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// gopm support
|
|
||||||
type gopm struct {
|
|
||||||
Enable bool
|
|
||||||
Install bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// dirStruct describes the application's directory structure
|
// dirStruct describes the application's directory structure
|
||||||
type dirStruct struct {
|
type dirStruct struct {
|
||||||
WatchAll bool `json:"watch_all" yaml:"watch_all"`
|
WatchAll bool `json:"watch_all" yaml:"watch_all"`
|
||||||
|
13
vendor/github.com/astaxie/beego/utils/safemap.go
generated
vendored
13
vendor/github.com/astaxie/beego/utils/safemap.go
generated
vendored
@ -61,10 +61,8 @@ func (m *BeeMap) Set(k interface{}, v interface{}) bool {
|
|||||||
func (m *BeeMap) Check(k interface{}) bool {
|
func (m *BeeMap) Check(k interface{}) bool {
|
||||||
m.lock.RLock()
|
m.lock.RLock()
|
||||||
defer m.lock.RUnlock()
|
defer m.lock.RUnlock()
|
||||||
if _, ok := m.bm[k]; !ok {
|
_, ok := m.bm[k]
|
||||||
return false
|
return ok
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the given key and value.
|
// Delete the given key and value.
|
||||||
@ -84,3 +82,10 @@ func (m *BeeMap) Items() map[interface{}]interface{} {
|
|||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Count returns the number of items within the map.
|
||||||
|
func (m *BeeMap) Count() int {
|
||||||
|
m.lock.RLock()
|
||||||
|
defer m.lock.RUnlock()
|
||||||
|
return len(m.bm)
|
||||||
|
}
|
||||||
|
5
vendor/github.com/gorilla/websocket/README.md
generated
vendored
5
vendor/github.com/gorilla/websocket/README.md
generated
vendored
@ -3,6 +3,9 @@
|
|||||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
||||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket)
|
||||||
|
[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket)
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
* [API Reference](http://godoc.org/github.com/gorilla/websocket)
|
* [API Reference](http://godoc.org/github.com/gorilla/websocket)
|
||||||
@ -43,7 +46,7 @@ subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn
|
|||||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr>
|
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr>
|
||||||
<tr><td>Get the <a href="https://tools.ietf.org/html/rfc6455#section-5.6">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>
|
<tr><td>Get the <a href="https://tools.ietf.org/html/rfc6455#section-5.6">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>
|
||||||
<tr><td colspan="3">Other Features</tr></td>
|
<tr><td colspan="3">Other Features</tr></td>
|
||||||
<tr><td>Limit size of received message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.SetReadLimit">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=5082">No</a></td></tr>
|
<tr><td><a href="https://tools.ietf.org/html/rfc7692">Compression Extensions</a></td><td>Experimental</td><td>No</td></tr>
|
||||||
<tr><td>Read message using io.Reader</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextReader">Yes</a></td><td>No, see note 3</td></tr>
|
<tr><td>Read message using io.Reader</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextReader">Yes</a></td><td>No, see note 3</td></tr>
|
||||||
<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
|
<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
81
vendor/github.com/gorilla/websocket/client.go
generated
vendored
81
vendor/github.com/gorilla/websocket/client.go
generated
vendored
@ -23,6 +23,8 @@ import (
|
|||||||
// invalid.
|
// invalid.
|
||||||
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
||||||
|
|
||||||
|
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
|
||||||
|
|
||||||
// NewClient creates a new client connection using the given net connection.
|
// NewClient creates a new client connection using the given net connection.
|
||||||
// The URL u specifies the host and request URI. Use requestHeader to specify
|
// The URL u specifies the host and request URI. Use requestHeader to specify
|
||||||
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
||||||
@ -64,12 +66,24 @@ type Dialer struct {
|
|||||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||||
HandshakeTimeout time.Duration
|
HandshakeTimeout time.Duration
|
||||||
|
|
||||||
// Input and output buffer sizes. If the buffer size is zero, then a
|
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
|
||||||
// default value of 4096 is used.
|
// size is zero, then a useful default size is used. The I/O buffer sizes
|
||||||
|
// do not limit the size of the messages that can be sent or received.
|
||||||
ReadBufferSize, WriteBufferSize int
|
ReadBufferSize, WriteBufferSize int
|
||||||
|
|
||||||
// Subprotocols specifies the client's requested subprotocols.
|
// Subprotocols specifies the client's requested subprotocols.
|
||||||
Subprotocols []string
|
Subprotocols []string
|
||||||
|
|
||||||
|
// EnableCompression specifies if the client should attempt to negotiate
|
||||||
|
// per message compression (RFC 7692). Setting this value to true does not
|
||||||
|
// guarantee that compression will be supported. Currently only "no context
|
||||||
|
// takeover" modes are supported.
|
||||||
|
EnableCompression bool
|
||||||
|
|
||||||
|
// Jar specifies the cookie jar.
|
||||||
|
// If Jar is nil, cookies are not sent in requests and ignored
|
||||||
|
// in responses.
|
||||||
|
Jar http.CookieJar
|
||||||
}
|
}
|
||||||
|
|
||||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||||
@ -83,7 +97,6 @@ func parseURL(s string) (*url.URL, error) {
|
|||||||
//
|
//
|
||||||
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
|
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
|
||||||
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
|
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
|
||||||
|
|
||||||
var u url.URL
|
var u url.URL
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(s, "ws://"):
|
case strings.HasPrefix(s, "ws://"):
|
||||||
@ -193,6 +206,13 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
Host: u.Host,
|
Host: u.Host,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the cookies present in the cookie jar of the dialer
|
||||||
|
if d.Jar != nil {
|
||||||
|
for _, cookie := range d.Jar.Cookies(u) {
|
||||||
|
req.AddCookie(cookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set the request headers using the capitalization for names and values in
|
// Set the request headers using the capitalization for names and values in
|
||||||
// RFC examples. Although the capitalization shouldn't matter, there are
|
// RFC examples. Although the capitalization shouldn't matter, there are
|
||||||
// servers that depend on it. The Header.Set method is not used because the
|
// servers that depend on it. The Header.Set method is not used because the
|
||||||
@ -214,6 +234,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
k == "Connection" ||
|
k == "Connection" ||
|
||||||
k == "Sec-Websocket-Key" ||
|
k == "Sec-Websocket-Key" ||
|
||||||
k == "Sec-Websocket-Version" ||
|
k == "Sec-Websocket-Version" ||
|
||||||
|
k == "Sec-Websocket-Extensions" ||
|
||||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||||
default:
|
default:
|
||||||
@ -221,6 +242,10 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.EnableCompression {
|
||||||
|
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
||||||
|
}
|
||||||
|
|
||||||
hostPort, hostNoPort := hostPortNoPort(u)
|
hostPort, hostNoPort := hostPortNoPort(u)
|
||||||
|
|
||||||
var proxyURL *url.URL
|
var proxyURL *url.URL
|
||||||
@ -324,6 +349,13 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.Jar != nil {
|
||||||
|
if rc := resp.Cookies(); len(rc) > 0 {
|
||||||
|
d.Jar.SetCookies(u, rc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if resp.StatusCode != 101 ||
|
if resp.StatusCode != 101 ||
|
||||||
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
|
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
|
||||||
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
|
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
|
||||||
@ -337,6 +369,20 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
return nil, resp, ErrBadHandshake
|
return nil, resp, ErrBadHandshake
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, ext := range parseExtensions(req.Header) {
|
||||||
|
if ext[""] != "permessage-deflate" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, snct := ext["server_no_context_takeover"]
|
||||||
|
_, cnct := ext["client_no_context_takeover"]
|
||||||
|
if !snct || !cnct {
|
||||||
|
return nil, resp, errInvalidCompression
|
||||||
|
}
|
||||||
|
conn.newCompressionWriter = compressNoContextTakeover
|
||||||
|
conn.newDecompressionReader = decompressNoContextTakeover
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||||
|
|
||||||
@ -344,32 +390,3 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
netConn = nil // to avoid close in defer.
|
netConn = nil // to avoid close in defer.
|
||||||
return conn, resp, nil
|
return conn, resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// cloneTLSConfig clones all public fields except the fields
|
|
||||||
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
|
|
||||||
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
|
|
||||||
// config in active use.
|
|
||||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
|
||||||
if cfg == nil {
|
|
||||||
return &tls.Config{}
|
|
||||||
}
|
|
||||||
return &tls.Config{
|
|
||||||
Rand: cfg.Rand,
|
|
||||||
Time: cfg.Time,
|
|
||||||
Certificates: cfg.Certificates,
|
|
||||||
NameToCertificate: cfg.NameToCertificate,
|
|
||||||
GetCertificate: cfg.GetCertificate,
|
|
||||||
RootCAs: cfg.RootCAs,
|
|
||||||
NextProtos: cfg.NextProtos,
|
|
||||||
ServerName: cfg.ServerName,
|
|
||||||
ClientAuth: cfg.ClientAuth,
|
|
||||||
ClientCAs: cfg.ClientCAs,
|
|
||||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
|
||||||
CipherSuites: cfg.CipherSuites,
|
|
||||||
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
|
||||||
ClientSessionCache: cfg.ClientSessionCache,
|
|
||||||
MinVersion: cfg.MinVersion,
|
|
||||||
MaxVersion: cfg.MaxVersion,
|
|
||||||
CurvePreferences: cfg.CurvePreferences,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
Normal file
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2013 The Gorilla WebSocket 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.8
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
|
if cfg == nil {
|
||||||
|
return &tls.Config{}
|
||||||
|
}
|
||||||
|
return cfg.Clone()
|
||||||
|
}
|
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
Normal file
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2013 The Gorilla WebSocket 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.8
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
// cloneTLSConfig clones all public fields except the fields
|
||||||
|
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
|
||||||
|
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
|
||||||
|
// config in active use.
|
||||||
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
|
if cfg == nil {
|
||||||
|
return &tls.Config{}
|
||||||
|
}
|
||||||
|
return &tls.Config{
|
||||||
|
Rand: cfg.Rand,
|
||||||
|
Time: cfg.Time,
|
||||||
|
Certificates: cfg.Certificates,
|
||||||
|
NameToCertificate: cfg.NameToCertificate,
|
||||||
|
GetCertificate: cfg.GetCertificate,
|
||||||
|
RootCAs: cfg.RootCAs,
|
||||||
|
NextProtos: cfg.NextProtos,
|
||||||
|
ServerName: cfg.ServerName,
|
||||||
|
ClientAuth: cfg.ClientAuth,
|
||||||
|
ClientCAs: cfg.ClientCAs,
|
||||||
|
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||||
|
CipherSuites: cfg.CipherSuites,
|
||||||
|
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
||||||
|
ClientSessionCache: cfg.ClientSessionCache,
|
||||||
|
MinVersion: cfg.MinVersion,
|
||||||
|
MaxVersion: cfg.MaxVersion,
|
||||||
|
CurvePreferences: cfg.CurvePreferences,
|
||||||
|
}
|
||||||
|
}
|
148
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
148
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// Copyright 2017 The Gorilla WebSocket 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 websocket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/flate"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6
|
||||||
|
maxCompressionLevel = flate.BestCompression
|
||||||
|
defaultCompressionLevel = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool
|
||||||
|
flateReaderPool = sync.Pool{New: func() interface{} {
|
||||||
|
return flate.NewReader(nil)
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
|
||||||
|
func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
|
||||||
|
const tail =
|
||||||
|
// Add four bytes as specified in RFC
|
||||||
|
"\x00\x00\xff\xff" +
|
||||||
|
// Add final block to squelch unexpected EOF error from flate reader.
|
||||||
|
"\x01\x00\x00\xff\xff"
|
||||||
|
|
||||||
|
fr, _ := flateReaderPool.Get().(io.ReadCloser)
|
||||||
|
fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
|
||||||
|
return &flateReadWrapper{fr}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidCompressionLevel(level int) bool {
|
||||||
|
return minCompressionLevel <= level && level <= maxCompressionLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {
|
||||||
|
p := &flateWriterPools[level-minCompressionLevel]
|
||||||
|
tw := &truncWriter{w: w}
|
||||||
|
fw, _ := p.Get().(*flate.Writer)
|
||||||
|
if fw == nil {
|
||||||
|
fw, _ = flate.NewWriter(tw, level)
|
||||||
|
} else {
|
||||||
|
fw.Reset(tw)
|
||||||
|
}
|
||||||
|
return &flateWriteWrapper{fw: fw, tw: tw, p: p}
|
||||||
|
}
|
||||||
|
|
||||||
|
// truncWriter is an io.Writer that writes all but the last four bytes of the
|
||||||
|
// stream to another io.Writer.
|
||||||
|
type truncWriter struct {
|
||||||
|
w io.WriteCloser
|
||||||
|
n int
|
||||||
|
p [4]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *truncWriter) Write(p []byte) (int, error) {
|
||||||
|
n := 0
|
||||||
|
|
||||||
|
// fill buffer first for simplicity.
|
||||||
|
if w.n < len(w.p) {
|
||||||
|
n = copy(w.p[w.n:], p)
|
||||||
|
p = p[n:]
|
||||||
|
w.n += n
|
||||||
|
if len(p) == 0 {
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m := len(p)
|
||||||
|
if m > len(w.p) {
|
||||||
|
m = len(w.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn, err := w.w.Write(w.p[:m]); err != nil {
|
||||||
|
return n + nn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(w.p[:], w.p[m:])
|
||||||
|
copy(w.p[len(w.p)-m:], p[len(p)-m:])
|
||||||
|
nn, err := w.w.Write(p[:len(p)-m])
|
||||||
|
return n + nn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type flateWriteWrapper struct {
|
||||||
|
fw *flate.Writer
|
||||||
|
tw *truncWriter
|
||||||
|
p *sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *flateWriteWrapper) Write(p []byte) (int, error) {
|
||||||
|
if w.fw == nil {
|
||||||
|
return 0, errWriteClosed
|
||||||
|
}
|
||||||
|
return w.fw.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *flateWriteWrapper) Close() error {
|
||||||
|
if w.fw == nil {
|
||||||
|
return errWriteClosed
|
||||||
|
}
|
||||||
|
err1 := w.fw.Flush()
|
||||||
|
w.p.Put(w.fw)
|
||||||
|
w.fw = nil
|
||||||
|
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
|
||||||
|
return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
|
||||||
|
}
|
||||||
|
err2 := w.tw.w.Close()
|
||||||
|
if err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
|
type flateReadWrapper struct {
|
||||||
|
fr io.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flateReadWrapper) Read(p []byte) (int, error) {
|
||||||
|
if r.fr == nil {
|
||||||
|
return 0, io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
n, err := r.fr.Read(p)
|
||||||
|
if err == io.EOF {
|
||||||
|
// Preemptively place the reader back in the pool. This helps with
|
||||||
|
// scenarios where the application does not call NextReader() soon after
|
||||||
|
// this final read.
|
||||||
|
r.Close()
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flateReadWrapper) Close() error {
|
||||||
|
if r.fr == nil {
|
||||||
|
return io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
err := r.fr.Close()
|
||||||
|
flateReaderPool.Put(r.fr)
|
||||||
|
r.fr = nil
|
||||||
|
return err
|
||||||
|
}
|
488
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
488
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
@ -13,16 +13,25 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// Frame header byte 0 bits from Section 5.2 of RFC 6455
|
||||||
|
finalBit = 1 << 7
|
||||||
|
rsv1Bit = 1 << 6
|
||||||
|
rsv2Bit = 1 << 5
|
||||||
|
rsv3Bit = 1 << 4
|
||||||
|
|
||||||
|
// Frame header byte 1 bits from Section 5.2 of RFC 6455
|
||||||
|
maskBit = 1 << 7
|
||||||
|
|
||||||
maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
|
maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
|
||||||
maxControlFramePayloadSize = 125
|
maxControlFramePayloadSize = 125
|
||||||
finalBit = 1 << 7
|
|
||||||
maskBit = 1 << 7
|
writeWait = time.Second
|
||||||
writeWait = time.Second
|
|
||||||
|
|
||||||
defaultReadBufferSize = 4096
|
defaultReadBufferSize = 4096
|
||||||
defaultWriteBufferSize = 4096
|
defaultWriteBufferSize = 4096
|
||||||
@ -172,6 +181,11 @@ var (
|
|||||||
errInvalidControlFrame = errors.New("websocket: invalid control frame")
|
errInvalidControlFrame = errors.New("websocket: invalid control frame")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func newMaskKey() [4]byte {
|
||||||
|
n := rand.Uint32()
|
||||||
|
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
||||||
|
}
|
||||||
|
|
||||||
func hideTempErr(err error) error {
|
func hideTempErr(err error) error {
|
||||||
if e, ok := err.(net.Error); ok && e.Temporary() {
|
if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||||
err = &netError{msg: e.Error(), timeout: e.Timeout()}
|
err = &netError{msg: e.Error(), timeout: e.Timeout()}
|
||||||
@ -210,39 +224,28 @@ func isValidReceivedCloseCode(code int) bool {
|
|||||||
return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
|
return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
|
||||||
}
|
}
|
||||||
|
|
||||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
// The Conn type represents a WebSocket connection.
|
||||||
for i := range b {
|
|
||||||
b[i] ^= key[pos&3]
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
return pos & 3
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMaskKey() [4]byte {
|
|
||||||
n := rand.Uint32()
|
|
||||||
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conn represents a WebSocket connection.
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
isServer bool
|
isServer bool
|
||||||
subprotocol string
|
subprotocol string
|
||||||
|
|
||||||
// Write fields
|
// Write fields
|
||||||
mu chan bool // used as mutex to protect write to conn and closeSent
|
mu chan bool // used as mutex to protect write to conn
|
||||||
closeSent bool // true if close message was sent
|
writeBuf []byte // frame is constructed in this buffer.
|
||||||
|
writeDeadline time.Time
|
||||||
|
writer io.WriteCloser // the current writer returned to the application
|
||||||
|
isWriting bool // for best-effort concurrent write detection
|
||||||
|
|
||||||
// Message writer fields.
|
writeErrMu sync.Mutex
|
||||||
writeErr error
|
writeErr error
|
||||||
writeBuf []byte // frame is constructed in this buffer.
|
|
||||||
writePos int // end of data in writeBuf.
|
enableWriteCompression bool
|
||||||
writeFrameType int // type of the current frame.
|
compressionLevel int
|
||||||
writeDeadline time.Time
|
newCompressionWriter func(io.WriteCloser, int) io.WriteCloser
|
||||||
isWriting bool // for best-effort concurrent write detection
|
|
||||||
messageWriter *messageWriter // the current writer
|
|
||||||
|
|
||||||
// Read fields
|
// Read fields
|
||||||
|
reader io.ReadCloser // the current reader returned to the application
|
||||||
readErr error
|
readErr error
|
||||||
br *bufio.Reader
|
br *bufio.Reader
|
||||||
readRemaining int64 // bytes remaining in current frame.
|
readRemaining int64 // bytes remaining in current frame.
|
||||||
@ -253,34 +256,83 @@ type Conn struct {
|
|||||||
readMaskKey [4]byte
|
readMaskKey [4]byte
|
||||||
handlePong func(string) error
|
handlePong func(string) error
|
||||||
handlePing func(string) error
|
handlePing func(string) error
|
||||||
|
handleClose func(int, string) error
|
||||||
readErrCount int
|
readErrCount int
|
||||||
messageReader *messageReader // the current reader
|
messageReader *messageReader // the current low-level reader
|
||||||
|
|
||||||
|
readDecompress bool // whether last read frame had RSV1 set
|
||||||
|
newDecompressionReader func(io.Reader) io.ReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
|
func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
|
||||||
|
return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeHook struct {
|
||||||
|
p []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wh *writeHook) Write(p []byte) (int, error) {
|
||||||
|
wh.p = p
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn {
|
||||||
mu := make(chan bool, 1)
|
mu := make(chan bool, 1)
|
||||||
mu <- true
|
mu <- true
|
||||||
|
|
||||||
if readBufferSize == 0 {
|
var br *bufio.Reader
|
||||||
readBufferSize = defaultReadBufferSize
|
if readBufferSize == 0 && brw != nil && brw.Reader != nil {
|
||||||
|
// Reuse the supplied bufio.Reader if the buffer has a useful size.
|
||||||
|
// This code assumes that peek on a reader returns
|
||||||
|
// bufio.Reader.buf[:0].
|
||||||
|
brw.Reader.Reset(conn)
|
||||||
|
if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 {
|
||||||
|
br = brw.Reader
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if readBufferSize < maxControlFramePayloadSize {
|
if br == nil {
|
||||||
readBufferSize = maxControlFramePayloadSize
|
if readBufferSize == 0 {
|
||||||
|
readBufferSize = defaultReadBufferSize
|
||||||
|
}
|
||||||
|
if readBufferSize < maxControlFramePayloadSize {
|
||||||
|
readBufferSize = maxControlFramePayloadSize
|
||||||
|
}
|
||||||
|
br = bufio.NewReaderSize(conn, readBufferSize)
|
||||||
}
|
}
|
||||||
if writeBufferSize == 0 {
|
|
||||||
writeBufferSize = defaultWriteBufferSize
|
var writeBuf []byte
|
||||||
|
if writeBufferSize == 0 && brw != nil && brw.Writer != nil {
|
||||||
|
// Use the bufio.Writer's buffer if the buffer has a useful size. This
|
||||||
|
// code assumes that bufio.Writer.buf[:1] is passed to the
|
||||||
|
// bufio.Writer's underlying writer.
|
||||||
|
var wh writeHook
|
||||||
|
brw.Writer.Reset(&wh)
|
||||||
|
brw.Writer.WriteByte(0)
|
||||||
|
brw.Flush()
|
||||||
|
if cap(wh.p) >= maxFrameHeaderSize+256 {
|
||||||
|
writeBuf = wh.p[:cap(wh.p)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if writeBuf == nil {
|
||||||
|
if writeBufferSize == 0 {
|
||||||
|
writeBufferSize = defaultWriteBufferSize
|
||||||
|
}
|
||||||
|
writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := &Conn{
|
c := &Conn{
|
||||||
isServer: isServer,
|
isServer: isServer,
|
||||||
br: bufio.NewReaderSize(conn, readBufferSize),
|
br: br,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
mu: mu,
|
mu: mu,
|
||||||
readFinal: true,
|
readFinal: true,
|
||||||
writeBuf: make([]byte, writeBufferSize+maxFrameHeaderSize),
|
writeBuf: writeBuf,
|
||||||
writeFrameType: noFrame,
|
enableWriteCompression: true,
|
||||||
writePos: maxFrameHeaderSize,
|
compressionLevel: defaultCompressionLevel,
|
||||||
}
|
}
|
||||||
|
c.SetCloseHandler(nil)
|
||||||
c.SetPingHandler(nil)
|
c.SetPingHandler(nil)
|
||||||
c.SetPongHandler(nil)
|
c.SetPongHandler(nil)
|
||||||
return c
|
return c
|
||||||
@ -308,29 +360,40 @@ func (c *Conn) RemoteAddr() net.Addr {
|
|||||||
|
|
||||||
// Write methods
|
// Write methods
|
||||||
|
|
||||||
|
func (c *Conn) writeFatal(err error) error {
|
||||||
|
err = hideTempErr(err)
|
||||||
|
c.writeErrMu.Lock()
|
||||||
|
if c.writeErr == nil {
|
||||||
|
c.writeErr = err
|
||||||
|
}
|
||||||
|
c.writeErrMu.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error {
|
func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error {
|
||||||
<-c.mu
|
<-c.mu
|
||||||
defer func() { c.mu <- true }()
|
defer func() { c.mu <- true }()
|
||||||
|
|
||||||
if c.closeSent {
|
c.writeErrMu.Lock()
|
||||||
return ErrCloseSent
|
err := c.writeErr
|
||||||
} else if frameType == CloseMessage {
|
c.writeErrMu.Unlock()
|
||||||
c.closeSent = true
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.conn.SetWriteDeadline(deadline)
|
c.conn.SetWriteDeadline(deadline)
|
||||||
for _, buf := range bufs {
|
for _, buf := range bufs {
|
||||||
if len(buf) > 0 {
|
if len(buf) > 0 {
|
||||||
n, err := c.conn.Write(buf)
|
_, err := c.conn.Write(buf)
|
||||||
if n != len(buf) {
|
|
||||||
// Close on partial write.
|
|
||||||
c.conn.Close()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return c.writeFatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if frameType == CloseMessage {
|
||||||
|
c.writeFatal(ErrCloseSent)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,18 +442,41 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
|||||||
}
|
}
|
||||||
defer func() { c.mu <- true }()
|
defer func() { c.mu <- true }()
|
||||||
|
|
||||||
if c.closeSent {
|
c.writeErrMu.Lock()
|
||||||
return ErrCloseSent
|
err := c.writeErr
|
||||||
} else if messageType == CloseMessage {
|
c.writeErrMu.Unlock()
|
||||||
c.closeSent = true
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.conn.SetWriteDeadline(deadline)
|
c.conn.SetWriteDeadline(deadline)
|
||||||
n, err := c.conn.Write(buf)
|
_, err = c.conn.Write(buf)
|
||||||
if n != 0 && n != len(buf) {
|
if err != nil {
|
||||||
c.conn.Close()
|
return c.writeFatal(err)
|
||||||
}
|
}
|
||||||
return hideTempErr(err)
|
if messageType == CloseMessage {
|
||||||
|
c.writeFatal(ErrCloseSent)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) prepWrite(messageType int) error {
|
||||||
|
// Close previous writer if not already closed by the application. It's
|
||||||
|
// probably better to return an error in this situation, but we cannot
|
||||||
|
// change this without breaking existing applications.
|
||||||
|
if c.writer != nil {
|
||||||
|
c.writer.Close()
|
||||||
|
c.writer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isControl(messageType) && !isData(messageType) {
|
||||||
|
return errBadWriteOpCode
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeErrMu.Lock()
|
||||||
|
err := c.writeErr
|
||||||
|
c.writeErrMu.Unlock()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextWriter returns a writer for the next message to send. The writer's Close
|
// NextWriter returns a writer for the next message to send. The writer's Close
|
||||||
@ -399,42 +485,61 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
|||||||
// There can be at most one open writer on a connection. NextWriter closes the
|
// There can be at most one open writer on a connection. NextWriter closes the
|
||||||
// previous writer if the application has not already done so.
|
// previous writer if the application has not already done so.
|
||||||
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
|
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
|
||||||
if c.writeErr != nil {
|
if err := c.prepWrite(messageType); err != nil {
|
||||||
return nil, c.writeErr
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.writeFrameType != noFrame {
|
mw := &messageWriter{
|
||||||
if err := c.flushFrame(true, nil); err != nil {
|
c: c,
|
||||||
return nil, err
|
frameType: messageType,
|
||||||
}
|
pos: maxFrameHeaderSize,
|
||||||
}
|
}
|
||||||
|
c.writer = mw
|
||||||
if !isControl(messageType) && !isData(messageType) {
|
if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {
|
||||||
return nil, errBadWriteOpCode
|
w := c.newCompressionWriter(c.writer, c.compressionLevel)
|
||||||
|
mw.compress = true
|
||||||
|
c.writer = w
|
||||||
}
|
}
|
||||||
|
return c.writer, nil
|
||||||
c.writeFrameType = messageType
|
|
||||||
w := &messageWriter{c}
|
|
||||||
c.messageWriter = w
|
|
||||||
return w, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) flushFrame(final bool, extra []byte) error {
|
type messageWriter struct {
|
||||||
length := c.writePos - maxFrameHeaderSize + len(extra)
|
c *Conn
|
||||||
|
compress bool // whether next call to flushFrame should set RSV1
|
||||||
|
pos int // end of data in writeBuf.
|
||||||
|
frameType int // type of the current frame.
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *messageWriter) fatal(err error) error {
|
||||||
|
if w.err != nil {
|
||||||
|
w.err = err
|
||||||
|
w.c.writer = nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// flushFrame writes buffered data and extra as a frame to the network. The
|
||||||
|
// final argument indicates that this is the last frame in the message.
|
||||||
|
func (w *messageWriter) flushFrame(final bool, extra []byte) error {
|
||||||
|
c := w.c
|
||||||
|
length := w.pos - maxFrameHeaderSize + len(extra)
|
||||||
|
|
||||||
// Check for invalid control frames.
|
// Check for invalid control frames.
|
||||||
if isControl(c.writeFrameType) &&
|
if isControl(w.frameType) &&
|
||||||
(!final || length > maxControlFramePayloadSize) {
|
(!final || length > maxControlFramePayloadSize) {
|
||||||
c.messageWriter = nil
|
return w.fatal(errInvalidControlFrame)
|
||||||
c.writeFrameType = noFrame
|
|
||||||
c.writePos = maxFrameHeaderSize
|
|
||||||
return errInvalidControlFrame
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b0 := byte(c.writeFrameType)
|
b0 := byte(w.frameType)
|
||||||
if final {
|
if final {
|
||||||
b0 |= finalBit
|
b0 |= finalBit
|
||||||
}
|
}
|
||||||
|
if w.compress {
|
||||||
|
b0 |= rsv1Bit
|
||||||
|
}
|
||||||
|
w.compress = false
|
||||||
|
|
||||||
b1 := byte(0)
|
b1 := byte(0)
|
||||||
if !c.isServer {
|
if !c.isServer {
|
||||||
b1 |= maskBit
|
b1 |= maskBit
|
||||||
@ -466,10 +571,9 @@ func (c *Conn) flushFrame(final bool, extra []byte) error {
|
|||||||
if !c.isServer {
|
if !c.isServer {
|
||||||
key := newMaskKey()
|
key := newMaskKey()
|
||||||
copy(c.writeBuf[maxFrameHeaderSize-4:], key[:])
|
copy(c.writeBuf[maxFrameHeaderSize-4:], key[:])
|
||||||
maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:c.writePos])
|
maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos])
|
||||||
if len(extra) > 0 {
|
if len(extra) > 0 {
|
||||||
c.writeErr = errors.New("websocket: internal error, extra used in client mode")
|
return c.writeFatal(errors.New("websocket: internal error, extra used in client mode"))
|
||||||
return c.writeErr
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,43 +586,35 @@ func (c *Conn) flushFrame(final bool, extra []byte) error {
|
|||||||
}
|
}
|
||||||
c.isWriting = true
|
c.isWriting = true
|
||||||
|
|
||||||
c.writeErr = c.write(c.writeFrameType, c.writeDeadline, c.writeBuf[framePos:c.writePos], extra)
|
err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra)
|
||||||
|
|
||||||
if !c.isWriting {
|
if !c.isWriting {
|
||||||
panic("concurrent write to websocket connection")
|
panic("concurrent write to websocket connection")
|
||||||
}
|
}
|
||||||
c.isWriting = false
|
c.isWriting = false
|
||||||
|
|
||||||
// Setup for next frame.
|
if err != nil {
|
||||||
c.writePos = maxFrameHeaderSize
|
return w.fatal(err)
|
||||||
c.writeFrameType = continuationFrame
|
}
|
||||||
|
|
||||||
if final {
|
if final {
|
||||||
c.messageWriter = nil
|
c.writer = nil
|
||||||
c.writeFrameType = noFrame
|
return nil
|
||||||
}
|
}
|
||||||
return c.writeErr
|
|
||||||
}
|
|
||||||
|
|
||||||
type messageWriter struct{ c *Conn }
|
// Setup for next frame.
|
||||||
|
w.pos = maxFrameHeaderSize
|
||||||
func (w *messageWriter) err() error {
|
w.frameType = continuationFrame
|
||||||
c := w.c
|
|
||||||
if c.messageWriter != w {
|
|
||||||
return errWriteClosed
|
|
||||||
}
|
|
||||||
if c.writeErr != nil {
|
|
||||||
return c.writeErr
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *messageWriter) ncopy(max int) (int, error) {
|
func (w *messageWriter) ncopy(max int) (int, error) {
|
||||||
n := len(w.c.writeBuf) - w.c.writePos
|
n := len(w.c.writeBuf) - w.pos
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
if err := w.c.flushFrame(false, nil); err != nil {
|
if err := w.flushFrame(false, nil); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
n = len(w.c.writeBuf) - w.c.writePos
|
n = len(w.c.writeBuf) - w.pos
|
||||||
}
|
}
|
||||||
if n > max {
|
if n > max {
|
||||||
n = max
|
n = max
|
||||||
@ -526,14 +622,14 @@ func (w *messageWriter) ncopy(max int) (int, error) {
|
|||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *messageWriter) write(final bool, p []byte) (int, error) {
|
func (w *messageWriter) Write(p []byte) (int, error) {
|
||||||
if err := w.err(); err != nil {
|
if w.err != nil {
|
||||||
return 0, err
|
return 0, w.err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(p) > 2*len(w.c.writeBuf) && w.c.isServer {
|
if len(p) > 2*len(w.c.writeBuf) && w.c.isServer {
|
||||||
// Don't buffer large messages.
|
// Don't buffer large messages.
|
||||||
err := w.c.flushFrame(final, p)
|
err := w.flushFrame(false, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -546,20 +642,16 @@ func (w *messageWriter) write(final bool, p []byte) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
copy(w.c.writeBuf[w.c.writePos:], p[:n])
|
copy(w.c.writeBuf[w.pos:], p[:n])
|
||||||
w.c.writePos += n
|
w.pos += n
|
||||||
p = p[n:]
|
p = p[n:]
|
||||||
}
|
}
|
||||||
return nn, nil
|
return nn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *messageWriter) Write(p []byte) (int, error) {
|
|
||||||
return w.write(false, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *messageWriter) WriteString(p string) (int, error) {
|
func (w *messageWriter) WriteString(p string) (int, error) {
|
||||||
if err := w.err(); err != nil {
|
if w.err != nil {
|
||||||
return 0, err
|
return 0, w.err
|
||||||
}
|
}
|
||||||
|
|
||||||
nn := len(p)
|
nn := len(p)
|
||||||
@ -568,27 +660,27 @@ func (w *messageWriter) WriteString(p string) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
copy(w.c.writeBuf[w.c.writePos:], p[:n])
|
copy(w.c.writeBuf[w.pos:], p[:n])
|
||||||
w.c.writePos += n
|
w.pos += n
|
||||||
p = p[n:]
|
p = p[n:]
|
||||||
}
|
}
|
||||||
return nn, nil
|
return nn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
||||||
if err := w.err(); err != nil {
|
if w.err != nil {
|
||||||
return 0, err
|
return 0, w.err
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
if w.c.writePos == len(w.c.writeBuf) {
|
if w.pos == len(w.c.writeBuf) {
|
||||||
err = w.c.flushFrame(false, nil)
|
err = w.flushFrame(false, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var n int
|
var n int
|
||||||
n, err = r.Read(w.c.writeBuf[w.c.writePos:])
|
n, err = r.Read(w.c.writeBuf[w.pos:])
|
||||||
w.c.writePos += n
|
w.pos += n
|
||||||
nn += int64(n)
|
nn += int64(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@ -601,27 +693,59 @@ func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *messageWriter) Close() error {
|
func (w *messageWriter) Close() error {
|
||||||
if err := w.err(); err != nil {
|
if w.err != nil {
|
||||||
|
return w.err
|
||||||
|
}
|
||||||
|
if err := w.flushFrame(true, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return w.c.flushFrame(true, nil)
|
w.err = errWriteClosed
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WritePreparedMessage writes prepared message into connection.
|
||||||
|
func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error {
|
||||||
|
frameType, frameData, err := pm.frame(prepareKey{
|
||||||
|
isServer: c.isServer,
|
||||||
|
compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType),
|
||||||
|
compressionLevel: c.compressionLevel,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.isWriting {
|
||||||
|
panic("concurrent write to websocket connection")
|
||||||
|
}
|
||||||
|
c.isWriting = true
|
||||||
|
err = c.write(frameType, c.writeDeadline, frameData, nil)
|
||||||
|
if !c.isWriting {
|
||||||
|
panic("concurrent write to websocket connection")
|
||||||
|
}
|
||||||
|
c.isWriting = false
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMessage is a helper method for getting a writer using NextWriter,
|
// WriteMessage is a helper method for getting a writer using NextWriter,
|
||||||
// writing the message and closing the writer.
|
// writing the message and closing the writer.
|
||||||
func (c *Conn) WriteMessage(messageType int, data []byte) error {
|
func (c *Conn) WriteMessage(messageType int, data []byte) error {
|
||||||
|
|
||||||
|
if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {
|
||||||
|
// Fast path with no allocations and single frame.
|
||||||
|
|
||||||
|
if err := c.prepWrite(messageType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize}
|
||||||
|
n := copy(c.writeBuf[mw.pos:], data)
|
||||||
|
mw.pos += n
|
||||||
|
data = data[n:]
|
||||||
|
return mw.flushFrame(true, data)
|
||||||
|
}
|
||||||
|
|
||||||
w, err := c.NextWriter(messageType)
|
w, err := c.NextWriter(messageType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, ok := w.(*messageWriter); ok && c.isServer {
|
|
||||||
// Optimize write as a single frame.
|
|
||||||
n := copy(c.writeBuf[c.writePos:], data)
|
|
||||||
c.writePos += n
|
|
||||||
data = data[n:]
|
|
||||||
err = c.flushFrame(true, data)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = w.Write(data); err != nil {
|
if _, err = w.Write(data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -658,12 +782,17 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
|
|
||||||
final := p[0]&finalBit != 0
|
final := p[0]&finalBit != 0
|
||||||
frameType := int(p[0] & 0xf)
|
frameType := int(p[0] & 0xf)
|
||||||
reserved := int((p[0] >> 4) & 0x7)
|
|
||||||
mask := p[1]&maskBit != 0
|
mask := p[1]&maskBit != 0
|
||||||
c.readRemaining = int64(p[1] & 0x7f)
|
c.readRemaining = int64(p[1] & 0x7f)
|
||||||
|
|
||||||
if reserved != 0 {
|
c.readDecompress = false
|
||||||
return noFrame, c.handleProtocolError("unexpected reserved bits " + strconv.Itoa(reserved))
|
if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 {
|
||||||
|
c.readDecompress = true
|
||||||
|
p[0] &^= rsv1Bit
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 {
|
||||||
|
return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch frameType {
|
switch frameType {
|
||||||
@ -759,11 +888,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
return noFrame, err
|
return noFrame, err
|
||||||
}
|
}
|
||||||
case CloseMessage:
|
case CloseMessage:
|
||||||
echoMessage := []byte{}
|
|
||||||
closeCode := CloseNoStatusReceived
|
closeCode := CloseNoStatusReceived
|
||||||
closeText := ""
|
closeText := ""
|
||||||
if len(payload) >= 2 {
|
if len(payload) >= 2 {
|
||||||
echoMessage = payload[:2]
|
|
||||||
closeCode = int(binary.BigEndian.Uint16(payload))
|
closeCode = int(binary.BigEndian.Uint16(payload))
|
||||||
if !isValidReceivedCloseCode(closeCode) {
|
if !isValidReceivedCloseCode(closeCode) {
|
||||||
return noFrame, c.handleProtocolError("invalid close code")
|
return noFrame, c.handleProtocolError("invalid close code")
|
||||||
@ -773,7 +900,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
return noFrame, c.handleProtocolError("invalid utf8 payload in close frame")
|
return noFrame, c.handleProtocolError("invalid utf8 payload in close frame")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.WriteControl(CloseMessage, echoMessage, time.Now().Add(writeWait))
|
if err := c.handleClose(closeCode, closeText); err != nil {
|
||||||
|
return noFrame, err
|
||||||
|
}
|
||||||
return noFrame, &CloseError{Code: closeCode, Text: closeText}
|
return noFrame, &CloseError{Code: closeCode, Text: closeText}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -796,6 +925,11 @@ func (c *Conn) handleProtocolError(message string) error {
|
|||||||
// permanent. Once this method returns a non-nil error, all subsequent calls to
|
// permanent. Once this method returns a non-nil error, all subsequent calls to
|
||||||
// this method return the same error.
|
// this method return the same error.
|
||||||
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
||||||
|
// Close previous reader, only relevant for decompression.
|
||||||
|
if c.reader != nil {
|
||||||
|
c.reader.Close()
|
||||||
|
c.reader = nil
|
||||||
|
}
|
||||||
|
|
||||||
c.messageReader = nil
|
c.messageReader = nil
|
||||||
c.readLength = 0
|
c.readLength = 0
|
||||||
@ -807,9 +941,12 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if frameType == TextMessage || frameType == BinaryMessage {
|
if frameType == TextMessage || frameType == BinaryMessage {
|
||||||
r := &messageReader{c}
|
c.messageReader = &messageReader{c}
|
||||||
c.messageReader = r
|
c.reader = c.messageReader
|
||||||
return frameType, r, nil
|
if c.readDecompress {
|
||||||
|
c.reader = c.newDecompressionReader(c.reader)
|
||||||
|
}
|
||||||
|
return frameType, c.reader, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -871,6 +1008,10 @@ func (r *messageReader) Read(b []byte) (int, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *messageReader) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReadMessage is a helper method for getting a reader using NextReader and
|
// ReadMessage is a helper method for getting a reader using NextReader and
|
||||||
// reading from that reader to a buffer.
|
// reading from that reader to a buffer.
|
||||||
func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
|
func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
|
||||||
@ -898,6 +1039,38 @@ func (c *Conn) SetReadLimit(limit int64) {
|
|||||||
c.readLimit = limit
|
c.readLimit = limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseHandler returns the current close handler
|
||||||
|
func (c *Conn) CloseHandler() func(code int, text string) error {
|
||||||
|
return c.handleClose
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCloseHandler sets the handler for close messages received from the peer.
|
||||||
|
// The code argument to h is the received close code or CloseNoStatusReceived
|
||||||
|
// if the close message is empty. The default close handler sends a close frame
|
||||||
|
// back to the peer.
|
||||||
|
//
|
||||||
|
// The application must read the connection to process close messages as
|
||||||
|
// described in the section on Control Frames above.
|
||||||
|
//
|
||||||
|
// The connection read methods return a CloseError when a close frame is
|
||||||
|
// received. Most applications should handle close messages as part of their
|
||||||
|
// normal error handling. Applications should only set a close handler when the
|
||||||
|
// application must perform some action before sending a close frame back to
|
||||||
|
// the peer.
|
||||||
|
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
||||||
|
if h == nil {
|
||||||
|
h = func(code int, text string) error {
|
||||||
|
message := []byte{}
|
||||||
|
if code != CloseNoStatusReceived {
|
||||||
|
message = FormatCloseMessage(code, "")
|
||||||
|
}
|
||||||
|
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.handleClose = h
|
||||||
|
}
|
||||||
|
|
||||||
// PingHandler returns the current ping handler
|
// PingHandler returns the current ping handler
|
||||||
func (c *Conn) PingHandler() func(appData string) error {
|
func (c *Conn) PingHandler() func(appData string) error {
|
||||||
return c.handlePing
|
return c.handlePing
|
||||||
@ -906,6 +1079,9 @@ func (c *Conn) PingHandler() func(appData string) error {
|
|||||||
// SetPingHandler sets the handler for ping messages received from the peer.
|
// SetPingHandler sets the handler for ping messages received from the peer.
|
||||||
// The appData argument to h is the PING frame application data. The default
|
// The appData argument to h is the PING frame application data. The default
|
||||||
// ping handler sends a pong to the peer.
|
// ping handler sends a pong to the peer.
|
||||||
|
//
|
||||||
|
// The application must read the connection to process ping messages as
|
||||||
|
// described in the section on Control Frames above.
|
||||||
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(message string) error {
|
h = func(message string) error {
|
||||||
@ -929,6 +1105,9 @@ func (c *Conn) PongHandler() func(appData string) error {
|
|||||||
// SetPongHandler sets the handler for pong messages received from the peer.
|
// SetPongHandler sets the handler for pong messages received from the peer.
|
||||||
// The appData argument to h is the PONG frame application data. The default
|
// The appData argument to h is the PONG frame application data. The default
|
||||||
// pong handler does nothing.
|
// pong handler does nothing.
|
||||||
|
//
|
||||||
|
// The application must read the connection to process ping messages as
|
||||||
|
// described in the section on Control Frames above.
|
||||||
func (c *Conn) SetPongHandler(h func(appData string) error) {
|
func (c *Conn) SetPongHandler(h func(appData string) error) {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(string) error { return nil }
|
h = func(string) error { return nil }
|
||||||
@ -942,6 +1121,25 @@ func (c *Conn) UnderlyingConn() net.Conn {
|
|||||||
return c.conn
|
return c.conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableWriteCompression enables and disables write compression of
|
||||||
|
// subsequent text and binary messages. This function is a noop if
|
||||||
|
// compression was not negotiated with the peer.
|
||||||
|
func (c *Conn) EnableWriteCompression(enable bool) {
|
||||||
|
c.enableWriteCompression = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCompressionLevel sets the flate compression level for subsequent text and
|
||||||
|
// binary messages. This function is a noop if compression was not negotiated
|
||||||
|
// with the peer. See the compress/flate package for a description of
|
||||||
|
// compression levels.
|
||||||
|
func (c *Conn) SetCompressionLevel(level int) error {
|
||||||
|
if !isValidCompressionLevel(level) {
|
||||||
|
return errors.New("websocket: invalid compression level")
|
||||||
|
}
|
||||||
|
c.compressionLevel = level
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
|
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
|
||||||
func FormatCloseMessage(closeCode int, text string) []byte {
|
func FormatCloseMessage(closeCode int, text string) []byte {
|
||||||
buf := make([]byte, 2+len(text))
|
buf := make([]byte, 2+len(text))
|
||||||
|
34
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
34
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
@ -118,9 +118,10 @@
|
|||||||
//
|
//
|
||||||
// Applications are responsible for ensuring that no more than one goroutine
|
// Applications are responsible for ensuring that no more than one goroutine
|
||||||
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
|
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
|
||||||
// WriteJSON) concurrently and that no more than one goroutine calls the read
|
// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and
|
||||||
// methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler,
|
// that no more than one goroutine calls the read methods (NextReader,
|
||||||
// SetPingHandler) concurrently.
|
// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)
|
||||||
|
// concurrently.
|
||||||
//
|
//
|
||||||
// The Close and WriteControl methods can be called concurrently with all other
|
// The Close and WriteControl methods can be called concurrently with all other
|
||||||
// methods.
|
// methods.
|
||||||
@ -149,4 +150,31 @@
|
|||||||
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
||||||
// application's responsibility to check the Origin header before calling
|
// application's responsibility to check the Origin header before calling
|
||||||
// Upgrade.
|
// Upgrade.
|
||||||
|
//
|
||||||
|
// Compression EXPERIMENTAL
|
||||||
|
//
|
||||||
|
// Per message compression extensions (RFC 7692) are experimentally supported
|
||||||
|
// by this package in a limited capacity. Setting the EnableCompression option
|
||||||
|
// to true in Dialer or Upgrader will attempt to negotiate per message deflate
|
||||||
|
// support.
|
||||||
|
//
|
||||||
|
// var upgrader = websocket.Upgrader{
|
||||||
|
// EnableCompression: true,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If compression was successfully negotiated with the connection's peer, any
|
||||||
|
// message received in compressed form will be automatically decompressed.
|
||||||
|
// All Read methods will return uncompressed bytes.
|
||||||
|
//
|
||||||
|
// Per message compression of messages written to a connection can be enabled
|
||||||
|
// or disabled by calling the corresponding Conn method:
|
||||||
|
//
|
||||||
|
// conn.EnableWriteCompression(false)
|
||||||
|
//
|
||||||
|
// Currently this package does not support compression with "context takeover".
|
||||||
|
// This means that messages must be compressed and decompressed in isolation,
|
||||||
|
// without retaining sliding window or dictionary state across messages. For
|
||||||
|
// more details refer to RFC 7692.
|
||||||
|
//
|
||||||
|
// Use of compression is experimental and may result in decreased performance.
|
||||||
package websocket
|
package websocket
|
||||||
|
55
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
Normal file
55
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2016 The Gorilla WebSocket 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 !appengine
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||||
|
|
||||||
|
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||||
|
|
||||||
|
// Mask one byte at a time for small buffers.
|
||||||
|
if len(b) < 2*wordSize {
|
||||||
|
for i := range b {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
return pos & 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask one byte at a time to word boundary.
|
||||||
|
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
||||||
|
n = wordSize - n
|
||||||
|
for i := range b[:n] {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create aligned word size key.
|
||||||
|
var k [wordSize]byte
|
||||||
|
for i := range k {
|
||||||
|
k[i] = key[(pos+i)&3]
|
||||||
|
}
|
||||||
|
kw := *(*uintptr)(unsafe.Pointer(&k))
|
||||||
|
|
||||||
|
// Mask one word at a time.
|
||||||
|
n := (len(b) / wordSize) * wordSize
|
||||||
|
for i := 0; i < n; i += wordSize {
|
||||||
|
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask one byte at a time for remaining bytes.
|
||||||
|
b = b[n:]
|
||||||
|
for i := range b {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos & 3
|
||||||
|
}
|
15
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
Normal file
15
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2016 The Gorilla WebSocket 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 appengine
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||||
|
for i := range b {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
return pos & 3
|
||||||
|
}
|
103
vendor/github.com/gorilla/websocket/prepared.go
generated
vendored
Normal file
103
vendor/github.com/gorilla/websocket/prepared.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// Copyright 2017 The Gorilla WebSocket 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 websocket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PreparedMessage caches on the wire representations of a message payload.
|
||||||
|
// Use PreparedMessage to efficiently send a message payload to multiple
|
||||||
|
// connections. PreparedMessage is especially useful when compression is used
|
||||||
|
// because the CPU and memory expensive compression operation can be executed
|
||||||
|
// once for a given set of compression options.
|
||||||
|
type PreparedMessage struct {
|
||||||
|
messageType int
|
||||||
|
data []byte
|
||||||
|
err error
|
||||||
|
mu sync.Mutex
|
||||||
|
frames map[prepareKey]*preparedFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
|
||||||
|
type prepareKey struct {
|
||||||
|
isServer bool
|
||||||
|
compress bool
|
||||||
|
compressionLevel int
|
||||||
|
}
|
||||||
|
|
||||||
|
// preparedFrame contains data in wire representation.
|
||||||
|
type preparedFrame struct {
|
||||||
|
once sync.Once
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPreparedMessage returns an initialized PreparedMessage. You can then send
|
||||||
|
// it to connection using WritePreparedMessage method. Valid wire
|
||||||
|
// representation will be calculated lazily only once for a set of current
|
||||||
|
// connection options.
|
||||||
|
func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {
|
||||||
|
pm := &PreparedMessage{
|
||||||
|
messageType: messageType,
|
||||||
|
frames: make(map[prepareKey]*preparedFrame),
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare a plain server frame.
|
||||||
|
_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// To protect against caller modifying the data argument, remember the data
|
||||||
|
// copied to the plain server frame.
|
||||||
|
pm.data = frameData[len(frameData)-len(data):]
|
||||||
|
return pm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
|
||||||
|
pm.mu.Lock()
|
||||||
|
frame, ok := pm.frames[key]
|
||||||
|
if !ok {
|
||||||
|
frame = &preparedFrame{}
|
||||||
|
pm.frames[key] = frame
|
||||||
|
}
|
||||||
|
pm.mu.Unlock()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
frame.once.Do(func() {
|
||||||
|
// Prepare a frame using a 'fake' connection.
|
||||||
|
// TODO: Refactor code in conn.go to allow more direct construction of
|
||||||
|
// the frame.
|
||||||
|
mu := make(chan bool, 1)
|
||||||
|
mu <- true
|
||||||
|
var nc prepareConn
|
||||||
|
c := &Conn{
|
||||||
|
conn: &nc,
|
||||||
|
mu: mu,
|
||||||
|
isServer: key.isServer,
|
||||||
|
compressionLevel: key.compressionLevel,
|
||||||
|
enableWriteCompression: true,
|
||||||
|
writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
|
||||||
|
}
|
||||||
|
if key.compress {
|
||||||
|
c.newCompressionWriter = compressNoContextTakeover
|
||||||
|
}
|
||||||
|
err = c.WriteMessage(pm.messageType, pm.data)
|
||||||
|
frame.data = nc.buf.Bytes()
|
||||||
|
})
|
||||||
|
return pm.messageType, frame.data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type prepareConn struct {
|
||||||
|
buf bytes.Buffer
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) }
|
||||||
|
func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }
|
61
vendor/github.com/gorilla/websocket/server.go
generated
vendored
61
vendor/github.com/gorilla/websocket/server.go
generated
vendored
@ -28,8 +28,9 @@ type Upgrader struct {
|
|||||||
HandshakeTimeout time.Duration
|
HandshakeTimeout time.Duration
|
||||||
|
|
||||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
|
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
|
||||||
// size is zero, then a default value of 4096 is used. The I/O buffer sizes
|
// size is zero, then buffers allocated by the HTTP server are used. The
|
||||||
// do not limit the size of the messages that can be sent or received.
|
// I/O buffer sizes do not limit the size of the messages that can be sent
|
||||||
|
// or received.
|
||||||
ReadBufferSize, WriteBufferSize int
|
ReadBufferSize, WriteBufferSize int
|
||||||
|
|
||||||
// Subprotocols specifies the server's supported protocols in order of
|
// Subprotocols specifies the server's supported protocols in order of
|
||||||
@ -46,6 +47,12 @@ type Upgrader struct {
|
|||||||
// CheckOrigin is nil, the host in the Origin header must not be set or
|
// CheckOrigin is nil, the host in the Origin header must not be set or
|
||||||
// must match the host of the request.
|
// must match the host of the request.
|
||||||
CheckOrigin func(r *http.Request) bool
|
CheckOrigin func(r *http.Request) bool
|
||||||
|
|
||||||
|
// EnableCompression specify if the server should attempt to negotiate per
|
||||||
|
// message compression (RFC 7692). Setting this value to true does not
|
||||||
|
// guarantee that compression will be supported. Currently only "no context
|
||||||
|
// takeover" modes are supported.
|
||||||
|
EnableCompression bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
|
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
|
||||||
@ -53,6 +60,7 @@ func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status in
|
|||||||
if u.Error != nil {
|
if u.Error != nil {
|
||||||
u.Error(w, r, status, err)
|
u.Error(w, r, status, err)
|
||||||
} else {
|
} else {
|
||||||
|
w.Header().Set("Sec-Websocket-Version", "13")
|
||||||
http.Error(w, http.StatusText(status), status)
|
http.Error(w, http.StatusText(status), status)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -97,18 +105,23 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header
|
|||||||
// response.
|
// response.
|
||||||
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
||||||
if r.Method != "GET" {
|
if r.Method != "GET" {
|
||||||
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: method not GET")
|
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET")
|
||||||
}
|
}
|
||||||
if values := r.Header["Sec-Websocket-Version"]; len(values) == 0 || values[0] != "13" {
|
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13")
|
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
||||||
|
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'")
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'")
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
||||||
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
|
||||||
}
|
}
|
||||||
|
|
||||||
checkOrigin := u.CheckOrigin
|
checkOrigin := u.CheckOrigin
|
||||||
@ -116,19 +129,30 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
checkOrigin = checkSameOrigin
|
checkOrigin = checkSameOrigin
|
||||||
}
|
}
|
||||||
if !checkOrigin(r) {
|
if !checkOrigin(r) {
|
||||||
return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed")
|
return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
||||||
if challengeKey == "" {
|
if challengeKey == "" {
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank")
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank")
|
||||||
}
|
}
|
||||||
|
|
||||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||||
|
|
||||||
|
// Negotiate PMCE
|
||||||
|
var compress bool
|
||||||
|
if u.EnableCompression {
|
||||||
|
for _, ext := range parseExtensions(r.Header) {
|
||||||
|
if ext[""] != "permessage-deflate" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
compress = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
netConn net.Conn
|
netConn net.Conn
|
||||||
br *bufio.Reader
|
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -136,21 +160,25 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
if !ok {
|
if !ok {
|
||||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
|
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
|
||||||
}
|
}
|
||||||
var rw *bufio.ReadWriter
|
var brw *bufio.ReadWriter
|
||||||
netConn, rw, err = h.Hijack()
|
netConn, brw, err = h.Hijack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
|
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
br = rw.Reader
|
|
||||||
|
|
||||||
if br.Buffered() > 0 {
|
if brw.Reader.Buffered() > 0 {
|
||||||
netConn.Close()
|
netConn.Close()
|
||||||
return nil, errors.New("websocket: client sent data before handshake is complete")
|
return nil, errors.New("websocket: client sent data before handshake is complete")
|
||||||
}
|
}
|
||||||
|
|
||||||
c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize)
|
c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw)
|
||||||
c.subprotocol = subprotocol
|
c.subprotocol = subprotocol
|
||||||
|
|
||||||
|
if compress {
|
||||||
|
c.newCompressionWriter = compressNoContextTakeover
|
||||||
|
c.newDecompressionReader = decompressNoContextTakeover
|
||||||
|
}
|
||||||
|
|
||||||
p := c.writeBuf[:0]
|
p := c.writeBuf[:0]
|
||||||
p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
|
p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
|
||||||
p = append(p, computeAcceptKey(challengeKey)...)
|
p = append(p, computeAcceptKey(challengeKey)...)
|
||||||
@ -160,6 +188,9 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
p = append(p, c.subprotocol...)
|
p = append(p, c.subprotocol...)
|
||||||
p = append(p, "\r\n"...)
|
p = append(p, "\r\n"...)
|
||||||
}
|
}
|
||||||
|
if compress {
|
||||||
|
p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
|
||||||
|
}
|
||||||
for k, vs := range responseHeader {
|
for k, vs := range responseHeader {
|
||||||
if k == "Sec-Websocket-Protocol" {
|
if k == "Sec-Websocket-Protocol" {
|
||||||
continue
|
continue
|
||||||
|
28
vendor/github.com/howeyc/fsnotify/AUTHORS
generated
vendored
28
vendor/github.com/howeyc/fsnotify/AUTHORS
generated
vendored
@ -1,28 +0,0 @@
|
|||||||
# Names should be added to this file as
|
|
||||||
# Name or Organization <email address>
|
|
||||||
# The email address is not required for organizations.
|
|
||||||
|
|
||||||
# You can update this list using the following command:
|
|
||||||
#
|
|
||||||
# $ git shortlog -se | awk '{print $2 " " $3 " " $4}'
|
|
||||||
|
|
||||||
# Please keep the list sorted.
|
|
||||||
|
|
||||||
Adrien Bustany <adrien@bustany.org>
|
|
||||||
Caleb Spare <cespare@gmail.com>
|
|
||||||
Case Nelson <case@teammating.com>
|
|
||||||
Chris Howey <howeyc@gmail.com> <chris@howey.me>
|
|
||||||
Christoffer Buchholz <christoffer.buchholz@gmail.com>
|
|
||||||
Dave Cheney <dave@cheney.net>
|
|
||||||
Francisco Souza <f@souza.cc>
|
|
||||||
John C Barstow
|
|
||||||
Kelvin Fo <vmirage@gmail.com>
|
|
||||||
Nathan Youngman <git@nathany.com>
|
|
||||||
Paul Hammond <paul@paulhammond.org>
|
|
||||||
Pursuit92 <JoshChase@techpursuit.net>
|
|
||||||
Rob Figueiredo <robfig@gmail.com>
|
|
||||||
Travis Cline <travis.cline@gmail.com>
|
|
||||||
Tudor Golubenco <tudor.g@gmail.com>
|
|
||||||
bronze1man <bronze1man@gmail.com>
|
|
||||||
debrando <denis.brandolini@gmail.com>
|
|
||||||
henrikedwards <henrik.edwards@gmail.com>
|
|
160
vendor/github.com/howeyc/fsnotify/CHANGELOG.md
generated
vendored
160
vendor/github.com/howeyc/fsnotify/CHANGELOG.md
generated
vendored
@ -1,160 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
## v0.9.0 / 2014-01-17
|
|
||||||
|
|
||||||
* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
|
|
||||||
* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
|
|
||||||
* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
|
|
||||||
|
|
||||||
## v0.8.12 / 2013-11-13
|
|
||||||
|
|
||||||
* [API] Remove FD_SET and friends from Linux adapter
|
|
||||||
|
|
||||||
## v0.8.11 / 2013-11-02
|
|
||||||
|
|
||||||
* [Doc] Add Changelog [#72][] (thanks @nathany)
|
|
||||||
* [Doc] Spotlight and double modify events on OS X [#62][] (reported by @paulhammond)
|
|
||||||
|
|
||||||
## v0.8.10 / 2013-10-19
|
|
||||||
|
|
||||||
* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
|
|
||||||
* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
|
|
||||||
* [Doc] specify OS-specific limits in README (thanks @debrando)
|
|
||||||
|
|
||||||
## v0.8.9 / 2013-09-08
|
|
||||||
|
|
||||||
* [Doc] Contributing (thanks @nathany)
|
|
||||||
* [Doc] update package path in example code [#63][] (thanks @paulhammond)
|
|
||||||
* [Doc] GoCI badge in README (Linux only) [#60][]
|
|
||||||
* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany)
|
|
||||||
|
|
||||||
## v0.8.8 / 2013-06-17
|
|
||||||
|
|
||||||
* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
|
|
||||||
|
|
||||||
## v0.8.7 / 2013-06-03
|
|
||||||
|
|
||||||
* [API] Make syscall flags internal
|
|
||||||
* [Fix] inotify: ignore event changes
|
|
||||||
* [Fix] race in symlink test [#45][] (reported by @srid)
|
|
||||||
* [Fix] tests on Windows
|
|
||||||
* lower case error messages
|
|
||||||
|
|
||||||
## v0.8.6 / 2013-05-23
|
|
||||||
|
|
||||||
* kqueue: Use EVT_ONLY flag on Darwin
|
|
||||||
* [Doc] Update README with full example
|
|
||||||
|
|
||||||
## v0.8.5 / 2013-05-09
|
|
||||||
|
|
||||||
* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
|
|
||||||
|
|
||||||
## v0.8.4 / 2013-04-07
|
|
||||||
|
|
||||||
* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
|
|
||||||
|
|
||||||
## v0.8.3 / 2013-03-13
|
|
||||||
|
|
||||||
* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
|
|
||||||
* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
|
|
||||||
|
|
||||||
## v0.8.2 / 2013-02-07
|
|
||||||
|
|
||||||
* [Doc] add Authors
|
|
||||||
* [Fix] fix data races for map access [#29][] (thanks @fsouza)
|
|
||||||
|
|
||||||
## v0.8.1 / 2013-01-09
|
|
||||||
|
|
||||||
* [Fix] Windows path separators
|
|
||||||
* [Doc] BSD License
|
|
||||||
|
|
||||||
## v0.8.0 / 2012-11-09
|
|
||||||
|
|
||||||
* kqueue: directory watching improvements (thanks @vmirage)
|
|
||||||
* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
|
|
||||||
* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
|
|
||||||
|
|
||||||
## v0.7.4 / 2012-10-09
|
|
||||||
|
|
||||||
* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
|
|
||||||
* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
|
|
||||||
* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
|
|
||||||
* [Fix] kqueue: modify after recreation of file
|
|
||||||
|
|
||||||
## v0.7.3 / 2012-09-27
|
|
||||||
|
|
||||||
* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
|
|
||||||
* [Fix] kqueue: no longer get duplicate CREATE events
|
|
||||||
|
|
||||||
## v0.7.2 / 2012-09-01
|
|
||||||
|
|
||||||
* kqueue: events for created directories
|
|
||||||
|
|
||||||
## v0.7.1 / 2012-07-14
|
|
||||||
|
|
||||||
* [Fix] for renaming files
|
|
||||||
|
|
||||||
## v0.7.0 / 2012-07-02
|
|
||||||
|
|
||||||
* [Feature] FSNotify flags
|
|
||||||
* [Fix] inotify: Added file name back to event path
|
|
||||||
|
|
||||||
## v0.6.0 / 2012-06-06
|
|
||||||
|
|
||||||
* kqueue: watch files after directory created (thanks @tmc)
|
|
||||||
|
|
||||||
## v0.5.1 / 2012-05-22
|
|
||||||
|
|
||||||
* [Fix] inotify: remove all watches before Close()
|
|
||||||
|
|
||||||
## v0.5.0 / 2012-05-03
|
|
||||||
|
|
||||||
* [API] kqueue: return errors during watch instead of sending over channel
|
|
||||||
* kqueue: match symlink behavior on Linux
|
|
||||||
* inotify: add `DELETE_SELF` (requested by @taralx)
|
|
||||||
* [Fix] kqueue: handle EINTR (reported by @robfig)
|
|
||||||
* [Doc] Godoc example [#1][] (thanks @davecheney)
|
|
||||||
|
|
||||||
## v0.4.0 / 2012-03-30
|
|
||||||
|
|
||||||
* Go 1 released: build with go tool
|
|
||||||
* [Feature] Windows support using winfsnotify
|
|
||||||
* Windows does not have attribute change notifications
|
|
||||||
* Roll attribute notifications into IsModify
|
|
||||||
|
|
||||||
## v0.3.0 / 2012-02-19
|
|
||||||
|
|
||||||
* kqueue: add files when watch directory
|
|
||||||
|
|
||||||
## v0.2.0 / 2011-12-30
|
|
||||||
|
|
||||||
* update to latest Go weekly code
|
|
||||||
|
|
||||||
## v0.1.0 / 2011-10-19
|
|
||||||
|
|
||||||
* kqueue: add watch on file creation to match inotify
|
|
||||||
* kqueue: create file event
|
|
||||||
* inotify: ignore `IN_IGNORED` events
|
|
||||||
* event String()
|
|
||||||
* linux: common FileEvent functions
|
|
||||||
* initial commit
|
|
||||||
|
|
||||||
[#79]: https://github.com/howeyc/fsnotify/pull/79
|
|
||||||
[#77]: https://github.com/howeyc/fsnotify/pull/77
|
|
||||||
[#72]: https://github.com/howeyc/fsnotify/issues/72
|
|
||||||
[#71]: https://github.com/howeyc/fsnotify/issues/71
|
|
||||||
[#70]: https://github.com/howeyc/fsnotify/issues/70
|
|
||||||
[#63]: https://github.com/howeyc/fsnotify/issues/63
|
|
||||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
|
|
||||||
[#60]: https://github.com/howeyc/fsnotify/issues/60
|
|
||||||
[#59]: https://github.com/howeyc/fsnotify/issues/59
|
|
||||||
[#49]: https://github.com/howeyc/fsnotify/issues/49
|
|
||||||
[#45]: https://github.com/howeyc/fsnotify/issues/45
|
|
||||||
[#40]: https://github.com/howeyc/fsnotify/issues/40
|
|
||||||
[#36]: https://github.com/howeyc/fsnotify/issues/36
|
|
||||||
[#33]: https://github.com/howeyc/fsnotify/issues/33
|
|
||||||
[#29]: https://github.com/howeyc/fsnotify/issues/29
|
|
||||||
[#25]: https://github.com/howeyc/fsnotify/issues/25
|
|
||||||
[#24]: https://github.com/howeyc/fsnotify/issues/24
|
|
||||||
[#21]: https://github.com/howeyc/fsnotify/issues/21
|
|
||||||
[#1]: https://github.com/howeyc/fsnotify/issues/1
|
|
7
vendor/github.com/howeyc/fsnotify/CONTRIBUTING.md
generated
vendored
7
vendor/github.com/howeyc/fsnotify/CONTRIBUTING.md
generated
vendored
@ -1,7 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
## Moving Notice
|
|
||||||
|
|
||||||
There is a fork being actively developed with a new API in preparation for the Go Standard Library:
|
|
||||||
[github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify)
|
|
||||||
|
|
28
vendor/github.com/howeyc/fsnotify/LICENSE
generated
vendored
28
vendor/github.com/howeyc/fsnotify/LICENSE
generated
vendored
@ -1,28 +0,0 @@
|
|||||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
|
||||||
Copyright (c) 2012 fsnotify Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
93
vendor/github.com/howeyc/fsnotify/README.md
generated
vendored
93
vendor/github.com/howeyc/fsnotify/README.md
generated
vendored
@ -1,93 +0,0 @@
|
|||||||
# File system notifications for Go
|
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/howeyc/fsnotify?status.png)](http://godoc.org/github.com/howeyc/fsnotify)
|
|
||||||
|
|
||||||
Cross platform: Windows, Linux, BSD and OS X.
|
|
||||||
|
|
||||||
## Moving Notice
|
|
||||||
|
|
||||||
There is a fork being actively developed with a new API in preparation for the Go Standard Library:
|
|
||||||
[github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify)
|
|
||||||
|
|
||||||
## Example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/howeyc/fsnotify"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
watcher, err := fsnotify.NewWatcher()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
done := make(chan bool)
|
|
||||||
|
|
||||||
// Process events
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case ev := <-watcher.Event:
|
|
||||||
log.Println("event:", ev)
|
|
||||||
case err := <-watcher.Error:
|
|
||||||
log.Println("error:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = watcher.Watch("testDir")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hang so program doesn't exit
|
|
||||||
<-done
|
|
||||||
|
|
||||||
/* ... do stuff ... */
|
|
||||||
watcher.Close()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For each event:
|
|
||||||
* Name
|
|
||||||
* IsCreate()
|
|
||||||
* IsDelete()
|
|
||||||
* IsModify()
|
|
||||||
* IsRename()
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
**When a file is moved to another directory is it still being watched?**
|
|
||||||
|
|
||||||
No (it shouldn't be, unless you are watching where it was moved to).
|
|
||||||
|
|
||||||
**When I watch a directory, are all subdirectories watched as well?**
|
|
||||||
|
|
||||||
No, you must add watches for any directory you want to watch (a recursive watcher is in the works [#56][]).
|
|
||||||
|
|
||||||
**Do I have to watch the Error and Event channels in a separate goroutine?**
|
|
||||||
|
|
||||||
As of now, yes. Looking into making this single-thread friendly (see [#7][])
|
|
||||||
|
|
||||||
**Why am I receiving multiple events for the same file on OS X?**
|
|
||||||
|
|
||||||
Spotlight indexing on OS X can result in multiple events (see [#62][]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#54][]).
|
|
||||||
|
|
||||||
**How many files can be watched at once?**
|
|
||||||
|
|
||||||
There are OS-specific limits as to how many watches can be created:
|
|
||||||
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit,
|
|
||||||
reaching this limit results in a "no space left on device" error.
|
|
||||||
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
|
|
||||||
|
|
||||||
|
|
||||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
|
|
||||||
[#56]: https://github.com/howeyc/fsnotify/issues/56
|
|
||||||
[#54]: https://github.com/howeyc/fsnotify/issues/54
|
|
||||||
[#7]: https://github.com/howeyc/fsnotify/issues/7
|
|
||||||
|
|
111
vendor/github.com/howeyc/fsnotify/fsnotify.go
generated
vendored
111
vendor/github.com/howeyc/fsnotify/fsnotify.go
generated
vendored
@ -1,111 +0,0 @@
|
|||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package fsnotify implements file system notification.
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
const (
|
|
||||||
FSN_CREATE = 1
|
|
||||||
FSN_MODIFY = 2
|
|
||||||
FSN_DELETE = 4
|
|
||||||
FSN_RENAME = 8
|
|
||||||
|
|
||||||
FSN_ALL = FSN_MODIFY | FSN_DELETE | FSN_RENAME | FSN_CREATE
|
|
||||||
)
|
|
||||||
|
|
||||||
// Purge events from interal chan to external chan if passes filter
|
|
||||||
func (w *Watcher) purgeEvents() {
|
|
||||||
for ev := range w.internalEvent {
|
|
||||||
sendEvent := false
|
|
||||||
w.fsnmut.Lock()
|
|
||||||
fsnFlags := w.fsnFlags[ev.Name]
|
|
||||||
w.fsnmut.Unlock()
|
|
||||||
|
|
||||||
if (fsnFlags&FSN_CREATE == FSN_CREATE) && ev.IsCreate() {
|
|
||||||
sendEvent = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fsnFlags&FSN_MODIFY == FSN_MODIFY) && ev.IsModify() {
|
|
||||||
sendEvent = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fsnFlags&FSN_DELETE == FSN_DELETE) && ev.IsDelete() {
|
|
||||||
sendEvent = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fsnFlags&FSN_RENAME == FSN_RENAME) && ev.IsRename() {
|
|
||||||
sendEvent = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if sendEvent {
|
|
||||||
w.Event <- ev
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's no file, then no more events for user
|
|
||||||
// BSD must keep watch for internal use (watches DELETEs to keep track
|
|
||||||
// what files exist for create events)
|
|
||||||
if ev.IsDelete() {
|
|
||||||
w.fsnmut.Lock()
|
|
||||||
delete(w.fsnFlags, ev.Name)
|
|
||||||
w.fsnmut.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(w.Event)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch a given file path
|
|
||||||
func (w *Watcher) Watch(path string) error {
|
|
||||||
return w.WatchFlags(path, FSN_ALL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch a given file path for a particular set of notifications (FSN_MODIFY etc.)
|
|
||||||
func (w *Watcher) WatchFlags(path string, flags uint32) error {
|
|
||||||
w.fsnmut.Lock()
|
|
||||||
w.fsnFlags[path] = flags
|
|
||||||
w.fsnmut.Unlock()
|
|
||||||
return w.watch(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a watch on a file
|
|
||||||
func (w *Watcher) RemoveWatch(path string) error {
|
|
||||||
w.fsnmut.Lock()
|
|
||||||
delete(w.fsnFlags, path)
|
|
||||||
w.fsnmut.Unlock()
|
|
||||||
return w.removeWatch(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String formats the event e in the form
|
|
||||||
// "filename: DELETE|MODIFY|..."
|
|
||||||
func (e *FileEvent) String() string {
|
|
||||||
var events string = ""
|
|
||||||
|
|
||||||
if e.IsCreate() {
|
|
||||||
events += "|" + "CREATE"
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.IsDelete() {
|
|
||||||
events += "|" + "DELETE"
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.IsModify() {
|
|
||||||
events += "|" + "MODIFY"
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.IsRename() {
|
|
||||||
events += "|" + "RENAME"
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.IsAttrib() {
|
|
||||||
events += "|" + "ATTRIB"
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(events) > 0 {
|
|
||||||
events = events[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%q: %s", e.Name, events)
|
|
||||||
}
|
|
496
vendor/github.com/howeyc/fsnotify/fsnotify_bsd.go
generated
vendored
496
vendor/github.com/howeyc/fsnotify/fsnotify_bsd.go
generated
vendored
@ -1,496 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build freebsd openbsd netbsd dragonfly darwin
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Flags (from <sys/event.h>)
|
|
||||||
sys_NOTE_DELETE = 0x0001 /* vnode was removed */
|
|
||||||
sys_NOTE_WRITE = 0x0002 /* data contents changed */
|
|
||||||
sys_NOTE_EXTEND = 0x0004 /* size increased */
|
|
||||||
sys_NOTE_ATTRIB = 0x0008 /* attributes changed */
|
|
||||||
sys_NOTE_LINK = 0x0010 /* link count changed */
|
|
||||||
sys_NOTE_RENAME = 0x0020 /* vnode was renamed */
|
|
||||||
sys_NOTE_REVOKE = 0x0040 /* vnode access was revoked */
|
|
||||||
|
|
||||||
// Watch all events
|
|
||||||
sys_NOTE_ALLEVENTS = sys_NOTE_DELETE | sys_NOTE_WRITE | sys_NOTE_ATTRIB | sys_NOTE_RENAME
|
|
||||||
|
|
||||||
// Block for 100 ms on each call to kevent
|
|
||||||
keventWaitTime = 100e6
|
|
||||||
)
|
|
||||||
|
|
||||||
type FileEvent struct {
|
|
||||||
mask uint32 // Mask of events
|
|
||||||
Name string // File name (optional)
|
|
||||||
create bool // set by fsnotify package if found new file
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsCreate reports whether the FileEvent was triggered by a creation
|
|
||||||
func (e *FileEvent) IsCreate() bool { return e.create }
|
|
||||||
|
|
||||||
// IsDelete reports whether the FileEvent was triggered by a delete
|
|
||||||
func (e *FileEvent) IsDelete() bool { return (e.mask & sys_NOTE_DELETE) == sys_NOTE_DELETE }
|
|
||||||
|
|
||||||
// IsModify reports whether the FileEvent was triggered by a file modification
|
|
||||||
func (e *FileEvent) IsModify() bool {
|
|
||||||
return ((e.mask&sys_NOTE_WRITE) == sys_NOTE_WRITE || (e.mask&sys_NOTE_ATTRIB) == sys_NOTE_ATTRIB)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRename reports whether the FileEvent was triggered by a change name
|
|
||||||
func (e *FileEvent) IsRename() bool { return (e.mask & sys_NOTE_RENAME) == sys_NOTE_RENAME }
|
|
||||||
|
|
||||||
// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
|
|
||||||
func (e *FileEvent) IsAttrib() bool {
|
|
||||||
return (e.mask & sys_NOTE_ATTRIB) == sys_NOTE_ATTRIB
|
|
||||||
}
|
|
||||||
|
|
||||||
type Watcher struct {
|
|
||||||
mu sync.Mutex // Mutex for the Watcher itself.
|
|
||||||
kq int // File descriptor (as returned by the kqueue() syscall)
|
|
||||||
watches map[string]int // Map of watched file descriptors (key: path)
|
|
||||||
wmut sync.Mutex // Protects access to watches.
|
|
||||||
fsnFlags map[string]uint32 // Map of watched files to flags used for filter
|
|
||||||
fsnmut sync.Mutex // Protects access to fsnFlags.
|
|
||||||
enFlags map[string]uint32 // Map of watched files to evfilt note flags used in kqueue
|
|
||||||
enmut sync.Mutex // Protects access to enFlags.
|
|
||||||
paths map[int]string // Map of watched paths (key: watch descriptor)
|
|
||||||
finfo map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor)
|
|
||||||
pmut sync.Mutex // Protects access to paths and finfo.
|
|
||||||
fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events)
|
|
||||||
femut sync.Mutex // Protects access to fileExists.
|
|
||||||
externalWatches map[string]bool // Map of watches added by user of the library.
|
|
||||||
ewmut sync.Mutex // Protects access to externalWatches.
|
|
||||||
Error chan error // Errors are sent on this channel
|
|
||||||
internalEvent chan *FileEvent // Events are queued on this channel
|
|
||||||
Event chan *FileEvent // Events are returned on this channel
|
|
||||||
done chan bool // Channel for sending a "quit message" to the reader goroutine
|
|
||||||
isClosed bool // Set to true when Close() is first called
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWatcher creates and returns a new kevent instance using kqueue(2)
|
|
||||||
func NewWatcher() (*Watcher, error) {
|
|
||||||
fd, errno := syscall.Kqueue()
|
|
||||||
if fd == -1 {
|
|
||||||
return nil, os.NewSyscallError("kqueue", errno)
|
|
||||||
}
|
|
||||||
w := &Watcher{
|
|
||||||
kq: fd,
|
|
||||||
watches: make(map[string]int),
|
|
||||||
fsnFlags: make(map[string]uint32),
|
|
||||||
enFlags: make(map[string]uint32),
|
|
||||||
paths: make(map[int]string),
|
|
||||||
finfo: make(map[int]os.FileInfo),
|
|
||||||
fileExists: make(map[string]bool),
|
|
||||||
externalWatches: make(map[string]bool),
|
|
||||||
internalEvent: make(chan *FileEvent),
|
|
||||||
Event: make(chan *FileEvent),
|
|
||||||
Error: make(chan error),
|
|
||||||
done: make(chan bool, 1),
|
|
||||||
}
|
|
||||||
|
|
||||||
go w.readEvents()
|
|
||||||
go w.purgeEvents()
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes a kevent watcher instance
|
|
||||||
// It sends a message to the reader goroutine to quit and removes all watches
|
|
||||||
// associated with the kevent instance
|
|
||||||
func (w *Watcher) Close() error {
|
|
||||||
w.mu.Lock()
|
|
||||||
if w.isClosed {
|
|
||||||
w.mu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
w.isClosed = true
|
|
||||||
w.mu.Unlock()
|
|
||||||
|
|
||||||
// Send "quit" message to the reader goroutine
|
|
||||||
w.done <- true
|
|
||||||
w.wmut.Lock()
|
|
||||||
ws := w.watches
|
|
||||||
w.wmut.Unlock()
|
|
||||||
for path := range ws {
|
|
||||||
w.removeWatch(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddWatch adds path to the watched file set.
|
|
||||||
// The flags are interpreted as described in kevent(2).
|
|
||||||
func (w *Watcher) addWatch(path string, flags uint32) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
if w.isClosed {
|
|
||||||
w.mu.Unlock()
|
|
||||||
return errors.New("kevent instance already closed")
|
|
||||||
}
|
|
||||||
w.mu.Unlock()
|
|
||||||
|
|
||||||
watchDir := false
|
|
||||||
|
|
||||||
w.wmut.Lock()
|
|
||||||
watchfd, found := w.watches[path]
|
|
||||||
w.wmut.Unlock()
|
|
||||||
if !found {
|
|
||||||
fi, errstat := os.Lstat(path)
|
|
||||||
if errstat != nil {
|
|
||||||
return errstat
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't watch socket
|
|
||||||
if fi.Mode()&os.ModeSocket == os.ModeSocket {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Follow Symlinks
|
|
||||||
// Unfortunately, Linux can add bogus symlinks to watch list without
|
|
||||||
// issue, and Windows can't do symlinks period (AFAIK). To maintain
|
|
||||||
// consistency, we will act like everything is fine. There will simply
|
|
||||||
// be no file events for broken symlinks.
|
|
||||||
// Hence the returns of nil on errors.
|
|
||||||
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
|
||||||
path, err := filepath.EvalSymlinks(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fi, errstat = os.Lstat(path)
|
|
||||||
if errstat != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fd, errno := syscall.Open(path, open_FLAGS, 0700)
|
|
||||||
if fd == -1 {
|
|
||||||
return errno
|
|
||||||
}
|
|
||||||
watchfd = fd
|
|
||||||
|
|
||||||
w.wmut.Lock()
|
|
||||||
w.watches[path] = watchfd
|
|
||||||
w.wmut.Unlock()
|
|
||||||
|
|
||||||
w.pmut.Lock()
|
|
||||||
w.paths[watchfd] = path
|
|
||||||
w.finfo[watchfd] = fi
|
|
||||||
w.pmut.Unlock()
|
|
||||||
}
|
|
||||||
// Watch the directory if it has not been watched before.
|
|
||||||
w.pmut.Lock()
|
|
||||||
w.enmut.Lock()
|
|
||||||
if w.finfo[watchfd].IsDir() &&
|
|
||||||
(flags&sys_NOTE_WRITE) == sys_NOTE_WRITE &&
|
|
||||||
(!found || (w.enFlags[path]&sys_NOTE_WRITE) != sys_NOTE_WRITE) {
|
|
||||||
watchDir = true
|
|
||||||
}
|
|
||||||
w.enmut.Unlock()
|
|
||||||
w.pmut.Unlock()
|
|
||||||
|
|
||||||
w.enmut.Lock()
|
|
||||||
w.enFlags[path] = flags
|
|
||||||
w.enmut.Unlock()
|
|
||||||
|
|
||||||
var kbuf [1]syscall.Kevent_t
|
|
||||||
watchEntry := &kbuf[0]
|
|
||||||
watchEntry.Fflags = flags
|
|
||||||
syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR)
|
|
||||||
entryFlags := watchEntry.Flags
|
|
||||||
success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil)
|
|
||||||
if success == -1 {
|
|
||||||
return errno
|
|
||||||
} else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR {
|
|
||||||
return errors.New("kevent add error")
|
|
||||||
}
|
|
||||||
|
|
||||||
if watchDir {
|
|
||||||
errdir := w.watchDirectoryFiles(path)
|
|
||||||
if errdir != nil {
|
|
||||||
return errdir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch adds path to the watched file set, watching all events.
|
|
||||||
func (w *Watcher) watch(path string) error {
|
|
||||||
w.ewmut.Lock()
|
|
||||||
w.externalWatches[path] = true
|
|
||||||
w.ewmut.Unlock()
|
|
||||||
return w.addWatch(path, sys_NOTE_ALLEVENTS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveWatch removes path from the watched file set.
|
|
||||||
func (w *Watcher) removeWatch(path string) error {
|
|
||||||
w.wmut.Lock()
|
|
||||||
watchfd, ok := w.watches[path]
|
|
||||||
w.wmut.Unlock()
|
|
||||||
if !ok {
|
|
||||||
return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path))
|
|
||||||
}
|
|
||||||
var kbuf [1]syscall.Kevent_t
|
|
||||||
watchEntry := &kbuf[0]
|
|
||||||
syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE)
|
|
||||||
entryFlags := watchEntry.Flags
|
|
||||||
success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil)
|
|
||||||
if success == -1 {
|
|
||||||
return os.NewSyscallError("kevent_rm_watch", errno)
|
|
||||||
} else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR {
|
|
||||||
return errors.New("kevent rm error")
|
|
||||||
}
|
|
||||||
syscall.Close(watchfd)
|
|
||||||
w.wmut.Lock()
|
|
||||||
delete(w.watches, path)
|
|
||||||
w.wmut.Unlock()
|
|
||||||
w.enmut.Lock()
|
|
||||||
delete(w.enFlags, path)
|
|
||||||
w.enmut.Unlock()
|
|
||||||
w.pmut.Lock()
|
|
||||||
delete(w.paths, watchfd)
|
|
||||||
fInfo := w.finfo[watchfd]
|
|
||||||
delete(w.finfo, watchfd)
|
|
||||||
w.pmut.Unlock()
|
|
||||||
|
|
||||||
// Find all watched paths that are in this directory that are not external.
|
|
||||||
if fInfo.IsDir() {
|
|
||||||
var pathsToRemove []string
|
|
||||||
w.pmut.Lock()
|
|
||||||
for _, wpath := range w.paths {
|
|
||||||
wdir, _ := filepath.Split(wpath)
|
|
||||||
if filepath.Clean(wdir) == filepath.Clean(path) {
|
|
||||||
w.ewmut.Lock()
|
|
||||||
if !w.externalWatches[wpath] {
|
|
||||||
pathsToRemove = append(pathsToRemove, wpath)
|
|
||||||
}
|
|
||||||
w.ewmut.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.pmut.Unlock()
|
|
||||||
for _, p := range pathsToRemove {
|
|
||||||
// Since these are internal, not much sense in propagating error
|
|
||||||
// to the user, as that will just confuse them with an error about
|
|
||||||
// a path they did not explicitly watch themselves.
|
|
||||||
w.removeWatch(p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readEvents reads from the kqueue file descriptor, converts the
|
|
||||||
// received events into Event objects and sends them via the Event channel
|
|
||||||
func (w *Watcher) readEvents() {
|
|
||||||
var (
|
|
||||||
eventbuf [10]syscall.Kevent_t // Event buffer
|
|
||||||
events []syscall.Kevent_t // Received events
|
|
||||||
twait *syscall.Timespec // Time to block waiting for events
|
|
||||||
n int // Number of events returned from kevent
|
|
||||||
errno error // Syscall errno
|
|
||||||
)
|
|
||||||
events = eventbuf[0:0]
|
|
||||||
twait = new(syscall.Timespec)
|
|
||||||
*twait = syscall.NsecToTimespec(keventWaitTime)
|
|
||||||
|
|
||||||
for {
|
|
||||||
// See if there is a message on the "done" channel
|
|
||||||
var done bool
|
|
||||||
select {
|
|
||||||
case done = <-w.done:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
// If "done" message is received
|
|
||||||
if done {
|
|
||||||
errno := syscall.Close(w.kq)
|
|
||||||
if errno != nil {
|
|
||||||
w.Error <- os.NewSyscallError("close", errno)
|
|
||||||
}
|
|
||||||
close(w.internalEvent)
|
|
||||||
close(w.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get new events
|
|
||||||
if len(events) == 0 {
|
|
||||||
n, errno = syscall.Kevent(w.kq, nil, eventbuf[:], twait)
|
|
||||||
|
|
||||||
// EINTR is okay, basically the syscall was interrupted before
|
|
||||||
// timeout expired.
|
|
||||||
if errno != nil && errno != syscall.EINTR {
|
|
||||||
w.Error <- os.NewSyscallError("kevent", errno)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Received some events
|
|
||||||
if n > 0 {
|
|
||||||
events = eventbuf[0:n]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush the events we received to the events channel
|
|
||||||
for len(events) > 0 {
|
|
||||||
fileEvent := new(FileEvent)
|
|
||||||
watchEvent := &events[0]
|
|
||||||
fileEvent.mask = uint32(watchEvent.Fflags)
|
|
||||||
w.pmut.Lock()
|
|
||||||
fileEvent.Name = w.paths[int(watchEvent.Ident)]
|
|
||||||
fileInfo := w.finfo[int(watchEvent.Ident)]
|
|
||||||
w.pmut.Unlock()
|
|
||||||
if fileInfo != nil && fileInfo.IsDir() && !fileEvent.IsDelete() {
|
|
||||||
// Double check to make sure the directory exist. This can happen when
|
|
||||||
// we do a rm -fr on a recursively watched folders and we receive a
|
|
||||||
// modification event first but the folder has been deleted and later
|
|
||||||
// receive the delete event
|
|
||||||
if _, err := os.Lstat(fileEvent.Name); os.IsNotExist(err) {
|
|
||||||
// mark is as delete event
|
|
||||||
fileEvent.mask |= sys_NOTE_DELETE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileInfo != nil && fileInfo.IsDir() && fileEvent.IsModify() && !fileEvent.IsDelete() {
|
|
||||||
w.sendDirectoryChangeEvents(fileEvent.Name)
|
|
||||||
} else {
|
|
||||||
// Send the event on the events channel
|
|
||||||
w.internalEvent <- fileEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to next event
|
|
||||||
events = events[1:]
|
|
||||||
|
|
||||||
if fileEvent.IsRename() {
|
|
||||||
w.removeWatch(fileEvent.Name)
|
|
||||||
w.femut.Lock()
|
|
||||||
delete(w.fileExists, fileEvent.Name)
|
|
||||||
w.femut.Unlock()
|
|
||||||
}
|
|
||||||
if fileEvent.IsDelete() {
|
|
||||||
w.removeWatch(fileEvent.Name)
|
|
||||||
w.femut.Lock()
|
|
||||||
delete(w.fileExists, fileEvent.Name)
|
|
||||||
w.femut.Unlock()
|
|
||||||
|
|
||||||
// Look for a file that may have overwritten this
|
|
||||||
// (ie mv f1 f2 will delete f2 then create f2)
|
|
||||||
fileDir, _ := filepath.Split(fileEvent.Name)
|
|
||||||
fileDir = filepath.Clean(fileDir)
|
|
||||||
w.wmut.Lock()
|
|
||||||
_, found := w.watches[fileDir]
|
|
||||||
w.wmut.Unlock()
|
|
||||||
if found {
|
|
||||||
// make sure the directory exist before we watch for changes. When we
|
|
||||||
// do a recursive watch and perform rm -fr, the parent directory might
|
|
||||||
// have gone missing, ignore the missing directory and let the
|
|
||||||
// upcoming delete event remove the watch form the parent folder
|
|
||||||
if _, err := os.Lstat(fileDir); !os.IsNotExist(err) {
|
|
||||||
w.sendDirectoryChangeEvents(fileDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Watcher) watchDirectoryFiles(dirPath string) error {
|
|
||||||
// Get all files
|
|
||||||
files, err := ioutil.ReadDir(dirPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for new files
|
|
||||||
for _, fileInfo := range files {
|
|
||||||
filePath := filepath.Join(dirPath, fileInfo.Name())
|
|
||||||
|
|
||||||
// Inherit fsnFlags from parent directory
|
|
||||||
w.fsnmut.Lock()
|
|
||||||
if flags, found := w.fsnFlags[dirPath]; found {
|
|
||||||
w.fsnFlags[filePath] = flags
|
|
||||||
} else {
|
|
||||||
w.fsnFlags[filePath] = FSN_ALL
|
|
||||||
}
|
|
||||||
w.fsnmut.Unlock()
|
|
||||||
|
|
||||||
if fileInfo.IsDir() == false {
|
|
||||||
// Watch file to mimic linux fsnotify
|
|
||||||
e := w.addWatch(filePath, sys_NOTE_ALLEVENTS)
|
|
||||||
if e != nil {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If the user is currently watching directory
|
|
||||||
// we want to preserve the flags used
|
|
||||||
w.enmut.Lock()
|
|
||||||
currFlags, found := w.enFlags[filePath]
|
|
||||||
w.enmut.Unlock()
|
|
||||||
var newFlags uint32 = sys_NOTE_DELETE
|
|
||||||
if found {
|
|
||||||
newFlags |= currFlags
|
|
||||||
}
|
|
||||||
|
|
||||||
// Linux gives deletes if not explicitly watching
|
|
||||||
e := w.addWatch(filePath, newFlags)
|
|
||||||
if e != nil {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.femut.Lock()
|
|
||||||
w.fileExists[filePath] = true
|
|
||||||
w.femut.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendDirectoryEvents searches the directory for newly created files
|
|
||||||
// and sends them over the event channel. This functionality is to have
|
|
||||||
// the BSD version of fsnotify match linux fsnotify which provides a
|
|
||||||
// create event for files created in a watched directory.
|
|
||||||
func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
|
|
||||||
// Get all files
|
|
||||||
files, err := ioutil.ReadDir(dirPath)
|
|
||||||
if err != nil {
|
|
||||||
w.Error <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for new files
|
|
||||||
for _, fileInfo := range files {
|
|
||||||
filePath := filepath.Join(dirPath, fileInfo.Name())
|
|
||||||
w.femut.Lock()
|
|
||||||
_, doesExist := w.fileExists[filePath]
|
|
||||||
w.femut.Unlock()
|
|
||||||
if !doesExist {
|
|
||||||
// Inherit fsnFlags from parent directory
|
|
||||||
w.fsnmut.Lock()
|
|
||||||
if flags, found := w.fsnFlags[dirPath]; found {
|
|
||||||
w.fsnFlags[filePath] = flags
|
|
||||||
} else {
|
|
||||||
w.fsnFlags[filePath] = FSN_ALL
|
|
||||||
}
|
|
||||||
w.fsnmut.Unlock()
|
|
||||||
|
|
||||||
// Send create event
|
|
||||||
fileEvent := new(FileEvent)
|
|
||||||
fileEvent.Name = filePath
|
|
||||||
fileEvent.create = true
|
|
||||||
w.internalEvent <- fileEvent
|
|
||||||
}
|
|
||||||
w.femut.Lock()
|
|
||||||
w.fileExists[filePath] = true
|
|
||||||
w.femut.Unlock()
|
|
||||||
}
|
|
||||||
w.watchDirectoryFiles(dirPath)
|
|
||||||
}
|
|
304
vendor/github.com/howeyc/fsnotify/fsnotify_linux.go
generated
vendored
304
vendor/github.com/howeyc/fsnotify/fsnotify_linux.go
generated
vendored
@ -1,304 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Options for inotify_init() are not exported
|
|
||||||
// sys_IN_CLOEXEC uint32 = syscall.IN_CLOEXEC
|
|
||||||
// sys_IN_NONBLOCK uint32 = syscall.IN_NONBLOCK
|
|
||||||
|
|
||||||
// Options for AddWatch
|
|
||||||
sys_IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW
|
|
||||||
sys_IN_ONESHOT uint32 = syscall.IN_ONESHOT
|
|
||||||
sys_IN_ONLYDIR uint32 = syscall.IN_ONLYDIR
|
|
||||||
|
|
||||||
// The "sys_IN_MASK_ADD" option is not exported, as AddWatch
|
|
||||||
// adds it automatically, if there is already a watch for the given path
|
|
||||||
// sys_IN_MASK_ADD uint32 = syscall.IN_MASK_ADD
|
|
||||||
|
|
||||||
// Events
|
|
||||||
sys_IN_ACCESS uint32 = syscall.IN_ACCESS
|
|
||||||
sys_IN_ALL_EVENTS uint32 = syscall.IN_ALL_EVENTS
|
|
||||||
sys_IN_ATTRIB uint32 = syscall.IN_ATTRIB
|
|
||||||
sys_IN_CLOSE uint32 = syscall.IN_CLOSE
|
|
||||||
sys_IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE
|
|
||||||
sys_IN_CLOSE_WRITE uint32 = syscall.IN_CLOSE_WRITE
|
|
||||||
sys_IN_CREATE uint32 = syscall.IN_CREATE
|
|
||||||
sys_IN_DELETE uint32 = syscall.IN_DELETE
|
|
||||||
sys_IN_DELETE_SELF uint32 = syscall.IN_DELETE_SELF
|
|
||||||
sys_IN_MODIFY uint32 = syscall.IN_MODIFY
|
|
||||||
sys_IN_MOVE uint32 = syscall.IN_MOVE
|
|
||||||
sys_IN_MOVED_FROM uint32 = syscall.IN_MOVED_FROM
|
|
||||||
sys_IN_MOVED_TO uint32 = syscall.IN_MOVED_TO
|
|
||||||
sys_IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF
|
|
||||||
sys_IN_OPEN uint32 = syscall.IN_OPEN
|
|
||||||
|
|
||||||
sys_AGNOSTIC_EVENTS = sys_IN_MOVED_TO | sys_IN_MOVED_FROM | sys_IN_CREATE | sys_IN_ATTRIB | sys_IN_MODIFY | sys_IN_MOVE_SELF | sys_IN_DELETE | sys_IN_DELETE_SELF
|
|
||||||
|
|
||||||
// Special events
|
|
||||||
sys_IN_ISDIR uint32 = syscall.IN_ISDIR
|
|
||||||
sys_IN_IGNORED uint32 = syscall.IN_IGNORED
|
|
||||||
sys_IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
|
|
||||||
sys_IN_UNMOUNT uint32 = syscall.IN_UNMOUNT
|
|
||||||
)
|
|
||||||
|
|
||||||
type FileEvent struct {
|
|
||||||
mask uint32 // Mask of events
|
|
||||||
cookie uint32 // Unique cookie associating related events (for rename(2))
|
|
||||||
Name string // File name (optional)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsCreate reports whether the FileEvent was triggered by a creation
|
|
||||||
func (e *FileEvent) IsCreate() bool {
|
|
||||||
return (e.mask&sys_IN_CREATE) == sys_IN_CREATE || (e.mask&sys_IN_MOVED_TO) == sys_IN_MOVED_TO
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDelete reports whether the FileEvent was triggered by a delete
|
|
||||||
func (e *FileEvent) IsDelete() bool {
|
|
||||||
return (e.mask&sys_IN_DELETE_SELF) == sys_IN_DELETE_SELF || (e.mask&sys_IN_DELETE) == sys_IN_DELETE
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsModify reports whether the FileEvent was triggered by a file modification or attribute change
|
|
||||||
func (e *FileEvent) IsModify() bool {
|
|
||||||
return ((e.mask&sys_IN_MODIFY) == sys_IN_MODIFY || (e.mask&sys_IN_ATTRIB) == sys_IN_ATTRIB)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRename reports whether the FileEvent was triggered by a change name
|
|
||||||
func (e *FileEvent) IsRename() bool {
|
|
||||||
return ((e.mask&sys_IN_MOVE_SELF) == sys_IN_MOVE_SELF || (e.mask&sys_IN_MOVED_FROM) == sys_IN_MOVED_FROM)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
|
|
||||||
func (e *FileEvent) IsAttrib() bool {
|
|
||||||
return (e.mask & sys_IN_ATTRIB) == sys_IN_ATTRIB
|
|
||||||
}
|
|
||||||
|
|
||||||
type watch struct {
|
|
||||||
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
|
|
||||||
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Watcher struct {
|
|
||||||
mu sync.Mutex // Map access
|
|
||||||
fd int // File descriptor (as returned by the inotify_init() syscall)
|
|
||||||
watches map[string]*watch // Map of inotify watches (key: path)
|
|
||||||
fsnFlags map[string]uint32 // Map of watched files to flags used for filter
|
|
||||||
fsnmut sync.Mutex // Protects access to fsnFlags.
|
|
||||||
paths map[int]string // Map of watched paths (key: watch descriptor)
|
|
||||||
Error chan error // Errors are sent on this channel
|
|
||||||
internalEvent chan *FileEvent // Events are queued on this channel
|
|
||||||
Event chan *FileEvent // Events are returned on this channel
|
|
||||||
done chan bool // Channel for sending a "quit message" to the reader goroutine
|
|
||||||
isClosed bool // Set to true when Close() is first called
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWatcher creates and returns a new inotify instance using inotify_init(2)
|
|
||||||
func NewWatcher() (*Watcher, error) {
|
|
||||||
fd, errno := syscall.InotifyInit()
|
|
||||||
if fd == -1 {
|
|
||||||
return nil, os.NewSyscallError("inotify_init", errno)
|
|
||||||
}
|
|
||||||
w := &Watcher{
|
|
||||||
fd: fd,
|
|
||||||
watches: make(map[string]*watch),
|
|
||||||
fsnFlags: make(map[string]uint32),
|
|
||||||
paths: make(map[int]string),
|
|
||||||
internalEvent: make(chan *FileEvent),
|
|
||||||
Event: make(chan *FileEvent),
|
|
||||||
Error: make(chan error),
|
|
||||||
done: make(chan bool, 1),
|
|
||||||
}
|
|
||||||
|
|
||||||
go w.readEvents()
|
|
||||||
go w.purgeEvents()
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes an inotify watcher instance
|
|
||||||
// It sends a message to the reader goroutine to quit and removes all watches
|
|
||||||
// associated with the inotify instance
|
|
||||||
func (w *Watcher) Close() error {
|
|
||||||
if w.isClosed {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
w.isClosed = true
|
|
||||||
|
|
||||||
// Remove all watches
|
|
||||||
for path := range w.watches {
|
|
||||||
w.RemoveWatch(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send "quit" message to the reader goroutine
|
|
||||||
w.done <- true
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddWatch adds path to the watched file set.
|
|
||||||
// The flags are interpreted as described in inotify_add_watch(2).
|
|
||||||
func (w *Watcher) addWatch(path string, flags uint32) error {
|
|
||||||
if w.isClosed {
|
|
||||||
return errors.New("inotify instance already closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
w.mu.Lock()
|
|
||||||
watchEntry, found := w.watches[path]
|
|
||||||
w.mu.Unlock()
|
|
||||||
if found {
|
|
||||||
watchEntry.flags |= flags
|
|
||||||
flags |= syscall.IN_MASK_ADD
|
|
||||||
}
|
|
||||||
wd, errno := syscall.InotifyAddWatch(w.fd, path, flags)
|
|
||||||
if wd == -1 {
|
|
||||||
return errno
|
|
||||||
}
|
|
||||||
|
|
||||||
w.mu.Lock()
|
|
||||||
w.watches[path] = &watch{wd: uint32(wd), flags: flags}
|
|
||||||
w.paths[wd] = path
|
|
||||||
w.mu.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch adds path to the watched file set, watching all events.
|
|
||||||
func (w *Watcher) watch(path string) error {
|
|
||||||
return w.addWatch(path, sys_AGNOSTIC_EVENTS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveWatch removes path from the watched file set.
|
|
||||||
func (w *Watcher) removeWatch(path string) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
watch, ok := w.watches[path]
|
|
||||||
if !ok {
|
|
||||||
return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path))
|
|
||||||
}
|
|
||||||
success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
|
|
||||||
if success == -1 {
|
|
||||||
return os.NewSyscallError("inotify_rm_watch", errno)
|
|
||||||
}
|
|
||||||
delete(w.watches, path)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readEvents reads from the inotify file descriptor, converts the
|
|
||||||
// received events into Event objects and sends them via the Event channel
|
|
||||||
func (w *Watcher) readEvents() {
|
|
||||||
var (
|
|
||||||
buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
|
|
||||||
n int // Number of bytes read with read()
|
|
||||||
errno error // Syscall errno
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
// See if there is a message on the "done" channel
|
|
||||||
select {
|
|
||||||
case <-w.done:
|
|
||||||
syscall.Close(w.fd)
|
|
||||||
close(w.internalEvent)
|
|
||||||
close(w.Error)
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
n, errno = syscall.Read(w.fd, buf[:])
|
|
||||||
|
|
||||||
// If EOF is received
|
|
||||||
if n == 0 {
|
|
||||||
syscall.Close(w.fd)
|
|
||||||
close(w.internalEvent)
|
|
||||||
close(w.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if n < 0 {
|
|
||||||
w.Error <- os.NewSyscallError("read", errno)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if n < syscall.SizeofInotifyEvent {
|
|
||||||
w.Error <- errors.New("inotify: short read in readEvents()")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var offset uint32 = 0
|
|
||||||
// We don't know how many events we just read into the buffer
|
|
||||||
// While the offset points to at least one whole event...
|
|
||||||
for offset <= uint32(n-syscall.SizeofInotifyEvent) {
|
|
||||||
// Point "raw" to the event in the buffer
|
|
||||||
raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
|
|
||||||
event := new(FileEvent)
|
|
||||||
event.mask = uint32(raw.Mask)
|
|
||||||
event.cookie = uint32(raw.Cookie)
|
|
||||||
nameLen := uint32(raw.Len)
|
|
||||||
// If the event happened to the watched directory or the watched file, the kernel
|
|
||||||
// doesn't append the filename to the event, but we would like to always fill the
|
|
||||||
// the "Name" field with a valid filename. We retrieve the path of the watch from
|
|
||||||
// the "paths" map.
|
|
||||||
w.mu.Lock()
|
|
||||||
event.Name = w.paths[int(raw.Wd)]
|
|
||||||
w.mu.Unlock()
|
|
||||||
watchedName := event.Name
|
|
||||||
if nameLen > 0 {
|
|
||||||
// Point "bytes" at the first byte of the filename
|
|
||||||
bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
|
|
||||||
// The filename is padded with NUL bytes. TrimRight() gets rid of those.
|
|
||||||
event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the events that are not ignored on the events channel
|
|
||||||
if !event.ignoreLinux() {
|
|
||||||
// Setup FSNotify flags (inherit from directory watch)
|
|
||||||
w.fsnmut.Lock()
|
|
||||||
if _, fsnFound := w.fsnFlags[event.Name]; !fsnFound {
|
|
||||||
if fsnFlags, watchFound := w.fsnFlags[watchedName]; watchFound {
|
|
||||||
w.fsnFlags[event.Name] = fsnFlags
|
|
||||||
} else {
|
|
||||||
w.fsnFlags[event.Name] = FSN_ALL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.fsnmut.Unlock()
|
|
||||||
|
|
||||||
w.internalEvent <- event
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the next event in the buffer
|
|
||||||
offset += syscall.SizeofInotifyEvent + nameLen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Certain types of events can be "ignored" and not sent over the Event
|
|
||||||
// channel. Such as events marked ignore by the kernel, or MODIFY events
|
|
||||||
// against files that do not exist.
|
|
||||||
func (e *FileEvent) ignoreLinux() bool {
|
|
||||||
// Ignore anything the inotify API says to ignore
|
|
||||||
if e.mask&sys_IN_IGNORED == sys_IN_IGNORED {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the event is not a DELETE or RENAME, the file must exist.
|
|
||||||
// Otherwise the event is ignored.
|
|
||||||
// *Note*: this was put in place because it was seen that a MODIFY
|
|
||||||
// event was sent after the DELETE. This ignores that MODIFY and
|
|
||||||
// assumes a DELETE will come or has come if the file doesn't exist.
|
|
||||||
if !(e.IsDelete() || e.IsRename()) {
|
|
||||||
_, statErr := os.Lstat(e.Name)
|
|
||||||
return os.IsNotExist(statErr)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
11
vendor/github.com/howeyc/fsnotify/fsnotify_open_bsd.go
generated
vendored
11
vendor/github.com/howeyc/fsnotify/fsnotify_open_bsd.go
generated
vendored
@ -1,11 +0,0 @@
|
|||||||
// 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 freebsd openbsd netbsd dragonfly
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import "syscall"
|
|
||||||
|
|
||||||
const open_FLAGS = syscall.O_NONBLOCK | syscall.O_RDONLY
|
|
11
vendor/github.com/howeyc/fsnotify/fsnotify_open_darwin.go
generated
vendored
11
vendor/github.com/howeyc/fsnotify/fsnotify_open_darwin.go
generated
vendored
@ -1,11 +0,0 @@
|
|||||||
// 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 darwin
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import "syscall"
|
|
||||||
|
|
||||||
const open_FLAGS = syscall.O_EVTONLY
|
|
598
vendor/github.com/howeyc/fsnotify/fsnotify_windows.go
generated
vendored
598
vendor/github.com/howeyc/fsnotify/fsnotify_windows.go
generated
vendored
@ -1,598 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Options for AddWatch
|
|
||||||
sys_FS_ONESHOT = 0x80000000
|
|
||||||
sys_FS_ONLYDIR = 0x1000000
|
|
||||||
|
|
||||||
// Events
|
|
||||||
sys_FS_ACCESS = 0x1
|
|
||||||
sys_FS_ALL_EVENTS = 0xfff
|
|
||||||
sys_FS_ATTRIB = 0x4
|
|
||||||
sys_FS_CLOSE = 0x18
|
|
||||||
sys_FS_CREATE = 0x100
|
|
||||||
sys_FS_DELETE = 0x200
|
|
||||||
sys_FS_DELETE_SELF = 0x400
|
|
||||||
sys_FS_MODIFY = 0x2
|
|
||||||
sys_FS_MOVE = 0xc0
|
|
||||||
sys_FS_MOVED_FROM = 0x40
|
|
||||||
sys_FS_MOVED_TO = 0x80
|
|
||||||
sys_FS_MOVE_SELF = 0x800
|
|
||||||
|
|
||||||
// Special events
|
|
||||||
sys_FS_IGNORED = 0x8000
|
|
||||||
sys_FS_Q_OVERFLOW = 0x4000
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// TODO(nj): Use syscall.ERROR_MORE_DATA from ztypes_windows in Go 1.3+
|
|
||||||
sys_ERROR_MORE_DATA syscall.Errno = 234
|
|
||||||
)
|
|
||||||
|
|
||||||
// Event is the type of the notification messages
|
|
||||||
// received on the watcher's Event channel.
|
|
||||||
type FileEvent struct {
|
|
||||||
mask uint32 // Mask of events
|
|
||||||
cookie uint32 // Unique cookie associating related events (for rename)
|
|
||||||
Name string // File name (optional)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsCreate reports whether the FileEvent was triggered by a creation
|
|
||||||
func (e *FileEvent) IsCreate() bool { return (e.mask & sys_FS_CREATE) == sys_FS_CREATE }
|
|
||||||
|
|
||||||
// IsDelete reports whether the FileEvent was triggered by a delete
|
|
||||||
func (e *FileEvent) IsDelete() bool {
|
|
||||||
return ((e.mask&sys_FS_DELETE) == sys_FS_DELETE || (e.mask&sys_FS_DELETE_SELF) == sys_FS_DELETE_SELF)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsModify reports whether the FileEvent was triggered by a file modification or attribute change
|
|
||||||
func (e *FileEvent) IsModify() bool {
|
|
||||||
return ((e.mask&sys_FS_MODIFY) == sys_FS_MODIFY || (e.mask&sys_FS_ATTRIB) == sys_FS_ATTRIB)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRename reports whether the FileEvent was triggered by a change name
|
|
||||||
func (e *FileEvent) IsRename() bool {
|
|
||||||
return ((e.mask&sys_FS_MOVE) == sys_FS_MOVE || (e.mask&sys_FS_MOVE_SELF) == sys_FS_MOVE_SELF || (e.mask&sys_FS_MOVED_FROM) == sys_FS_MOVED_FROM || (e.mask&sys_FS_MOVED_TO) == sys_FS_MOVED_TO)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
|
|
||||||
func (e *FileEvent) IsAttrib() bool {
|
|
||||||
return (e.mask & sys_FS_ATTRIB) == sys_FS_ATTRIB
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
opAddWatch = iota
|
|
||||||
opRemoveWatch
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
provisional uint64 = 1 << (32 + iota)
|
|
||||||
)
|
|
||||||
|
|
||||||
type input struct {
|
|
||||||
op int
|
|
||||||
path string
|
|
||||||
flags uint32
|
|
||||||
reply chan error
|
|
||||||
}
|
|
||||||
|
|
||||||
type inode struct {
|
|
||||||
handle syscall.Handle
|
|
||||||
volume uint32
|
|
||||||
index uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type watch struct {
|
|
||||||
ov syscall.Overlapped
|
|
||||||
ino *inode // i-number
|
|
||||||
path string // Directory path
|
|
||||||
mask uint64 // Directory itself is being watched with these notify flags
|
|
||||||
names map[string]uint64 // Map of names being watched and their notify flags
|
|
||||||
rename string // Remembers the old name while renaming a file
|
|
||||||
buf [4096]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type indexMap map[uint64]*watch
|
|
||||||
type watchMap map[uint32]indexMap
|
|
||||||
|
|
||||||
// A Watcher waits for and receives event notifications
|
|
||||||
// for a specific set of files and directories.
|
|
||||||
type Watcher struct {
|
|
||||||
mu sync.Mutex // Map access
|
|
||||||
port syscall.Handle // Handle to completion port
|
|
||||||
watches watchMap // Map of watches (key: i-number)
|
|
||||||
fsnFlags map[string]uint32 // Map of watched files to flags used for filter
|
|
||||||
fsnmut sync.Mutex // Protects access to fsnFlags.
|
|
||||||
input chan *input // Inputs to the reader are sent on this channel
|
|
||||||
internalEvent chan *FileEvent // Events are queued on this channel
|
|
||||||
Event chan *FileEvent // Events are returned on this channel
|
|
||||||
Error chan error // Errors are sent on this channel
|
|
||||||
isClosed bool // Set to true when Close() is first called
|
|
||||||
quit chan chan<- error
|
|
||||||
cookie uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWatcher creates and returns a Watcher.
|
|
||||||
func NewWatcher() (*Watcher, error) {
|
|
||||||
port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
|
|
||||||
if e != nil {
|
|
||||||
return nil, os.NewSyscallError("CreateIoCompletionPort", e)
|
|
||||||
}
|
|
||||||
w := &Watcher{
|
|
||||||
port: port,
|
|
||||||
watches: make(watchMap),
|
|
||||||
fsnFlags: make(map[string]uint32),
|
|
||||||
input: make(chan *input, 1),
|
|
||||||
Event: make(chan *FileEvent, 50),
|
|
||||||
internalEvent: make(chan *FileEvent),
|
|
||||||
Error: make(chan error),
|
|
||||||
quit: make(chan chan<- error, 1),
|
|
||||||
}
|
|
||||||
go w.readEvents()
|
|
||||||
go w.purgeEvents()
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes a Watcher.
|
|
||||||
// It sends a message to the reader goroutine to quit and removes all watches
|
|
||||||
// associated with the watcher.
|
|
||||||
func (w *Watcher) Close() error {
|
|
||||||
if w.isClosed {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
w.isClosed = true
|
|
||||||
|
|
||||||
// Send "quit" message to the reader goroutine
|
|
||||||
ch := make(chan error)
|
|
||||||
w.quit <- ch
|
|
||||||
if err := w.wakeupReader(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return <-ch
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddWatch adds path to the watched file set.
|
|
||||||
func (w *Watcher) AddWatch(path string, flags uint32) error {
|
|
||||||
if w.isClosed {
|
|
||||||
return errors.New("watcher already closed")
|
|
||||||
}
|
|
||||||
in := &input{
|
|
||||||
op: opAddWatch,
|
|
||||||
path: filepath.Clean(path),
|
|
||||||
flags: flags,
|
|
||||||
reply: make(chan error),
|
|
||||||
}
|
|
||||||
w.input <- in
|
|
||||||
if err := w.wakeupReader(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return <-in.reply
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch adds path to the watched file set, watching all events.
|
|
||||||
func (w *Watcher) watch(path string) error {
|
|
||||||
return w.AddWatch(path, sys_FS_ALL_EVENTS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveWatch removes path from the watched file set.
|
|
||||||
func (w *Watcher) removeWatch(path string) error {
|
|
||||||
in := &input{
|
|
||||||
op: opRemoveWatch,
|
|
||||||
path: filepath.Clean(path),
|
|
||||||
reply: make(chan error),
|
|
||||||
}
|
|
||||||
w.input <- in
|
|
||||||
if err := w.wakeupReader(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return <-in.reply
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Watcher) wakeupReader() error {
|
|
||||||
e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
|
|
||||||
if e != nil {
|
|
||||||
return os.NewSyscallError("PostQueuedCompletionStatus", e)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDir(pathname string) (dir string, err error) {
|
|
||||||
attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
|
|
||||||
if e != nil {
|
|
||||||
return "", os.NewSyscallError("GetFileAttributes", e)
|
|
||||||
}
|
|
||||||
if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
|
||||||
dir = pathname
|
|
||||||
} else {
|
|
||||||
dir, _ = filepath.Split(pathname)
|
|
||||||
dir = filepath.Clean(dir)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIno(path string) (ino *inode, err error) {
|
|
||||||
h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
|
|
||||||
syscall.FILE_LIST_DIRECTORY,
|
|
||||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
|
||||||
nil, syscall.OPEN_EXISTING,
|
|
||||||
syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
|
|
||||||
if e != nil {
|
|
||||||
return nil, os.NewSyscallError("CreateFile", e)
|
|
||||||
}
|
|
||||||
var fi syscall.ByHandleFileInformation
|
|
||||||
if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
|
|
||||||
syscall.CloseHandle(h)
|
|
||||||
return nil, os.NewSyscallError("GetFileInformationByHandle", e)
|
|
||||||
}
|
|
||||||
ino = &inode{
|
|
||||||
handle: h,
|
|
||||||
volume: fi.VolumeSerialNumber,
|
|
||||||
index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
|
|
||||||
}
|
|
||||||
return ino, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (m watchMap) get(ino *inode) *watch {
|
|
||||||
if i := m[ino.volume]; i != nil {
|
|
||||||
return i[ino.index]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (m watchMap) set(ino *inode, watch *watch) {
|
|
||||||
i := m[ino.volume]
|
|
||||||
if i == nil {
|
|
||||||
i = make(indexMap)
|
|
||||||
m[ino.volume] = i
|
|
||||||
}
|
|
||||||
i[ino.index] = watch
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (w *Watcher) addWatch(pathname string, flags uint64) error {
|
|
||||||
dir, err := getDir(pathname)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if flags&sys_FS_ONLYDIR != 0 && pathname != dir {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ino, err := getIno(dir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.mu.Lock()
|
|
||||||
watchEntry := w.watches.get(ino)
|
|
||||||
w.mu.Unlock()
|
|
||||||
if watchEntry == nil {
|
|
||||||
if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
|
|
||||||
syscall.CloseHandle(ino.handle)
|
|
||||||
return os.NewSyscallError("CreateIoCompletionPort", e)
|
|
||||||
}
|
|
||||||
watchEntry = &watch{
|
|
||||||
ino: ino,
|
|
||||||
path: dir,
|
|
||||||
names: make(map[string]uint64),
|
|
||||||
}
|
|
||||||
w.mu.Lock()
|
|
||||||
w.watches.set(ino, watchEntry)
|
|
||||||
w.mu.Unlock()
|
|
||||||
flags |= provisional
|
|
||||||
} else {
|
|
||||||
syscall.CloseHandle(ino.handle)
|
|
||||||
}
|
|
||||||
if pathname == dir {
|
|
||||||
watchEntry.mask |= flags
|
|
||||||
} else {
|
|
||||||
watchEntry.names[filepath.Base(pathname)] |= flags
|
|
||||||
}
|
|
||||||
if err = w.startRead(watchEntry); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if pathname == dir {
|
|
||||||
watchEntry.mask &= ^provisional
|
|
||||||
} else {
|
|
||||||
watchEntry.names[filepath.Base(pathname)] &= ^provisional
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (w *Watcher) remWatch(pathname string) error {
|
|
||||||
dir, err := getDir(pathname)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ino, err := getIno(dir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.mu.Lock()
|
|
||||||
watch := w.watches.get(ino)
|
|
||||||
w.mu.Unlock()
|
|
||||||
if watch == nil {
|
|
||||||
return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
|
|
||||||
}
|
|
||||||
if pathname == dir {
|
|
||||||
w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED)
|
|
||||||
watch.mask = 0
|
|
||||||
} else {
|
|
||||||
name := filepath.Base(pathname)
|
|
||||||
w.sendEvent(watch.path+"\\"+name, watch.names[name]&sys_FS_IGNORED)
|
|
||||||
delete(watch.names, name)
|
|
||||||
}
|
|
||||||
return w.startRead(watch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (w *Watcher) deleteWatch(watch *watch) {
|
|
||||||
for name, mask := range watch.names {
|
|
||||||
if mask&provisional == 0 {
|
|
||||||
w.sendEvent(watch.path+"\\"+name, mask&sys_FS_IGNORED)
|
|
||||||
}
|
|
||||||
delete(watch.names, name)
|
|
||||||
}
|
|
||||||
if watch.mask != 0 {
|
|
||||||
if watch.mask&provisional == 0 {
|
|
||||||
w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED)
|
|
||||||
}
|
|
||||||
watch.mask = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (w *Watcher) startRead(watch *watch) error {
|
|
||||||
if e := syscall.CancelIo(watch.ino.handle); e != nil {
|
|
||||||
w.Error <- os.NewSyscallError("CancelIo", e)
|
|
||||||
w.deleteWatch(watch)
|
|
||||||
}
|
|
||||||
mask := toWindowsFlags(watch.mask)
|
|
||||||
for _, m := range watch.names {
|
|
||||||
mask |= toWindowsFlags(m)
|
|
||||||
}
|
|
||||||
if mask == 0 {
|
|
||||||
if e := syscall.CloseHandle(watch.ino.handle); e != nil {
|
|
||||||
w.Error <- os.NewSyscallError("CloseHandle", e)
|
|
||||||
}
|
|
||||||
w.mu.Lock()
|
|
||||||
delete(w.watches[watch.ino.volume], watch.ino.index)
|
|
||||||
w.mu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
|
|
||||||
uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
|
|
||||||
if e != nil {
|
|
||||||
err := os.NewSyscallError("ReadDirectoryChanges", e)
|
|
||||||
if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
|
|
||||||
// Watched directory was probably removed
|
|
||||||
if w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) {
|
|
||||||
if watch.mask&sys_FS_ONESHOT != 0 {
|
|
||||||
watch.mask = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
w.deleteWatch(watch)
|
|
||||||
w.startRead(watch)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readEvents reads from the I/O completion port, converts the
|
|
||||||
// received events into Event objects and sends them via the Event channel.
|
|
||||||
// Entry point to the I/O thread.
|
|
||||||
func (w *Watcher) readEvents() {
|
|
||||||
var (
|
|
||||||
n, key uint32
|
|
||||||
ov *syscall.Overlapped
|
|
||||||
)
|
|
||||||
runtime.LockOSThread()
|
|
||||||
|
|
||||||
for {
|
|
||||||
e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
|
|
||||||
watch := (*watch)(unsafe.Pointer(ov))
|
|
||||||
|
|
||||||
if watch == nil {
|
|
||||||
select {
|
|
||||||
case ch := <-w.quit:
|
|
||||||
w.mu.Lock()
|
|
||||||
var indexes []indexMap
|
|
||||||
for _, index := range w.watches {
|
|
||||||
indexes = append(indexes, index)
|
|
||||||
}
|
|
||||||
w.mu.Unlock()
|
|
||||||
for _, index := range indexes {
|
|
||||||
for _, watch := range index {
|
|
||||||
w.deleteWatch(watch)
|
|
||||||
w.startRead(watch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
if e := syscall.CloseHandle(w.port); e != nil {
|
|
||||||
err = os.NewSyscallError("CloseHandle", e)
|
|
||||||
}
|
|
||||||
close(w.internalEvent)
|
|
||||||
close(w.Error)
|
|
||||||
ch <- err
|
|
||||||
return
|
|
||||||
case in := <-w.input:
|
|
||||||
switch in.op {
|
|
||||||
case opAddWatch:
|
|
||||||
in.reply <- w.addWatch(in.path, uint64(in.flags))
|
|
||||||
case opRemoveWatch:
|
|
||||||
in.reply <- w.remWatch(in.path)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch e {
|
|
||||||
case sys_ERROR_MORE_DATA:
|
|
||||||
if watch == nil {
|
|
||||||
w.Error <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
|
|
||||||
} else {
|
|
||||||
// The i/o succeeded but the buffer is full.
|
|
||||||
// In theory we should be building up a full packet.
|
|
||||||
// In practice we can get away with just carrying on.
|
|
||||||
n = uint32(unsafe.Sizeof(watch.buf))
|
|
||||||
}
|
|
||||||
case syscall.ERROR_ACCESS_DENIED:
|
|
||||||
// Watched directory was probably removed
|
|
||||||
w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF)
|
|
||||||
w.deleteWatch(watch)
|
|
||||||
w.startRead(watch)
|
|
||||||
continue
|
|
||||||
case syscall.ERROR_OPERATION_ABORTED:
|
|
||||||
// CancelIo was called on this handle
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e)
|
|
||||||
continue
|
|
||||||
case nil:
|
|
||||||
}
|
|
||||||
|
|
||||||
var offset uint32
|
|
||||||
for {
|
|
||||||
if n == 0 {
|
|
||||||
w.internalEvent <- &FileEvent{mask: sys_FS_Q_OVERFLOW}
|
|
||||||
w.Error <- errors.New("short read in readEvents()")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point "raw" to the event in the buffer
|
|
||||||
raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
|
|
||||||
buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
|
|
||||||
name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
|
|
||||||
fullname := watch.path + "\\" + name
|
|
||||||
|
|
||||||
var mask uint64
|
|
||||||
switch raw.Action {
|
|
||||||
case syscall.FILE_ACTION_REMOVED:
|
|
||||||
mask = sys_FS_DELETE_SELF
|
|
||||||
case syscall.FILE_ACTION_MODIFIED:
|
|
||||||
mask = sys_FS_MODIFY
|
|
||||||
case syscall.FILE_ACTION_RENAMED_OLD_NAME:
|
|
||||||
watch.rename = name
|
|
||||||
case syscall.FILE_ACTION_RENAMED_NEW_NAME:
|
|
||||||
if watch.names[watch.rename] != 0 {
|
|
||||||
watch.names[name] |= watch.names[watch.rename]
|
|
||||||
delete(watch.names, watch.rename)
|
|
||||||
mask = sys_FS_MOVE_SELF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sendNameEvent := func() {
|
|
||||||
if w.sendEvent(fullname, watch.names[name]&mask) {
|
|
||||||
if watch.names[name]&sys_FS_ONESHOT != 0 {
|
|
||||||
delete(watch.names, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
|
|
||||||
sendNameEvent()
|
|
||||||
}
|
|
||||||
if raw.Action == syscall.FILE_ACTION_REMOVED {
|
|
||||||
w.sendEvent(fullname, watch.names[name]&sys_FS_IGNORED)
|
|
||||||
delete(watch.names, name)
|
|
||||||
}
|
|
||||||
if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
|
|
||||||
if watch.mask&sys_FS_ONESHOT != 0 {
|
|
||||||
watch.mask = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
|
|
||||||
fullname = watch.path + "\\" + watch.rename
|
|
||||||
sendNameEvent()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the next event in the buffer
|
|
||||||
if raw.NextEntryOffset == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
offset += raw.NextEntryOffset
|
|
||||||
|
|
||||||
// Error!
|
|
||||||
if offset >= n {
|
|
||||||
w.Error <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.startRead(watch); err != nil {
|
|
||||||
w.Error <- err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Watcher) sendEvent(name string, mask uint64) bool {
|
|
||||||
if mask == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
event := &FileEvent{mask: uint32(mask), Name: name}
|
|
||||||
if mask&sys_FS_MOVE != 0 {
|
|
||||||
if mask&sys_FS_MOVED_FROM != 0 {
|
|
||||||
w.cookie++
|
|
||||||
}
|
|
||||||
event.cookie = w.cookie
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case ch := <-w.quit:
|
|
||||||
w.quit <- ch
|
|
||||||
case w.Event <- event:
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func toWindowsFlags(mask uint64) uint32 {
|
|
||||||
var m uint32
|
|
||||||
if mask&sys_FS_ACCESS != 0 {
|
|
||||||
m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
|
|
||||||
}
|
|
||||||
if mask&sys_FS_MODIFY != 0 {
|
|
||||||
m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
|
|
||||||
}
|
|
||||||
if mask&sys_FS_ATTRIB != 0 {
|
|
||||||
m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
|
|
||||||
}
|
|
||||||
if mask&(sys_FS_MOVE|sys_FS_CREATE|sys_FS_DELETE) != 0 {
|
|
||||||
m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func toFSnotifyFlags(action uint32) uint64 {
|
|
||||||
switch action {
|
|
||||||
case syscall.FILE_ACTION_ADDED:
|
|
||||||
return sys_FS_CREATE
|
|
||||||
case syscall.FILE_ACTION_REMOVED:
|
|
||||||
return sys_FS_DELETE
|
|
||||||
case syscall.FILE_ACTION_MODIFIED:
|
|
||||||
return sys_FS_MODIFY
|
|
||||||
case syscall.FILE_ACTION_RENAMED_OLD_NAME:
|
|
||||||
return sys_FS_MOVED_FROM
|
|
||||||
case syscall.FILE_ACTION_RENAMED_NEW_NAME:
|
|
||||||
return sys_FS_MOVED_TO
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
18
vendor/github.com/jtolds/gls/LICENSE
generated
vendored
18
vendor/github.com/jtolds/gls/LICENSE
generated
vendored
@ -1,18 +0,0 @@
|
|||||||
Copyright (c) 2013, Space Monkey, Inc.
|
|
||||||
|
|
||||||
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.
|
|
89
vendor/github.com/jtolds/gls/README.md
generated
vendored
89
vendor/github.com/jtolds/gls/README.md
generated
vendored
@ -1,89 +0,0 @@
|
|||||||
gls
|
|
||||||
===
|
|
||||||
|
|
||||||
Goroutine local storage
|
|
||||||
|
|
||||||
### IMPORTANT NOTE ###
|
|
||||||
|
|
||||||
It is my duty to point you to https://blog.golang.org/context, which is how
|
|
||||||
Google solves all of the problems you'd perhaps consider using this package
|
|
||||||
for at scale.
|
|
||||||
|
|
||||||
One downside to Google's approach is that *all* of your functions must have
|
|
||||||
a new first argument, but after clearing that hurdle everything else is much
|
|
||||||
better.
|
|
||||||
|
|
||||||
If you aren't interested in this warning, read on.
|
|
||||||
|
|
||||||
### Huhwaht? Why? ###
|
|
||||||
|
|
||||||
Every so often, a thread shows up on the
|
|
||||||
[golang-nuts](https://groups.google.com/d/forum/golang-nuts) asking for some
|
|
||||||
form of goroutine-local-storage, or some kind of goroutine id, or some kind of
|
|
||||||
context. There are a few valid use cases for goroutine-local-storage, one of
|
|
||||||
the most prominent being log line context. One poster was interested in being
|
|
||||||
able to log an HTTP request context id in every log line in the same goroutine
|
|
||||||
as the incoming HTTP request, without having to change every library and
|
|
||||||
function call he was interested in logging.
|
|
||||||
|
|
||||||
This would be pretty useful. Provided that you could get some kind of
|
|
||||||
goroutine-local-storage, you could call
|
|
||||||
[log.SetOutput](http://golang.org/pkg/log/#SetOutput) with your own logging
|
|
||||||
writer that checks goroutine-local-storage for some context information and
|
|
||||||
adds that context to your log lines.
|
|
||||||
|
|
||||||
But alas, Andrew Gerrand's typically diplomatic answer to the question of
|
|
||||||
goroutine-local variables was:
|
|
||||||
|
|
||||||
> We wouldn't even be having this discussion if thread local storage wasn't
|
|
||||||
> useful. But every feature comes at a cost, and in my opinion the cost of
|
|
||||||
> threadlocals far outweighs their benefits. They're just not a good fit for
|
|
||||||
> Go.
|
|
||||||
|
|
||||||
So, yeah, that makes sense. That's a pretty good reason for why the language
|
|
||||||
won't support a specific and (relatively) unuseful feature that requires some
|
|
||||||
runtime changes, just for the sake of a little bit of log improvement.
|
|
||||||
|
|
||||||
But does Go require runtime changes?
|
|
||||||
|
|
||||||
### How it works ###
|
|
||||||
|
|
||||||
Go has pretty fantastic introspective and reflective features, but one thing Go
|
|
||||||
doesn't give you is any kind of access to the stack pointer, or frame pointer,
|
|
||||||
or goroutine id, or anything contextual about your current stack. It gives you
|
|
||||||
access to your list of callers, but only along with program counters, which are
|
|
||||||
fixed at compile time.
|
|
||||||
|
|
||||||
But it does give you the stack.
|
|
||||||
|
|
||||||
So, we define 16 special functions and embed base-16 tags into the stack using
|
|
||||||
the call order of those 16 functions. Then, we can read our tags back out of
|
|
||||||
the stack looking at the callers list.
|
|
||||||
|
|
||||||
We then use these tags as an index into a traditional map for implementing
|
|
||||||
this library.
|
|
||||||
|
|
||||||
### What are people saying? ###
|
|
||||||
|
|
||||||
"Wow, that's horrifying."
|
|
||||||
|
|
||||||
"This is the most terrible thing I have seen in a very long time."
|
|
||||||
|
|
||||||
"Where is it getting a context from? Is this serializing all the requests?
|
|
||||||
What the heck is the client being bound to? What are these tags? Why does he
|
|
||||||
need callers? Oh god no. No no no."
|
|
||||||
|
|
||||||
### Docs ###
|
|
||||||
|
|
||||||
Please see the docs at http://godoc.org/github.com/jtolds/gls
|
|
||||||
|
|
||||||
### Related ###
|
|
||||||
|
|
||||||
If you're okay relying on the string format of the current runtime stacktrace
|
|
||||||
including a unique goroutine id (not guaranteed by the spec or anything, but
|
|
||||||
very unlikely to change within a Go release), you might be able to squeeze
|
|
||||||
out a bit more performance by using this similar library, inspired by some
|
|
||||||
code Brad Fitzpatrick wrote for debugging his HTTP/2 library:
|
|
||||||
https://github.com/tylerb/gls (in contrast, jtolds/gls doesn't require
|
|
||||||
any knowledge of the string format of the runtime stacktrace, which
|
|
||||||
probably adds unnecessary overhead).
|
|
144
vendor/github.com/jtolds/gls/context.go
generated
vendored
144
vendor/github.com/jtolds/gls/context.go
generated
vendored
@ -1,144 +0,0 @@
|
|||||||
// Package gls implements goroutine-local storage.
|
|
||||||
package gls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxCallers = 64
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
stackTagPool = &idPool{}
|
|
||||||
mgrRegistry = make(map[*ContextManager]bool)
|
|
||||||
mgrRegistryMtx sync.RWMutex
|
|
||||||
)
|
|
||||||
|
|
||||||
// Values is simply a map of key types to value types. Used by SetValues to
|
|
||||||
// set multiple values at once.
|
|
||||||
type Values map[interface{}]interface{}
|
|
||||||
|
|
||||||
// ContextManager is the main entrypoint for interacting with
|
|
||||||
// Goroutine-local-storage. You can have multiple independent ContextManagers
|
|
||||||
// at any given time. ContextManagers are usually declared globally for a given
|
|
||||||
// class of context variables. You should use NewContextManager for
|
|
||||||
// construction.
|
|
||||||
type ContextManager struct {
|
|
||||||
mtx sync.RWMutex
|
|
||||||
values map[uint]Values
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContextManager returns a brand new ContextManager. It also registers the
|
|
||||||
// new ContextManager in the ContextManager registry which is used by the Go
|
|
||||||
// method. ContextManagers are typically defined globally at package scope.
|
|
||||||
func NewContextManager() *ContextManager {
|
|
||||||
mgr := &ContextManager{values: make(map[uint]Values)}
|
|
||||||
mgrRegistryMtx.Lock()
|
|
||||||
defer mgrRegistryMtx.Unlock()
|
|
||||||
mgrRegistry[mgr] = true
|
|
||||||
return mgr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unregister removes a ContextManager from the global registry, used by the
|
|
||||||
// Go method. Only intended for use when you're completely done with a
|
|
||||||
// ContextManager. Use of Unregister at all is rare.
|
|
||||||
func (m *ContextManager) Unregister() {
|
|
||||||
mgrRegistryMtx.Lock()
|
|
||||||
defer mgrRegistryMtx.Unlock()
|
|
||||||
delete(mgrRegistry, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetValues takes a collection of values and a function to call for those
|
|
||||||
// values to be set in. Anything further down the stack will have the set
|
|
||||||
// values available through GetValue. SetValues will add new values or replace
|
|
||||||
// existing values of the same key and will not mutate or change values for
|
|
||||||
// previous stack frames.
|
|
||||||
// SetValues is slow (makes a copy of all current and new values for the new
|
|
||||||
// gls-context) in order to reduce the amount of lookups GetValue requires.
|
|
||||||
func (m *ContextManager) SetValues(new_values Values, context_call func()) {
|
|
||||||
if len(new_values) == 0 {
|
|
||||||
context_call()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tags := readStackTags(1)
|
|
||||||
|
|
||||||
m.mtx.Lock()
|
|
||||||
values := new_values
|
|
||||||
for _, tag := range tags {
|
|
||||||
if existing_values, ok := m.values[tag]; ok {
|
|
||||||
// oh, we found existing values, let's make a copy
|
|
||||||
values = make(Values, len(existing_values)+len(new_values))
|
|
||||||
for key, val := range existing_values {
|
|
||||||
values[key] = val
|
|
||||||
}
|
|
||||||
for key, val := range new_values {
|
|
||||||
values[key] = val
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new_tag := stackTagPool.Acquire()
|
|
||||||
m.values[new_tag] = values
|
|
||||||
m.mtx.Unlock()
|
|
||||||
defer func() {
|
|
||||||
m.mtx.Lock()
|
|
||||||
delete(m.values, new_tag)
|
|
||||||
m.mtx.Unlock()
|
|
||||||
stackTagPool.Release(new_tag)
|
|
||||||
}()
|
|
||||||
|
|
||||||
addStackTag(new_tag, context_call)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetValue will return a previously set value, provided that the value was set
|
|
||||||
// by SetValues somewhere higher up the stack. If the value is not found, ok
|
|
||||||
// will be false.
|
|
||||||
func (m *ContextManager) GetValue(key interface{}) (value interface{}, ok bool) {
|
|
||||||
|
|
||||||
tags := readStackTags(1)
|
|
||||||
m.mtx.RLock()
|
|
||||||
defer m.mtx.RUnlock()
|
|
||||||
for _, tag := range tags {
|
|
||||||
if values, ok := m.values[tag]; ok {
|
|
||||||
value, ok := values[key]
|
|
||||||
return value, ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ContextManager) getValues() Values {
|
|
||||||
tags := readStackTags(2)
|
|
||||||
m.mtx.RLock()
|
|
||||||
defer m.mtx.RUnlock()
|
|
||||||
for _, tag := range tags {
|
|
||||||
if values, ok := m.values[tag]; ok {
|
|
||||||
return values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go preserves ContextManager values and Goroutine-local-storage across new
|
|
||||||
// goroutine invocations. The Go method makes a copy of all existing values on
|
|
||||||
// all registered context managers and makes sure they are still set after
|
|
||||||
// kicking off the provided function in a new goroutine. If you don't use this
|
|
||||||
// Go method instead of the standard 'go' keyword, you will lose values in
|
|
||||||
// ContextManagers, as goroutines have brand new stacks.
|
|
||||||
func Go(cb func()) {
|
|
||||||
mgrRegistryMtx.RLock()
|
|
||||||
defer mgrRegistryMtx.RUnlock()
|
|
||||||
|
|
||||||
for mgr, _ := range mgrRegistry {
|
|
||||||
values := mgr.getValues()
|
|
||||||
if len(values) > 0 {
|
|
||||||
mgr_copy := mgr
|
|
||||||
cb_copy := cb
|
|
||||||
cb = func() { mgr_copy.SetValues(values, cb_copy) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
go cb()
|
|
||||||
}
|
|
13
vendor/github.com/jtolds/gls/gen_sym.go
generated
vendored
13
vendor/github.com/jtolds/gls/gen_sym.go
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
package gls
|
|
||||||
|
|
||||||
var (
|
|
||||||
symPool = &idPool{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContextKey is a throwaway value you can use as a key to a ContextManager
|
|
||||||
type ContextKey struct{ id uint }
|
|
||||||
|
|
||||||
// GenSym will return a brand new, never-before-used ContextKey
|
|
||||||
func GenSym() ContextKey {
|
|
||||||
return ContextKey{id: symPool.Acquire()}
|
|
||||||
}
|
|
34
vendor/github.com/jtolds/gls/id_pool.go
generated
vendored
34
vendor/github.com/jtolds/gls/id_pool.go
generated
vendored
@ -1,34 +0,0 @@
|
|||||||
package gls
|
|
||||||
|
|
||||||
// though this could probably be better at keeping ids smaller, the goal of
|
|
||||||
// this class is to keep a registry of the smallest unique integer ids
|
|
||||||
// per-process possible
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type idPool struct {
|
|
||||||
mtx sync.Mutex
|
|
||||||
released []uint
|
|
||||||
max_id uint
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *idPool) Acquire() (id uint) {
|
|
||||||
p.mtx.Lock()
|
|
||||||
defer p.mtx.Unlock()
|
|
||||||
if len(p.released) > 0 {
|
|
||||||
id = p.released[len(p.released)-1]
|
|
||||||
p.released = p.released[:len(p.released)-1]
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
id = p.max_id
|
|
||||||
p.max_id++
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *idPool) Release(id uint) {
|
|
||||||
p.mtx.Lock()
|
|
||||||
defer p.mtx.Unlock()
|
|
||||||
p.released = append(p.released, id)
|
|
||||||
}
|
|
43
vendor/github.com/jtolds/gls/stack_tags.go
generated
vendored
43
vendor/github.com/jtolds/gls/stack_tags.go
generated
vendored
@ -1,43 +0,0 @@
|
|||||||
package gls
|
|
||||||
|
|
||||||
// so, basically, we're going to encode integer tags in base-16 on the stack
|
|
||||||
|
|
||||||
const (
|
|
||||||
bitWidth = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
func addStackTag(tag uint, context_call func()) {
|
|
||||||
if context_call == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
markS(tag, context_call)
|
|
||||||
}
|
|
||||||
|
|
||||||
func markS(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func mark0(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func mark1(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func mark2(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func mark3(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func mark4(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func mark5(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func mark6(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func mark7(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func mark8(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func mark9(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func markA(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func markB(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func markC(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func markD(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func markE(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
func markF(tag uint, cb func()) { _m(tag, cb) }
|
|
||||||
|
|
||||||
var pc_lookup = make(map[uintptr]int8, 17)
|
|
||||||
var mark_lookup [16]func(uint, func())
|
|
||||||
|
|
||||||
func _m(tag_remainder uint, cb func()) {
|
|
||||||
if tag_remainder == 0 {
|
|
||||||
cb()
|
|
||||||
} else {
|
|
||||||
mark_lookup[tag_remainder&0xf](tag_remainder>>bitWidth, cb)
|
|
||||||
}
|
|
||||||
}
|
|
101
vendor/github.com/jtolds/gls/stack_tags_js.go
generated
vendored
101
vendor/github.com/jtolds/gls/stack_tags_js.go
generated
vendored
@ -1,101 +0,0 @@
|
|||||||
// +build js
|
|
||||||
|
|
||||||
package gls
|
|
||||||
|
|
||||||
// This file is used for GopherJS builds, which don't have normal runtime support
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gopherjs/gopherjs/js"
|
|
||||||
)
|
|
||||||
|
|
||||||
var stackRE = regexp.MustCompile("\\s+at (\\S*) \\([^:]+:(\\d+):(\\d+)")
|
|
||||||
|
|
||||||
func findPtr() uintptr {
|
|
||||||
jsStack := js.Global.Get("Error").New().Get("stack").Call("split", "\n")
|
|
||||||
for i := 1; i < jsStack.Get("length").Int(); i++ {
|
|
||||||
item := jsStack.Index(i).String()
|
|
||||||
matches := stackRE.FindAllStringSubmatch(item, -1)
|
|
||||||
if matches == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
pkgPath := matches[0][1]
|
|
||||||
if strings.HasPrefix(pkgPath, "$packages.github.com/jtolds/gls.mark") {
|
|
||||||
line, _ := strconv.Atoi(matches[0][2])
|
|
||||||
char, _ := strconv.Atoi(matches[0][3])
|
|
||||||
x := (uintptr(line) << 16) | uintptr(char)
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
setEntries := func(f func(uint, func()), v int8) {
|
|
||||||
var ptr uintptr
|
|
||||||
f(0, func() {
|
|
||||||
ptr = findPtr()
|
|
||||||
})
|
|
||||||
pc_lookup[ptr] = v
|
|
||||||
if v >= 0 {
|
|
||||||
mark_lookup[v] = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setEntries(markS, -0x1)
|
|
||||||
setEntries(mark0, 0x0)
|
|
||||||
setEntries(mark1, 0x1)
|
|
||||||
setEntries(mark2, 0x2)
|
|
||||||
setEntries(mark3, 0x3)
|
|
||||||
setEntries(mark4, 0x4)
|
|
||||||
setEntries(mark5, 0x5)
|
|
||||||
setEntries(mark6, 0x6)
|
|
||||||
setEntries(mark7, 0x7)
|
|
||||||
setEntries(mark8, 0x8)
|
|
||||||
setEntries(mark9, 0x9)
|
|
||||||
setEntries(markA, 0xa)
|
|
||||||
setEntries(markB, 0xb)
|
|
||||||
setEntries(markC, 0xc)
|
|
||||||
setEntries(markD, 0xd)
|
|
||||||
setEntries(markE, 0xe)
|
|
||||||
setEntries(markF, 0xf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func currentStack(skip int) (stack []uintptr) {
|
|
||||||
jsStack := js.Global.Get("Error").New().Get("stack").Call("split", "\n")
|
|
||||||
for i := skip + 2; i < jsStack.Get("length").Int(); i++ {
|
|
||||||
item := jsStack.Index(i).String()
|
|
||||||
matches := stackRE.FindAllStringSubmatch(item, -1)
|
|
||||||
if matches == nil {
|
|
||||||
return stack
|
|
||||||
}
|
|
||||||
line, _ := strconv.Atoi(matches[0][2])
|
|
||||||
char, _ := strconv.Atoi(matches[0][3])
|
|
||||||
x := (uintptr(line) << 16) | uintptr(char)&0xffff
|
|
||||||
stack = append(stack, x)
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack
|
|
||||||
}
|
|
||||||
|
|
||||||
func readStackTags(skip int) (tags []uint) {
|
|
||||||
stack := currentStack(skip)
|
|
||||||
var current_tag uint
|
|
||||||
for _, pc := range stack {
|
|
||||||
val, ok := pc_lookup[pc]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if val < 0 {
|
|
||||||
tags = append(tags, current_tag)
|
|
||||||
current_tag = 0
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
current_tag <<= bitWidth
|
|
||||||
current_tag += uint(val)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
61
vendor/github.com/jtolds/gls/stack_tags_main.go
generated
vendored
61
vendor/github.com/jtolds/gls/stack_tags_main.go
generated
vendored
@ -1,61 +0,0 @@
|
|||||||
// +build !js
|
|
||||||
|
|
||||||
package gls
|
|
||||||
|
|
||||||
// This file is used for standard Go builds, which have the expected runtime support
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
setEntries := func(f func(uint, func()), v int8) {
|
|
||||||
pc_lookup[reflect.ValueOf(f).Pointer()] = v
|
|
||||||
if v >= 0 {
|
|
||||||
mark_lookup[v] = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setEntries(markS, -0x1)
|
|
||||||
setEntries(mark0, 0x0)
|
|
||||||
setEntries(mark1, 0x1)
|
|
||||||
setEntries(mark2, 0x2)
|
|
||||||
setEntries(mark3, 0x3)
|
|
||||||
setEntries(mark4, 0x4)
|
|
||||||
setEntries(mark5, 0x5)
|
|
||||||
setEntries(mark6, 0x6)
|
|
||||||
setEntries(mark7, 0x7)
|
|
||||||
setEntries(mark8, 0x8)
|
|
||||||
setEntries(mark9, 0x9)
|
|
||||||
setEntries(markA, 0xa)
|
|
||||||
setEntries(markB, 0xb)
|
|
||||||
setEntries(markC, 0xc)
|
|
||||||
setEntries(markD, 0xd)
|
|
||||||
setEntries(markE, 0xe)
|
|
||||||
setEntries(markF, 0xf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func currentStack(skip int) []uintptr {
|
|
||||||
stack := make([]uintptr, maxCallers)
|
|
||||||
return stack[:runtime.Callers(3+skip, stack)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func readStackTags(skip int) (tags []uint) {
|
|
||||||
stack := currentStack(skip)
|
|
||||||
var current_tag uint
|
|
||||||
for _, pc := range stack {
|
|
||||||
pc = runtime.FuncForPC(pc).Entry()
|
|
||||||
val, ok := pc_lookup[pc]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if val < 0 {
|
|
||||||
tags = append(tags, current_tag)
|
|
||||||
current_tag = 0
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
current_tag <<= bitWidth
|
|
||||||
current_tag += uint(val)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
23
vendor/github.com/smartystreets/assertions/LICENSE.md
generated
vendored
23
vendor/github.com/smartystreets/assertions/LICENSE.md
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
Copyright (c) 2015 SmartyStreets, LLC
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
NOTE: Various optional and subordinate components carry their own licensing
|
|
||||||
requirements and restrictions. Use of those components is subject to the terms
|
|
||||||
and conditions outlined the respective license of each component.
|
|
554
vendor/github.com/smartystreets/assertions/README.md
generated
vendored
554
vendor/github.com/smartystreets/assertions/README.md
generated
vendored
@ -1,554 +0,0 @@
|
|||||||
# assertions
|
|
||||||
--
|
|
||||||
import "github.com/smartystreets/assertions"
|
|
||||||
|
|
||||||
Package assertions contains the implementations for all assertions which are
|
|
||||||
referenced in goconvey's `convey` package
|
|
||||||
(github.com/smartystreets/goconvey/convey) for use with the So(...) method. They
|
|
||||||
can also be used in traditional Go test functions and even in applicaitons.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### func GoConveyMode
|
|
||||||
|
|
||||||
```go
|
|
||||||
func GoConveyMode(yes bool)
|
|
||||||
```
|
|
||||||
GoConveyMode provides control over JSON serialization of failures. When using
|
|
||||||
the assertions in this package from the convey package JSON results are very
|
|
||||||
helpful and can be rendered in a DIFF view. In that case, this function will be
|
|
||||||
called with a true value to enable the JSON serialization. By default, the
|
|
||||||
assertions in this package will not serializer a JSON result, making standalone
|
|
||||||
ussage more convenient.
|
|
||||||
|
|
||||||
#### func ShouldAlmostEqual
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldAlmostEqual(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldAlmostEqual makes sure that two parameters are close enough to being
|
|
||||||
equal. The acceptable delta may be specified with a third argument, or a very
|
|
||||||
small default delta will be used.
|
|
||||||
|
|
||||||
#### func ShouldBeBetween
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeBetween(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeBetween receives exactly three parameters: an actual value, a lower
|
|
||||||
bound, and an upper bound. It ensures that the actual value is between both
|
|
||||||
bounds (but not equal to either of them).
|
|
||||||
|
|
||||||
#### func ShouldBeBetweenOrEqual
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeBetweenOrEqual(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeBetweenOrEqual receives exactly three parameters: an actual value, a
|
|
||||||
lower bound, and an upper bound. It ensures that the actual value is between
|
|
||||||
both bounds or equal to one of them.
|
|
||||||
|
|
||||||
#### func ShouldBeBlank
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeBlank(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeBlank receives exactly 1 string parameter and ensures that it is equal
|
|
||||||
to "".
|
|
||||||
|
|
||||||
#### func ShouldBeChronological
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeChronological(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeChronological receives a []time.Time slice and asserts that the are in
|
|
||||||
chronological order starting with the first time.Time as the earliest.
|
|
||||||
|
|
||||||
#### func ShouldBeEmpty
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeEmpty(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeEmpty receives a single parameter (actual) and determines whether or not
|
|
||||||
calling len(actual) would return `0`. It obeys the rules specified by the len
|
|
||||||
function for determining length: http://golang.org/pkg/builtin/#len
|
|
||||||
|
|
||||||
#### func ShouldBeFalse
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeFalse(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeFalse receives a single parameter and ensures that it is false.
|
|
||||||
|
|
||||||
#### func ShouldBeGreaterThan
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeGreaterThan(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeGreaterThan receives exactly two parameters and ensures that the first
|
|
||||||
is greater than the second.
|
|
||||||
|
|
||||||
#### func ShouldBeGreaterThanOrEqualTo
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeGreaterThanOrEqualTo(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeGreaterThanOrEqualTo receives exactly two parameters and ensures that
|
|
||||||
the first is greater than or equal to the second.
|
|
||||||
|
|
||||||
#### func ShouldBeIn
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeIn(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeIn receives at least 2 parameters. The first is a proposed member of the
|
|
||||||
collection that is passed in either as the second parameter, or of the
|
|
||||||
collection that is comprised of all the remaining parameters. This assertion
|
|
||||||
ensures that the proposed member is in the collection (using ShouldEqual).
|
|
||||||
|
|
||||||
#### func ShouldBeLessThan
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeLessThan(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeLessThan receives exactly two parameters and ensures that the first is
|
|
||||||
less than the second.
|
|
||||||
|
|
||||||
#### func ShouldBeLessThanOrEqualTo
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeLessThanOrEqualTo(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeLessThan receives exactly two parameters and ensures that the first is
|
|
||||||
less than or equal to the second.
|
|
||||||
|
|
||||||
#### func ShouldBeNil
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeNil(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeNil receives a single parameter and ensures that it is nil.
|
|
||||||
|
|
||||||
#### func ShouldBeTrue
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeTrue(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeTrue receives a single parameter and ensures that it is true.
|
|
||||||
|
|
||||||
#### func ShouldBeZeroValue
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldBeZeroValue(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldBeZeroValue receives a single parameter and ensures that it is the Go
|
|
||||||
equivalent of the default value, or "zero" value.
|
|
||||||
|
|
||||||
#### func ShouldContain
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldContain(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldContain receives exactly two parameters. The first is a slice and the
|
|
||||||
second is a proposed member. Membership is determined using ShouldEqual.
|
|
||||||
|
|
||||||
#### func ShouldContainKey
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldContainKey(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldContainKey receives exactly two parameters. The first is a map and the
|
|
||||||
second is a proposed key. Keys are compared with a simple '=='.
|
|
||||||
|
|
||||||
#### func ShouldContainSubstring
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldContainSubstring(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldContainSubstring receives exactly 2 string parameters and ensures that the
|
|
||||||
first contains the second as a substring.
|
|
||||||
|
|
||||||
#### func ShouldEndWith
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldEndWith(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldEndWith receives exactly 2 string parameters and ensures that the first
|
|
||||||
ends with the second.
|
|
||||||
|
|
||||||
#### func ShouldEqual
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldEqual(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldEqual receives exactly two parameters and does an equality check.
|
|
||||||
|
|
||||||
#### func ShouldEqualTrimSpace
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldEqualTrimSpace(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldEqualTrimSpace receives exactly 2 string parameters and ensures that the
|
|
||||||
first is equal to the second after removing all leading and trailing whitespace
|
|
||||||
using strings.TrimSpace(first).
|
|
||||||
|
|
||||||
#### func ShouldEqualWithout
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldEqualWithout(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldEqualWithout receives exactly 3 string parameters and ensures that the
|
|
||||||
first is equal to the second after removing all instances of the third from the
|
|
||||||
first using strings.Replace(first, third, "", -1).
|
|
||||||
|
|
||||||
#### func ShouldHappenAfter
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldHappenAfter(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldHappenAfter receives exactly 2 time.Time arguments and asserts that the
|
|
||||||
first happens after the second.
|
|
||||||
|
|
||||||
#### func ShouldHappenBefore
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldHappenBefore(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldHappenBefore receives exactly 2 time.Time arguments and asserts that the
|
|
||||||
first happens before the second.
|
|
||||||
|
|
||||||
#### func ShouldHappenBetween
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldHappenBetween(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldHappenBetween receives exactly 3 time.Time arguments and asserts that the
|
|
||||||
first happens between (not on) the second and third.
|
|
||||||
|
|
||||||
#### func ShouldHappenOnOrAfter
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldHappenOnOrAfter(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldHappenOnOrAfter receives exactly 2 time.Time arguments and asserts that
|
|
||||||
the first happens on or after the second.
|
|
||||||
|
|
||||||
#### func ShouldHappenOnOrBefore
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldHappenOnOrBefore(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldHappenOnOrBefore receives exactly 2 time.Time arguments and asserts that
|
|
||||||
the first happens on or before the second.
|
|
||||||
|
|
||||||
#### func ShouldHappenOnOrBetween
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldHappenOnOrBetween(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that
|
|
||||||
the first happens between or on the second and third.
|
|
||||||
|
|
||||||
#### func ShouldHappenWithin
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldHappenWithin(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldHappenWithin receives a time.Time, a time.Duration, and a time.Time (3
|
|
||||||
arguments) and asserts that the first time.Time happens within or on the
|
|
||||||
duration specified relative to the other time.Time.
|
|
||||||
|
|
||||||
#### func ShouldHaveLength
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldHaveLength(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldHaveLength receives a collection and a positive integer and asserts that
|
|
||||||
the length of the collection is equal to the integer provided.
|
|
||||||
|
|
||||||
#### func ShouldHaveSameTypeAs
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldHaveSameTypeAs(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldHaveSameTypeAs receives exactly two parameters and compares their
|
|
||||||
underlying types for equality.
|
|
||||||
|
|
||||||
#### func ShouldImplement
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldImplement(actual interface{}, expectedList ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldImplement receives exactly two parameters and ensures that the first
|
|
||||||
implements the interface type of the second.
|
|
||||||
|
|
||||||
#### func ShouldNotAlmostEqual
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotAlmostEqual(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotAlmostEqual is the inverse of ShouldAlmostEqual
|
|
||||||
|
|
||||||
#### func ShouldNotBeBetween
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotBeBetween(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotBeBetween receives exactly three parameters: an actual value, a lower
|
|
||||||
bound, and an upper bound. It ensures that the actual value is NOT between both
|
|
||||||
bounds.
|
|
||||||
|
|
||||||
#### func ShouldNotBeBetweenOrEqual
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotBeBetweenOrEqual(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotBeBetweenOrEqual receives exactly three parameters: an actual value, a
|
|
||||||
lower bound, and an upper bound. It ensures that the actual value is nopt
|
|
||||||
between the bounds nor equal to either of them.
|
|
||||||
|
|
||||||
#### func ShouldNotBeBlank
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotBeBlank(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotBeBlank receives exactly 1 string parameter and ensures that it is
|
|
||||||
equal to "".
|
|
||||||
|
|
||||||
#### func ShouldNotBeEmpty
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotBeEmpty(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotBeEmpty receives a single parameter (actual) and determines whether or
|
|
||||||
not calling len(actual) would return a value greater than zero. It obeys the
|
|
||||||
rules specified by the `len` function for determining length:
|
|
||||||
http://golang.org/pkg/builtin/#len
|
|
||||||
|
|
||||||
#### func ShouldNotBeIn
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotBeIn(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotBeIn receives at least 2 parameters. The first is a proposed member of
|
|
||||||
the collection that is passed in either as the second parameter, or of the
|
|
||||||
collection that is comprised of all the remaining parameters. This assertion
|
|
||||||
ensures that the proposed member is NOT in the collection (using ShouldEqual).
|
|
||||||
|
|
||||||
#### func ShouldNotBeNil
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotBeNil(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotBeNil receives a single parameter and ensures that it is not nil.
|
|
||||||
|
|
||||||
#### func ShouldNotContain
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotContain(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotContain receives exactly two parameters. The first is a slice and the
|
|
||||||
second is a proposed member. Membership is determinied using ShouldEqual.
|
|
||||||
|
|
||||||
#### func ShouldNotContainKey
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotContainKey(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotContainKey receives exactly two parameters. The first is a map and the
|
|
||||||
second is a proposed absent key. Keys are compared with a simple '=='.
|
|
||||||
|
|
||||||
#### func ShouldNotContainSubstring
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotContainSubstring(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotContainSubstring receives exactly 2 string parameters and ensures that
|
|
||||||
the first does NOT contain the second as a substring.
|
|
||||||
|
|
||||||
#### func ShouldNotEndWith
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotEndWith(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldEndWith receives exactly 2 string parameters and ensures that the first
|
|
||||||
does not end with the second.
|
|
||||||
|
|
||||||
#### func ShouldNotEqual
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotEqual(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotEqual receives exactly two parameters and does an inequality check.
|
|
||||||
|
|
||||||
#### func ShouldNotHappenOnOrBetween
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotHappenOnOrBetween(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotHappenOnOrBetween receives exactly 3 time.Time arguments and asserts
|
|
||||||
that the first does NOT happen between or on the second or third.
|
|
||||||
|
|
||||||
#### func ShouldNotHappenWithin
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotHappenWithin(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotHappenWithin receives a time.Time, a time.Duration, and a time.Time (3
|
|
||||||
arguments) and asserts that the first time.Time does NOT happen within or on the
|
|
||||||
duration specified relative to the other time.Time.
|
|
||||||
|
|
||||||
#### func ShouldNotHaveSameTypeAs
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotHaveSameTypeAs(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotHaveSameTypeAs receives exactly two parameters and compares their
|
|
||||||
underlying types for inequality.
|
|
||||||
|
|
||||||
#### func ShouldNotImplement
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotImplement(actual interface{}, expectedList ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotImplement receives exactly two parameters and ensures that the first
|
|
||||||
does NOT implement the interface type of the second.
|
|
||||||
|
|
||||||
#### func ShouldNotPanic
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotPanic(actual interface{}, expected ...interface{}) (message string)
|
|
||||||
```
|
|
||||||
ShouldNotPanic receives a void, niladic function and expects to execute the
|
|
||||||
function without any panic.
|
|
||||||
|
|
||||||
#### func ShouldNotPanicWith
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotPanicWith(actual interface{}, expected ...interface{}) (message string)
|
|
||||||
```
|
|
||||||
ShouldNotPanicWith receives a void, niladic function and expects to recover a
|
|
||||||
panic whose content differs from the second argument.
|
|
||||||
|
|
||||||
#### func ShouldNotPointTo
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotPointTo(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotPointTo receives exactly two parameters and checks to see that they
|
|
||||||
point to different addresess.
|
|
||||||
|
|
||||||
#### func ShouldNotResemble
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotResemble(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotResemble receives exactly two parameters and does an inverse deep equal
|
|
||||||
check (see reflect.DeepEqual)
|
|
||||||
|
|
||||||
#### func ShouldNotStartWith
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldNotStartWith(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldNotStartWith receives exactly 2 string parameters and ensures that the
|
|
||||||
first does not start with the second.
|
|
||||||
|
|
||||||
#### func ShouldPanic
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldPanic(actual interface{}, expected ...interface{}) (message string)
|
|
||||||
```
|
|
||||||
ShouldPanic receives a void, niladic function and expects to recover a panic.
|
|
||||||
|
|
||||||
#### func ShouldPanicWith
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldPanicWith(actual interface{}, expected ...interface{}) (message string)
|
|
||||||
```
|
|
||||||
ShouldPanicWith receives a void, niladic function and expects to recover a panic
|
|
||||||
with the second argument as the content.
|
|
||||||
|
|
||||||
#### func ShouldPointTo
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldPointTo(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldPointTo receives exactly two parameters and checks to see that they point
|
|
||||||
to the same address.
|
|
||||||
|
|
||||||
#### func ShouldResemble
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldResemble(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldResemble receives exactly two parameters and does a deep equal check (see
|
|
||||||
reflect.DeepEqual)
|
|
||||||
|
|
||||||
#### func ShouldStartWith
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ShouldStartWith(actual interface{}, expected ...interface{}) string
|
|
||||||
```
|
|
||||||
ShouldStartWith receives exactly 2 string parameters and ensures that the first
|
|
||||||
starts with the second.
|
|
||||||
|
|
||||||
#### func So
|
|
||||||
|
|
||||||
```go
|
|
||||||
func So(actual interface{}, assert assertion, expected ...interface{}) (bool, string)
|
|
||||||
```
|
|
||||||
So is a convenience function (as opposed to an inconvenience function?) for
|
|
||||||
running assertions on arbitrary arguments in any context, be it for testing or
|
|
||||||
even application logging. It allows you to perform assertion-like behavior (and
|
|
||||||
get nicely formatted messages detailing discrepancies) but without the program
|
|
||||||
blowing up or panicking. All that is required is to import this package and call
|
|
||||||
`So` with one of the assertions exported by this package as the second
|
|
||||||
parameter. The first return parameter is a boolean indicating if the assertion
|
|
||||||
was true. The second return parameter is the well-formatted message showing why
|
|
||||||
an assertion was incorrect, or blank if the assertion was correct.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
if ok, message := So(x, ShouldBeGreaterThan, y); !ok {
|
|
||||||
log.Println(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
#### type Assertion
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Assertion struct {
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### func New
|
|
||||||
|
|
||||||
```go
|
|
||||||
func New(t testingT) *Assertion
|
|
||||||
```
|
|
||||||
New swallows the *testing.T struct and prints failed assertions using t.Error.
|
|
||||||
Example: assertions.New(t).So(1, should.Equal, 1)
|
|
||||||
|
|
||||||
#### func (*Assertion) Failed
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (this *Assertion) Failed() bool
|
|
||||||
```
|
|
||||||
Failed reports whether any calls to So (on this Assertion instance) have failed.
|
|
||||||
|
|
||||||
#### func (*Assertion) So
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (this *Assertion) So(actual interface{}, assert assertion, expected ...interface{}) bool
|
|
||||||
```
|
|
||||||
So calls the standalone So function and additionally, calls t.Error in failure
|
|
||||||
scenarios.
|
|
||||||
|
|
||||||
#### type Serializer
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Serializer interface {
|
|
||||||
// contains filtered or unexported methods
|
|
||||||
}
|
|
||||||
```
|
|
3
vendor/github.com/smartystreets/assertions/assertions.goconvey
generated
vendored
3
vendor/github.com/smartystreets/assertions/assertions.goconvey
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
#ignore
|
|
||||||
-timeout=1s
|
|
||||||
-coverpkg=github.com/smartystreets/assertions,github.com/smartystreets/assertions/internal/oglematchers
|
|
250
vendor/github.com/smartystreets/assertions/collections.go
generated
vendored
250
vendor/github.com/smartystreets/assertions/collections.go
generated
vendored
@ -1,250 +0,0 @@
|
|||||||
package assertions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/smartystreets/assertions/internal/oglematchers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ShouldContain receives exactly two parameters. The first is a slice and the
|
|
||||||
// second is a proposed member. Membership is determined using ShouldEqual.
|
|
||||||
func ShouldContain(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
if matchError := oglematchers.Contains(expected[0]).Matches(actual); matchError != nil {
|
|
||||||
typeName := reflect.TypeOf(actual)
|
|
||||||
|
|
||||||
if fmt.Sprintf("%v", matchError) == "which is not a slice or array" {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenAValidCollection, typeName)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(shouldHaveContained, typeName, expected[0])
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotContain receives exactly two parameters. The first is a slice and the
|
|
||||||
// second is a proposed member. Membership is determinied using ShouldEqual.
|
|
||||||
func ShouldNotContain(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
typeName := reflect.TypeOf(actual)
|
|
||||||
|
|
||||||
if matchError := oglematchers.Contains(expected[0]).Matches(actual); matchError != nil {
|
|
||||||
if fmt.Sprintf("%v", matchError) == "which is not a slice or array" {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenAValidCollection, typeName)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(shouldNotHaveContained, typeName, expected[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldContainKey receives exactly two parameters. The first is a map and the
|
|
||||||
// second is a proposed key. Keys are compared with a simple '=='.
|
|
||||||
func ShouldContainKey(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
keys, isMap := mapKeys(actual)
|
|
||||||
if !isMap {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenAValidMap, reflect.TypeOf(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !keyFound(keys, expected[0]) {
|
|
||||||
return fmt.Sprintf(shouldHaveContainedKey, reflect.TypeOf(actual), expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotContainKey receives exactly two parameters. The first is a map and the
|
|
||||||
// second is a proposed absent key. Keys are compared with a simple '=='.
|
|
||||||
func ShouldNotContainKey(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
keys, isMap := mapKeys(actual)
|
|
||||||
if !isMap {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenAValidMap, reflect.TypeOf(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
if keyFound(keys, expected[0]) {
|
|
||||||
return fmt.Sprintf(shouldNotHaveContainedKey, reflect.TypeOf(actual), expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapKeys(m interface{}) ([]reflect.Value, bool) {
|
|
||||||
value := reflect.ValueOf(m)
|
|
||||||
if value.Kind() != reflect.Map {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return value.MapKeys(), true
|
|
||||||
}
|
|
||||||
func keyFound(keys []reflect.Value, expectedKey interface{}) bool {
|
|
||||||
found := false
|
|
||||||
for _, key := range keys {
|
|
||||||
if key.Interface() == expectedKey {
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeIn receives at least 2 parameters. The first is a proposed member of the collection
|
|
||||||
// that is passed in either as the second parameter, or of the collection that is comprised
|
|
||||||
// of all the remaining parameters. This assertion ensures that the proposed member is in
|
|
||||||
// the collection (using ShouldEqual).
|
|
||||||
func ShouldBeIn(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := atLeast(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(expected) == 1 {
|
|
||||||
return shouldBeIn(actual, expected[0])
|
|
||||||
}
|
|
||||||
return shouldBeIn(actual, expected)
|
|
||||||
}
|
|
||||||
func shouldBeIn(actual interface{}, expected interface{}) string {
|
|
||||||
if matchError := oglematchers.Contains(actual).Matches(expected); matchError != nil {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenIn, actual, reflect.TypeOf(expected))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotBeIn receives at least 2 parameters. The first is a proposed member of the collection
|
|
||||||
// that is passed in either as the second parameter, or of the collection that is comprised
|
|
||||||
// of all the remaining parameters. This assertion ensures that the proposed member is NOT in
|
|
||||||
// the collection (using ShouldEqual).
|
|
||||||
func ShouldNotBeIn(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := atLeast(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(expected) == 1 {
|
|
||||||
return shouldNotBeIn(actual, expected[0])
|
|
||||||
}
|
|
||||||
return shouldNotBeIn(actual, expected)
|
|
||||||
}
|
|
||||||
func shouldNotBeIn(actual interface{}, expected interface{}) string {
|
|
||||||
if matchError := oglematchers.Contains(actual).Matches(expected); matchError == nil {
|
|
||||||
return fmt.Sprintf(shouldNotHaveBeenIn, actual, reflect.TypeOf(expected))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeEmpty receives a single parameter (actual) and determines whether or not
|
|
||||||
// calling len(actual) would return `0`. It obeys the rules specified by the len
|
|
||||||
// function for determining length: http://golang.org/pkg/builtin/#len
|
|
||||||
func ShouldBeEmpty(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
if actual == nil {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
value := reflect.ValueOf(actual)
|
|
||||||
switch value.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
if value.Len() == 0 {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
case reflect.Chan:
|
|
||||||
if value.Len() == 0 {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
if value.Len() == 0 {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
if value.Len() == 0 {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
elem := value.Elem()
|
|
||||||
kind := elem.Kind()
|
|
||||||
if (kind == reflect.Slice || kind == reflect.Array) && elem.Len() == 0 {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf(shouldHaveBeenEmpty, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotBeEmpty receives a single parameter (actual) and determines whether or not
|
|
||||||
// calling len(actual) would return a value greater than zero. It obeys the rules
|
|
||||||
// specified by the `len` function for determining length: http://golang.org/pkg/builtin/#len
|
|
||||||
func ShouldNotBeEmpty(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
if empty := ShouldBeEmpty(actual, expected...); empty != success {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(shouldNotHaveBeenEmpty, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldHaveLength receives 2 parameters. The first is a collection to check
|
|
||||||
// the length of, the second being the expected length. It obeys the rules
|
|
||||||
// specified by the len function for determining length:
|
|
||||||
// http://golang.org/pkg/builtin/#len
|
|
||||||
func ShouldHaveLength(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedLen int64
|
|
||||||
lenValue := reflect.ValueOf(expected[0])
|
|
||||||
switch lenValue.Kind() {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
expectedLen = lenValue.Int()
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
expectedLen = int64(lenValue.Uint())
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf(shouldHaveBeenAValidInteger, reflect.TypeOf(expected[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedLen < 0 {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenAValidLength, expected[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
value := reflect.ValueOf(actual)
|
|
||||||
switch value.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
if int64(value.Len()) == expectedLen {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
case reflect.Chan:
|
|
||||||
if int64(value.Len()) == expectedLen {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
if int64(value.Len()) == expectedLen {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
if int64(value.Len()) == expectedLen {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
elem := value.Elem()
|
|
||||||
kind := elem.Kind()
|
|
||||||
if (kind == reflect.Slice || kind == reflect.Array) && int64(elem.Len()) == expectedLen {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf(shouldHaveBeenAValidCollection, reflect.TypeOf(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf(shouldHaveHadLength, actual, expectedLen)
|
|
||||||
}
|
|
99
vendor/github.com/smartystreets/assertions/doc.go
generated
vendored
99
vendor/github.com/smartystreets/assertions/doc.go
generated
vendored
@ -1,99 +0,0 @@
|
|||||||
// Package assertions contains the implementations for all assertions which
|
|
||||||
// are referenced in goconvey's `convey` package
|
|
||||||
// (github.com/smartystreets/goconvey/convey) for use with the So(...) method.
|
|
||||||
// They can also be used in traditional Go test functions and even in
|
|
||||||
// applicaitons.
|
|
||||||
package assertions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// By default we use a no-op serializer. The actual Serializer provides a JSON
|
|
||||||
// representation of failure results on selected assertions so the goconvey
|
|
||||||
// web UI can display a convenient diff.
|
|
||||||
var serializer Serializer = new(noopSerializer)
|
|
||||||
|
|
||||||
// GoConveyMode provides control over JSON serialization of failures. When
|
|
||||||
// using the assertions in this package from the convey package JSON results
|
|
||||||
// are very helpful and can be rendered in a DIFF view. In that case, this function
|
|
||||||
// will be called with a true value to enable the JSON serialization. By default,
|
|
||||||
// the assertions in this package will not serializer a JSON result, making
|
|
||||||
// standalone ussage more convenient.
|
|
||||||
func GoConveyMode(yes bool) {
|
|
||||||
if yes {
|
|
||||||
serializer = newSerializer()
|
|
||||||
} else {
|
|
||||||
serializer = new(noopSerializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type testingT interface {
|
|
||||||
Error(args ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Assertion struct {
|
|
||||||
t testingT
|
|
||||||
failed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// New swallows the *testing.T struct and prints failed assertions using t.Error.
|
|
||||||
// Example: assertions.New(t).So(1, should.Equal, 1)
|
|
||||||
func New(t testingT) *Assertion {
|
|
||||||
return &Assertion{t: t}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed reports whether any calls to So (on this Assertion instance) have failed.
|
|
||||||
func (this *Assertion) Failed() bool {
|
|
||||||
return this.failed
|
|
||||||
}
|
|
||||||
|
|
||||||
// So calls the standalone So function and additionally, calls t.Error in failure scenarios.
|
|
||||||
func (this *Assertion) So(actual interface{}, assert assertion, expected ...interface{}) bool {
|
|
||||||
ok, result := So(actual, assert, expected...)
|
|
||||||
if !ok {
|
|
||||||
this.failed = true
|
|
||||||
_, file, line, _ := runtime.Caller(1)
|
|
||||||
this.t.Error(fmt.Sprintf("\n%s:%d\n%s", file, line, result))
|
|
||||||
}
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// So is a convenience function (as opposed to an inconvenience function?)
|
|
||||||
// for running assertions on arbitrary arguments in any context, be it for testing or even
|
|
||||||
// application logging. It allows you to perform assertion-like behavior (and get nicely
|
|
||||||
// formatted messages detailing discrepancies) but without the program blowing up or panicking.
|
|
||||||
// All that is required is to import this package and call `So` with one of the assertions
|
|
||||||
// exported by this package as the second parameter.
|
|
||||||
// The first return parameter is a boolean indicating if the assertion was true. The second
|
|
||||||
// return parameter is the well-formatted message showing why an assertion was incorrect, or
|
|
||||||
// blank if the assertion was correct.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// if ok, message := So(x, ShouldBeGreaterThan, y); !ok {
|
|
||||||
// log.Println(message)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
func So(actual interface{}, assert assertion, expected ...interface{}) (bool, string) {
|
|
||||||
if result := so(actual, assert, expected...); len(result) == 0 {
|
|
||||||
return true, result
|
|
||||||
} else {
|
|
||||||
return false, result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// so is like So, except that it only returns the string message, which is blank if the
|
|
||||||
// assertion passed. Used to facilitate testing.
|
|
||||||
func so(actual interface{}, assert func(interface{}, ...interface{}) string, expected ...interface{}) string {
|
|
||||||
return assert(actual, expected...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// assertion is an alias for a function with a signature that the So()
|
|
||||||
// function can handle. Any future or custom assertions should conform to this
|
|
||||||
// method signature. The return value should be an empty string if the assertion
|
|
||||||
// passes and a well-formed failure message if not.
|
|
||||||
type assertion func(actual interface{}, expected ...interface{}) string
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
286
vendor/github.com/smartystreets/assertions/equality.go
generated
vendored
286
vendor/github.com/smartystreets/assertions/equality.go
generated
vendored
@ -1,286 +0,0 @@
|
|||||||
package assertions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/smartystreets/assertions/internal/oglematchers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// default acceptable delta for ShouldAlmostEqual
|
|
||||||
const defaultDelta = 0.0000000001
|
|
||||||
|
|
||||||
// ShouldEqual receives exactly two parameters and does an equality check.
|
|
||||||
func ShouldEqual(actual interface{}, expected ...interface{}) string {
|
|
||||||
if message := need(1, expected); message != success {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
return shouldEqual(actual, expected[0])
|
|
||||||
}
|
|
||||||
func shouldEqual(actual, expected interface{}) (message string) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
message = serializer.serialize(expected, actual, fmt.Sprintf(shouldHaveBeenEqual, expected, actual))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if matchError := oglematchers.Equals(expected).Matches(actual); matchError != nil {
|
|
||||||
expectedSyntax := fmt.Sprintf("%v", expected)
|
|
||||||
actualSyntax := fmt.Sprintf("%v", actual)
|
|
||||||
if expectedSyntax == actualSyntax && reflect.TypeOf(expected) != reflect.TypeOf(actual) {
|
|
||||||
message = fmt.Sprintf(shouldHaveBeenEqualTypeMismatch, expected, expected, actual, actual)
|
|
||||||
} else {
|
|
||||||
message = fmt.Sprintf(shouldHaveBeenEqual, expected, actual)
|
|
||||||
}
|
|
||||||
message = serializer.serialize(expected, actual, message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotEqual receives exactly two parameters and does an inequality check.
|
|
||||||
func ShouldNotEqual(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
} else if ShouldEqual(actual, expected[0]) == success {
|
|
||||||
return fmt.Sprintf(shouldNotHaveBeenEqual, actual, expected[0])
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldAlmostEqual makes sure that two parameters are close enough to being equal.
|
|
||||||
// The acceptable delta may be specified with a third argument,
|
|
||||||
// or a very small default delta will be used.
|
|
||||||
func ShouldAlmostEqual(actual interface{}, expected ...interface{}) string {
|
|
||||||
actualFloat, expectedFloat, deltaFloat, err := cleanAlmostEqualInput(actual, expected...)
|
|
||||||
|
|
||||||
if err != "" {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if math.Abs(actualFloat-expectedFloat) <= deltaFloat {
|
|
||||||
return success
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenAlmostEqual, actualFloat, expectedFloat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotAlmostEqual is the inverse of ShouldAlmostEqual
|
|
||||||
func ShouldNotAlmostEqual(actual interface{}, expected ...interface{}) string {
|
|
||||||
actualFloat, expectedFloat, deltaFloat, err := cleanAlmostEqualInput(actual, expected...)
|
|
||||||
|
|
||||||
if err != "" {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if math.Abs(actualFloat-expectedFloat) > deltaFloat {
|
|
||||||
return success
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf(shouldHaveNotBeenAlmostEqual, actualFloat, expectedFloat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanAlmostEqualInput(actual interface{}, expected ...interface{}) (float64, float64, float64, string) {
|
|
||||||
deltaFloat := 0.0000000001
|
|
||||||
|
|
||||||
if len(expected) == 0 {
|
|
||||||
return 0.0, 0.0, 0.0, "This assertion requires exactly one comparison value and an optional delta (you provided neither)"
|
|
||||||
} else if len(expected) == 2 {
|
|
||||||
delta, err := getFloat(expected[1])
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return 0.0, 0.0, 0.0, "delta must be a numerical type"
|
|
||||||
}
|
|
||||||
|
|
||||||
deltaFloat = delta
|
|
||||||
} else if len(expected) > 2 {
|
|
||||||
return 0.0, 0.0, 0.0, "This assertion requires exactly one comparison value and an optional delta (you provided more values)"
|
|
||||||
}
|
|
||||||
|
|
||||||
actualFloat, err := getFloat(actual)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return 0.0, 0.0, 0.0, err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedFloat, err := getFloat(expected[0])
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return 0.0, 0.0, 0.0, err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
return actualFloat, expectedFloat, deltaFloat, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the float value of any real number, or error if it is not a numerical type
|
|
||||||
func getFloat(num interface{}) (float64, error) {
|
|
||||||
numValue := reflect.ValueOf(num)
|
|
||||||
numKind := numValue.Kind()
|
|
||||||
|
|
||||||
if numKind == reflect.Int ||
|
|
||||||
numKind == reflect.Int8 ||
|
|
||||||
numKind == reflect.Int16 ||
|
|
||||||
numKind == reflect.Int32 ||
|
|
||||||
numKind == reflect.Int64 {
|
|
||||||
return float64(numValue.Int()), nil
|
|
||||||
} else if numKind == reflect.Uint ||
|
|
||||||
numKind == reflect.Uint8 ||
|
|
||||||
numKind == reflect.Uint16 ||
|
|
||||||
numKind == reflect.Uint32 ||
|
|
||||||
numKind == reflect.Uint64 {
|
|
||||||
return float64(numValue.Uint()), nil
|
|
||||||
} else if numKind == reflect.Float32 ||
|
|
||||||
numKind == reflect.Float64 {
|
|
||||||
return numValue.Float(), nil
|
|
||||||
} else {
|
|
||||||
return 0.0, errors.New("must be a numerical type, but was " + numKind.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldResemble receives exactly two parameters and does a deep equal check (see reflect.DeepEqual)
|
|
||||||
func ShouldResemble(actual interface{}, expected ...interface{}) string {
|
|
||||||
if message := need(1, expected); message != success {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
if matchError := oglematchers.DeepEquals(expected[0]).Matches(actual); matchError != nil {
|
|
||||||
expectedSyntax := fmt.Sprintf("%#v", expected[0])
|
|
||||||
actualSyntax := fmt.Sprintf("%#v", actual)
|
|
||||||
var message string
|
|
||||||
if expectedSyntax == actualSyntax {
|
|
||||||
message = fmt.Sprintf(shouldHaveResembledTypeMismatch, expected[0], expected[0], actual, actual)
|
|
||||||
} else {
|
|
||||||
message = fmt.Sprintf(shouldHaveResembled, expected[0], actual)
|
|
||||||
}
|
|
||||||
return serializer.serializeDetailed(expected[0], actual, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotResemble receives exactly two parameters and does an inverse deep equal check (see reflect.DeepEqual)
|
|
||||||
func ShouldNotResemble(actual interface{}, expected ...interface{}) string {
|
|
||||||
if message := need(1, expected); message != success {
|
|
||||||
return message
|
|
||||||
} else if ShouldResemble(actual, expected[0]) == success {
|
|
||||||
return fmt.Sprintf(shouldNotHaveResembled, actual, expected[0])
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldPointTo receives exactly two parameters and checks to see that they point to the same address.
|
|
||||||
func ShouldPointTo(actual interface{}, expected ...interface{}) string {
|
|
||||||
if message := need(1, expected); message != success {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
return shouldPointTo(actual, expected[0])
|
|
||||||
|
|
||||||
}
|
|
||||||
func shouldPointTo(actual, expected interface{}) string {
|
|
||||||
actualValue := reflect.ValueOf(actual)
|
|
||||||
expectedValue := reflect.ValueOf(expected)
|
|
||||||
|
|
||||||
if ShouldNotBeNil(actual) != success {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenNonNilPointer, "first", "nil")
|
|
||||||
} else if ShouldNotBeNil(expected) != success {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenNonNilPointer, "second", "nil")
|
|
||||||
} else if actualValue.Kind() != reflect.Ptr {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenNonNilPointer, "first", "not")
|
|
||||||
} else if expectedValue.Kind() != reflect.Ptr {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenNonNilPointer, "second", "not")
|
|
||||||
} else if ShouldEqual(actualValue.Pointer(), expectedValue.Pointer()) != success {
|
|
||||||
actualAddress := reflect.ValueOf(actual).Pointer()
|
|
||||||
expectedAddress := reflect.ValueOf(expected).Pointer()
|
|
||||||
return serializer.serialize(expectedAddress, actualAddress, fmt.Sprintf(shouldHavePointedTo,
|
|
||||||
actual, actualAddress,
|
|
||||||
expected, expectedAddress))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotPointTo receives exactly two parameters and checks to see that they point to different addresess.
|
|
||||||
func ShouldNotPointTo(actual interface{}, expected ...interface{}) string {
|
|
||||||
if message := need(1, expected); message != success {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
compare := ShouldPointTo(actual, expected[0])
|
|
||||||
if strings.HasPrefix(compare, shouldBePointers) {
|
|
||||||
return compare
|
|
||||||
} else if compare == success {
|
|
||||||
return fmt.Sprintf(shouldNotHavePointedTo, actual, expected[0], reflect.ValueOf(actual).Pointer())
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeNil receives a single parameter and ensures that it is nil.
|
|
||||||
func ShouldBeNil(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
} else if actual == nil {
|
|
||||||
return success
|
|
||||||
} else if interfaceHasNilValue(actual) {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(shouldHaveBeenNil, actual)
|
|
||||||
}
|
|
||||||
func interfaceHasNilValue(actual interface{}) bool {
|
|
||||||
value := reflect.ValueOf(actual)
|
|
||||||
kind := value.Kind()
|
|
||||||
nilable := kind == reflect.Slice ||
|
|
||||||
kind == reflect.Chan ||
|
|
||||||
kind == reflect.Func ||
|
|
||||||
kind == reflect.Ptr ||
|
|
||||||
kind == reflect.Map
|
|
||||||
|
|
||||||
// Careful: reflect.Value.IsNil() will panic unless it's an interface, chan, map, func, slice, or ptr
|
|
||||||
// Reference: http://golang.org/pkg/reflect/#Value.IsNil
|
|
||||||
return nilable && value.IsNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotBeNil receives a single parameter and ensures that it is not nil.
|
|
||||||
func ShouldNotBeNil(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
} else if ShouldBeNil(actual) == success {
|
|
||||||
return fmt.Sprintf(shouldNotHaveBeenNil, actual)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeTrue receives a single parameter and ensures that it is true.
|
|
||||||
func ShouldBeTrue(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
} else if actual != true {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenTrue, actual)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeFalse receives a single parameter and ensures that it is false.
|
|
||||||
func ShouldBeFalse(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
} else if actual != false {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenFalse, actual)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeZeroValue receives a single parameter and ensures that it is
|
|
||||||
// the Go equivalent of the default value, or "zero" value.
|
|
||||||
func ShouldBeZeroValue(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
zeroVal := reflect.Zero(reflect.TypeOf(actual)).Interface()
|
|
||||||
if !reflect.DeepEqual(zeroVal, actual) {
|
|
||||||
return serializer.serialize(zeroVal, actual, fmt.Sprintf(shouldHaveBeenZeroValue, actual))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
23
vendor/github.com/smartystreets/assertions/filter.go
generated
vendored
23
vendor/github.com/smartystreets/assertions/filter.go
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
package assertions
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
const (
|
|
||||||
success = ""
|
|
||||||
needExactValues = "This assertion requires exactly %d comparison values (you provided %d)."
|
|
||||||
needNonEmptyCollection = "This assertion requires at least 1 comparison value (you provided 0)."
|
|
||||||
)
|
|
||||||
|
|
||||||
func need(needed int, expected []interface{}) string {
|
|
||||||
if len(expected) != needed {
|
|
||||||
return fmt.Sprintf(needExactValues, needed, len(expected))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
func atLeast(minimum int, expected []interface{}) string {
|
|
||||||
if len(expected) < 1 {
|
|
||||||
return needNonEmptyCollection
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
202
vendor/github.com/smartystreets/assertions/internal/oglematchers/LICENSE
generated
vendored
202
vendor/github.com/smartystreets/assertions/internal/oglematchers/LICENSE
generated
vendored
@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
58
vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md
generated
vendored
58
vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md
generated
vendored
@ -1,58 +0,0 @@
|
|||||||
[![GoDoc](https://godoc.org/github.com/smartystreets/assertions/internal/oglematchers?status.svg)](https://godoc.org/github.com/smartystreets/assertions/internal/oglematchers)
|
|
||||||
|
|
||||||
`oglematchers` is a package for the Go programming language containing a set of
|
|
||||||
matchers, useful in a testing or mocking framework, inspired by and mostly
|
|
||||||
compatible with [Google Test][googletest] for C++ and
|
|
||||||
[Google JS Test][google-js-test]. The package is used by the
|
|
||||||
[ogletest][ogletest] testing framework and [oglemock][oglemock] mocking
|
|
||||||
framework, which may be more directly useful to you, but can be generically used
|
|
||||||
elsewhere as well.
|
|
||||||
|
|
||||||
A "matcher" is simply an object with a `Matches` method defining a set of golang
|
|
||||||
values matched by the matcher, and a `Description` method describing that set.
|
|
||||||
For example, here are some matchers:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Numbers
|
|
||||||
Equals(17.13)
|
|
||||||
LessThan(19)
|
|
||||||
|
|
||||||
// Strings
|
|
||||||
Equals("taco")
|
|
||||||
HasSubstr("burrito")
|
|
||||||
MatchesRegex("t.*o")
|
|
||||||
|
|
||||||
// Combining matchers
|
|
||||||
AnyOf(LessThan(17), GreaterThan(19))
|
|
||||||
```
|
|
||||||
|
|
||||||
There are lots more; see [here][reference] for a reference. You can also add
|
|
||||||
your own simply by implementing the `oglematchers.Matcher` interface.
|
|
||||||
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
First, make sure you have installed Go 1.0.2 or newer. See
|
|
||||||
[here][golang-install] for instructions.
|
|
||||||
|
|
||||||
Use the following command to install `oglematchers` and keep it up to date:
|
|
||||||
|
|
||||||
go get -u github.com/smartystreets/assertions/internal/oglematchers
|
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
See [here][reference] for documentation. Alternatively, you can install the
|
|
||||||
package and then use `godoc`:
|
|
||||||
|
|
||||||
godoc github.com/smartystreets/assertions/internal/oglematchers
|
|
||||||
|
|
||||||
|
|
||||||
[reference]: http://godoc.org/github.com/smartystreets/assertions/internal/oglematchers
|
|
||||||
[golang-install]: http://golang.org/doc/install.html
|
|
||||||
[googletest]: http://code.google.com/p/googletest/
|
|
||||||
[google-js-test]: http://code.google.com/p/google-js-test/
|
|
||||||
[ogletest]: http://github.com/smartystreets/assertions/internal/ogletest
|
|
||||||
[oglemock]: http://github.com/smartystreets/assertions/internal/oglemock
|
|
70
vendor/github.com/smartystreets/assertions/internal/oglematchers/all_of.go
generated
vendored
70
vendor/github.com/smartystreets/assertions/internal/oglematchers/all_of.go
generated
vendored
@ -1,70 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AllOf accepts a set of matchers S and returns a matcher that follows the
|
|
||||||
// algorithm below when considering a candidate c:
|
|
||||||
//
|
|
||||||
// 1. Return true if for every Matcher m in S, m matches c.
|
|
||||||
//
|
|
||||||
// 2. Otherwise, if there is a matcher m in S such that m returns a fatal
|
|
||||||
// error for c, return that matcher's error message.
|
|
||||||
//
|
|
||||||
// 3. Otherwise, return false with the error from some wrapped matcher.
|
|
||||||
//
|
|
||||||
// This is akin to a logical AND operation for matchers.
|
|
||||||
func AllOf(matchers ...Matcher) Matcher {
|
|
||||||
return &allOfMatcher{matchers}
|
|
||||||
}
|
|
||||||
|
|
||||||
type allOfMatcher struct {
|
|
||||||
wrappedMatchers []Matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *allOfMatcher) Description() string {
|
|
||||||
// Special case: the empty set.
|
|
||||||
if len(m.wrappedMatchers) == 0 {
|
|
||||||
return "is anything"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join the descriptions for the wrapped matchers.
|
|
||||||
wrappedDescs := make([]string, len(m.wrappedMatchers))
|
|
||||||
for i, wrappedMatcher := range m.wrappedMatchers {
|
|
||||||
wrappedDescs[i] = wrappedMatcher.Description()
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(wrappedDescs, ", and ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *allOfMatcher) Matches(c interface{}) (err error) {
|
|
||||||
for _, wrappedMatcher := range m.wrappedMatchers {
|
|
||||||
if wrappedErr := wrappedMatcher.Matches(c); wrappedErr != nil {
|
|
||||||
err = wrappedErr
|
|
||||||
|
|
||||||
// If the error is fatal, return immediately with this error.
|
|
||||||
_, ok := wrappedErr.(*FatalError)
|
|
||||||
if ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
32
vendor/github.com/smartystreets/assertions/internal/oglematchers/any.go
generated
vendored
32
vendor/github.com/smartystreets/assertions/internal/oglematchers/any.go
generated
vendored
@ -1,32 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
// Any returns a matcher that matches any value.
|
|
||||||
func Any() Matcher {
|
|
||||||
return &anyMatcher{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type anyMatcher struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *anyMatcher) Description() string {
|
|
||||||
return "is anything"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *anyMatcher) Matches(c interface{}) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
94
vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go
generated
vendored
94
vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go
generated
vendored
@ -1,94 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AnyOf accepts a set of values S and returns a matcher that follows the
|
|
||||||
// algorithm below when considering a candidate c:
|
|
||||||
//
|
|
||||||
// 1. If there exists a value m in S such that m implements the Matcher
|
|
||||||
// interface and m matches c, return true.
|
|
||||||
//
|
|
||||||
// 2. Otherwise, if there exists a value v in S such that v does not implement
|
|
||||||
// the Matcher interface and the matcher Equals(v) matches c, return true.
|
|
||||||
//
|
|
||||||
// 3. Otherwise, if there is a value m in S such that m implements the Matcher
|
|
||||||
// interface and m returns a fatal error for c, return that fatal error.
|
|
||||||
//
|
|
||||||
// 4. Otherwise, return false.
|
|
||||||
//
|
|
||||||
// This is akin to a logical OR operation for matchers, with non-matchers x
|
|
||||||
// being treated as Equals(x).
|
|
||||||
func AnyOf(vals ...interface{}) Matcher {
|
|
||||||
// Get ahold of a type variable for the Matcher interface.
|
|
||||||
var dummy *Matcher
|
|
||||||
matcherType := reflect.TypeOf(dummy).Elem()
|
|
||||||
|
|
||||||
// Create a matcher for each value, or use the value itself if it's already a
|
|
||||||
// matcher.
|
|
||||||
wrapped := make([]Matcher, len(vals))
|
|
||||||
for i, v := range vals {
|
|
||||||
t := reflect.TypeOf(v)
|
|
||||||
if t != nil && t.Implements(matcherType) {
|
|
||||||
wrapped[i] = v.(Matcher)
|
|
||||||
} else {
|
|
||||||
wrapped[i] = Equals(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &anyOfMatcher{wrapped}
|
|
||||||
}
|
|
||||||
|
|
||||||
type anyOfMatcher struct {
|
|
||||||
wrapped []Matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *anyOfMatcher) Description() string {
|
|
||||||
wrappedDescs := make([]string, len(m.wrapped))
|
|
||||||
for i, matcher := range m.wrapped {
|
|
||||||
wrappedDescs[i] = matcher.Description()
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("or(%s)", strings.Join(wrappedDescs, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *anyOfMatcher) Matches(c interface{}) (err error) {
|
|
||||||
err = errors.New("")
|
|
||||||
|
|
||||||
// Try each matcher in turn.
|
|
||||||
for _, matcher := range m.wrapped {
|
|
||||||
wrappedErr := matcher.Matches(c)
|
|
||||||
|
|
||||||
// Return immediately if there's a match.
|
|
||||||
if wrappedErr == nil {
|
|
||||||
err = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note the fatal error, if any.
|
|
||||||
if _, isFatal := wrappedErr.(*FatalError); isFatal {
|
|
||||||
err = wrappedErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
61
vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go
generated
vendored
61
vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go
generated
vendored
@ -1,61 +0,0 @@
|
|||||||
// Copyright 2012 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Return a matcher that matches arrays slices with at least one element that
|
|
||||||
// matches the supplied argument. If the argument x is not itself a Matcher,
|
|
||||||
// this is equivalent to Contains(Equals(x)).
|
|
||||||
func Contains(x interface{}) Matcher {
|
|
||||||
var result containsMatcher
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if result.elementMatcher, ok = x.(Matcher); !ok {
|
|
||||||
result.elementMatcher = Equals(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result
|
|
||||||
}
|
|
||||||
|
|
||||||
type containsMatcher struct {
|
|
||||||
elementMatcher Matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *containsMatcher) Description() string {
|
|
||||||
return fmt.Sprintf("contains: %s", m.elementMatcher.Description())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *containsMatcher) Matches(candidate interface{}) error {
|
|
||||||
// The candidate must be a slice or an array.
|
|
||||||
v := reflect.ValueOf(candidate)
|
|
||||||
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
|
|
||||||
return NewFatalError("which is not a slice or array")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check each element.
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
elem := v.Index(i)
|
|
||||||
if matchErr := m.elementMatcher.Matches(elem.Interface()); matchErr == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("")
|
|
||||||
}
|
|
88
vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go
generated
vendored
88
vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go
generated
vendored
@ -1,88 +0,0 @@
|
|||||||
// Copyright 2012 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
var byteSliceType reflect.Type = reflect.TypeOf([]byte{})
|
|
||||||
|
|
||||||
// DeepEquals returns a matcher that matches based on 'deep equality', as
|
|
||||||
// defined by the reflect package. This matcher requires that values have
|
|
||||||
// identical types to x.
|
|
||||||
func DeepEquals(x interface{}) Matcher {
|
|
||||||
return &deepEqualsMatcher{x}
|
|
||||||
}
|
|
||||||
|
|
||||||
type deepEqualsMatcher struct {
|
|
||||||
x interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *deepEqualsMatcher) Description() string {
|
|
||||||
xDesc := fmt.Sprintf("%v", m.x)
|
|
||||||
xValue := reflect.ValueOf(m.x)
|
|
||||||
|
|
||||||
// Special case: fmt.Sprintf presents nil slices as "[]", but
|
|
||||||
// reflect.DeepEqual makes a distinction between nil and empty slices. Make
|
|
||||||
// this less confusing.
|
|
||||||
if xValue.Kind() == reflect.Slice && xValue.IsNil() {
|
|
||||||
xDesc = "<nil slice>"
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("deep equals: %s", xDesc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *deepEqualsMatcher) Matches(c interface{}) error {
|
|
||||||
// Make sure the types match.
|
|
||||||
ct := reflect.TypeOf(c)
|
|
||||||
xt := reflect.TypeOf(m.x)
|
|
||||||
|
|
||||||
if ct != xt {
|
|
||||||
return NewFatalError(fmt.Sprintf("which is of type %v", ct))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case: handle byte slices more efficiently.
|
|
||||||
cValue := reflect.ValueOf(c)
|
|
||||||
xValue := reflect.ValueOf(m.x)
|
|
||||||
|
|
||||||
if ct == byteSliceType && !cValue.IsNil() && !xValue.IsNil() {
|
|
||||||
xBytes := m.x.([]byte)
|
|
||||||
cBytes := c.([]byte)
|
|
||||||
|
|
||||||
if bytes.Equal(cBytes, xBytes) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defer to the reflect package.
|
|
||||||
if reflect.DeepEqual(m.x, c) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case: if the comparison failed because c is the nil slice, given
|
|
||||||
// an indication of this (since its value is printed as "[]").
|
|
||||||
if cValue.Kind() == reflect.Slice && cValue.IsNil() {
|
|
||||||
return errors.New("which is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("")
|
|
||||||
}
|
|
91
vendor/github.com/smartystreets/assertions/internal/oglematchers/elements_are.go
generated
vendored
91
vendor/github.com/smartystreets/assertions/internal/oglematchers/elements_are.go
generated
vendored
@ -1,91 +0,0 @@
|
|||||||
// Copyright 2012 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Given a list of arguments M, ElementsAre returns a matcher that matches
|
|
||||||
// arrays and slices A where all of the following hold:
|
|
||||||
//
|
|
||||||
// * A is the same length as M.
|
|
||||||
//
|
|
||||||
// * For each i < len(A) where M[i] is a matcher, A[i] matches M[i].
|
|
||||||
//
|
|
||||||
// * For each i < len(A) where M[i] is not a matcher, A[i] matches
|
|
||||||
// Equals(M[i]).
|
|
||||||
//
|
|
||||||
func ElementsAre(M ...interface{}) Matcher {
|
|
||||||
// Copy over matchers, or convert to Equals(x) for non-matcher x.
|
|
||||||
subMatchers := make([]Matcher, len(M))
|
|
||||||
for i, x := range M {
|
|
||||||
if matcher, ok := x.(Matcher); ok {
|
|
||||||
subMatchers[i] = matcher
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
subMatchers[i] = Equals(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &elementsAreMatcher{subMatchers}
|
|
||||||
}
|
|
||||||
|
|
||||||
type elementsAreMatcher struct {
|
|
||||||
subMatchers []Matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *elementsAreMatcher) Description() string {
|
|
||||||
subDescs := make([]string, len(m.subMatchers))
|
|
||||||
for i, sm := range m.subMatchers {
|
|
||||||
subDescs[i] = sm.Description()
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("elements are: [%s]", strings.Join(subDescs, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *elementsAreMatcher) Matches(candidates interface{}) error {
|
|
||||||
// The candidate must be a slice or an array.
|
|
||||||
v := reflect.ValueOf(candidates)
|
|
||||||
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
|
|
||||||
return NewFatalError("which is not a slice or array")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The length must be correct.
|
|
||||||
if v.Len() != len(m.subMatchers) {
|
|
||||||
return errors.New(fmt.Sprintf("which is of length %d", v.Len()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check each element.
|
|
||||||
for i, subMatcher := range m.subMatchers {
|
|
||||||
c := v.Index(i)
|
|
||||||
if matchErr := subMatcher.Matches(c.Interface()); matchErr != nil {
|
|
||||||
// Return an errors indicating which element doesn't match. If the
|
|
||||||
// matcher error was fatal, make this one fatal too.
|
|
||||||
err := errors.New(fmt.Sprintf("whose element %d doesn't match", i))
|
|
||||||
if _, isFatal := matchErr.(*FatalError); isFatal {
|
|
||||||
err = NewFatalError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
541
vendor/github.com/smartystreets/assertions/internal/oglematchers/equals.go
generated
vendored
541
vendor/github.com/smartystreets/assertions/internal/oglematchers/equals.go
generated
vendored
@ -1,541 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Equals(x) returns a matcher that matches values v such that v and x are
|
|
||||||
// equivalent. This includes the case when the comparison v == x using Go's
|
|
||||||
// built-in comparison operator is legal (except for structs, which this
|
|
||||||
// matcher does not support), but for convenience the following rules also
|
|
||||||
// apply:
|
|
||||||
//
|
|
||||||
// * Type checking is done based on underlying types rather than actual
|
|
||||||
// types, so that e.g. two aliases for string can be compared:
|
|
||||||
//
|
|
||||||
// type stringAlias1 string
|
|
||||||
// type stringAlias2 string
|
|
||||||
//
|
|
||||||
// a := "taco"
|
|
||||||
// b := stringAlias1("taco")
|
|
||||||
// c := stringAlias2("taco")
|
|
||||||
//
|
|
||||||
// ExpectTrue(a == b) // Legal, passes
|
|
||||||
// ExpectTrue(b == c) // Illegal, doesn't compile
|
|
||||||
//
|
|
||||||
// ExpectThat(a, Equals(b)) // Passes
|
|
||||||
// ExpectThat(b, Equals(c)) // Passes
|
|
||||||
//
|
|
||||||
// * Values of numeric type are treated as if they were abstract numbers, and
|
|
||||||
// compared accordingly. Therefore Equals(17) will match int(17),
|
|
||||||
// int16(17), uint(17), float32(17), complex64(17), and so on.
|
|
||||||
//
|
|
||||||
// If you want a stricter matcher that contains no such cleverness, see
|
|
||||||
// IdenticalTo instead.
|
|
||||||
//
|
|
||||||
// Arrays are supported by this matcher, but do not participate in the
|
|
||||||
// exceptions above. Two arrays compared with this matcher must have identical
|
|
||||||
// types, and their element type must itself be comparable according to Go's ==
|
|
||||||
// operator.
|
|
||||||
func Equals(x interface{}) Matcher {
|
|
||||||
v := reflect.ValueOf(x)
|
|
||||||
|
|
||||||
// This matcher doesn't support structs.
|
|
||||||
if v.Kind() == reflect.Struct {
|
|
||||||
panic(fmt.Sprintf("oglematchers.Equals: unsupported kind %v", v.Kind()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// The == operator is not defined for non-nil slices.
|
|
||||||
if v.Kind() == reflect.Slice && v.Pointer() != uintptr(0) {
|
|
||||||
panic(fmt.Sprintf("oglematchers.Equals: non-nil slice"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &equalsMatcher{v}
|
|
||||||
}
|
|
||||||
|
|
||||||
type equalsMatcher struct {
|
|
||||||
expectedValue reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
// Numeric types
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
func isSignedInteger(v reflect.Value) bool {
|
|
||||||
k := v.Kind()
|
|
||||||
return k >= reflect.Int && k <= reflect.Int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func isUnsignedInteger(v reflect.Value) bool {
|
|
||||||
k := v.Kind()
|
|
||||||
return k >= reflect.Uint && k <= reflect.Uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
func isInteger(v reflect.Value) bool {
|
|
||||||
return isSignedInteger(v) || isUnsignedInteger(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isFloat(v reflect.Value) bool {
|
|
||||||
k := v.Kind()
|
|
||||||
return k == reflect.Float32 || k == reflect.Float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func isComplex(v reflect.Value) bool {
|
|
||||||
k := v.Kind()
|
|
||||||
return k == reflect.Complex64 || k == reflect.Complex128
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstInt64(e int64, c reflect.Value) (err error) {
|
|
||||||
err = errors.New("")
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case isSignedInteger(c):
|
|
||||||
if c.Int() == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case isUnsignedInteger(c):
|
|
||||||
u := c.Uint()
|
|
||||||
if u <= math.MaxInt64 && int64(u) == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn around the various floating point types so that the checkAgainst*
|
|
||||||
// functions for them can deal with precision issues.
|
|
||||||
case isFloat(c), isComplex(c):
|
|
||||||
return Equals(c.Interface()).Matches(e)
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = NewFatalError("which is not numeric")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstUint64(e uint64, c reflect.Value) (err error) {
|
|
||||||
err = errors.New("")
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case isSignedInteger(c):
|
|
||||||
i := c.Int()
|
|
||||||
if i >= 0 && uint64(i) == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case isUnsignedInteger(c):
|
|
||||||
if c.Uint() == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn around the various floating point types so that the checkAgainst*
|
|
||||||
// functions for them can deal with precision issues.
|
|
||||||
case isFloat(c), isComplex(c):
|
|
||||||
return Equals(c.Interface()).Matches(e)
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = NewFatalError("which is not numeric")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstFloat32(e float32, c reflect.Value) (err error) {
|
|
||||||
err = errors.New("")
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case isSignedInteger(c):
|
|
||||||
if float32(c.Int()) == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case isUnsignedInteger(c):
|
|
||||||
if float32(c.Uint()) == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case isFloat(c):
|
|
||||||
// Compare using float32 to avoid a false sense of precision; otherwise
|
|
||||||
// e.g. Equals(float32(0.1)) won't match float32(0.1).
|
|
||||||
if float32(c.Float()) == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case isComplex(c):
|
|
||||||
comp := c.Complex()
|
|
||||||
rl := real(comp)
|
|
||||||
im := imag(comp)
|
|
||||||
|
|
||||||
// Compare using float32 to avoid a false sense of precision; otherwise
|
|
||||||
// e.g. Equals(float32(0.1)) won't match (0.1 + 0i).
|
|
||||||
if im == 0 && float32(rl) == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = NewFatalError("which is not numeric")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstFloat64(e float64, c reflect.Value) (err error) {
|
|
||||||
err = errors.New("")
|
|
||||||
|
|
||||||
ck := c.Kind()
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case isSignedInteger(c):
|
|
||||||
if float64(c.Int()) == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case isUnsignedInteger(c):
|
|
||||||
if float64(c.Uint()) == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the actual value is lower precision, turn the comparison around so we
|
|
||||||
// apply the low-precision rules. Otherwise, e.g. Equals(0.1) may not match
|
|
||||||
// float32(0.1).
|
|
||||||
case ck == reflect.Float32 || ck == reflect.Complex64:
|
|
||||||
return Equals(c.Interface()).Matches(e)
|
|
||||||
|
|
||||||
// Otherwise, compare with double precision.
|
|
||||||
case isFloat(c):
|
|
||||||
if c.Float() == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case isComplex(c):
|
|
||||||
comp := c.Complex()
|
|
||||||
rl := real(comp)
|
|
||||||
im := imag(comp)
|
|
||||||
|
|
||||||
if im == 0 && rl == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = NewFatalError("which is not numeric")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstComplex64(e complex64, c reflect.Value) (err error) {
|
|
||||||
err = errors.New("")
|
|
||||||
realPart := real(e)
|
|
||||||
imaginaryPart := imag(e)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case isInteger(c) || isFloat(c):
|
|
||||||
// If we have no imaginary part, then we should just compare against the
|
|
||||||
// real part. Otherwise, we can't be equal.
|
|
||||||
if imaginaryPart != 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return checkAgainstFloat32(realPart, c)
|
|
||||||
|
|
||||||
case isComplex(c):
|
|
||||||
// Compare using complex64 to avoid a false sense of precision; otherwise
|
|
||||||
// e.g. Equals(0.1 + 0i) won't match float32(0.1).
|
|
||||||
if complex64(c.Complex()) == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = NewFatalError("which is not numeric")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstComplex128(e complex128, c reflect.Value) (err error) {
|
|
||||||
err = errors.New("")
|
|
||||||
realPart := real(e)
|
|
||||||
imaginaryPart := imag(e)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case isInteger(c) || isFloat(c):
|
|
||||||
// If we have no imaginary part, then we should just compare against the
|
|
||||||
// real part. Otherwise, we can't be equal.
|
|
||||||
if imaginaryPart != 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return checkAgainstFloat64(realPart, c)
|
|
||||||
|
|
||||||
case isComplex(c):
|
|
||||||
if c.Complex() == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = NewFatalError("which is not numeric")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
// Other types
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
func checkAgainstBool(e bool, c reflect.Value) (err error) {
|
|
||||||
if c.Kind() != reflect.Bool {
|
|
||||||
err = NewFatalError("which is not a bool")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = errors.New("")
|
|
||||||
if c.Bool() == e {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstChan(e reflect.Value, c reflect.Value) (err error) {
|
|
||||||
// Create a description of e's type, e.g. "chan int".
|
|
||||||
typeStr := fmt.Sprintf("%s %s", e.Type().ChanDir(), e.Type().Elem())
|
|
||||||
|
|
||||||
// Make sure c is a chan of the correct type.
|
|
||||||
if c.Kind() != reflect.Chan ||
|
|
||||||
c.Type().ChanDir() != e.Type().ChanDir() ||
|
|
||||||
c.Type().Elem() != e.Type().Elem() {
|
|
||||||
err = NewFatalError(fmt.Sprintf("which is not a %s", typeStr))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = errors.New("")
|
|
||||||
if c.Pointer() == e.Pointer() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstFunc(e reflect.Value, c reflect.Value) (err error) {
|
|
||||||
// Make sure c is a function.
|
|
||||||
if c.Kind() != reflect.Func {
|
|
||||||
err = NewFatalError("which is not a function")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = errors.New("")
|
|
||||||
if c.Pointer() == e.Pointer() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstMap(e reflect.Value, c reflect.Value) (err error) {
|
|
||||||
// Make sure c is a map.
|
|
||||||
if c.Kind() != reflect.Map {
|
|
||||||
err = NewFatalError("which is not a map")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = errors.New("")
|
|
||||||
if c.Pointer() == e.Pointer() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstPtr(e reflect.Value, c reflect.Value) (err error) {
|
|
||||||
// Create a description of e's type, e.g. "*int".
|
|
||||||
typeStr := fmt.Sprintf("*%v", e.Type().Elem())
|
|
||||||
|
|
||||||
// Make sure c is a pointer of the correct type.
|
|
||||||
if c.Kind() != reflect.Ptr ||
|
|
||||||
c.Type().Elem() != e.Type().Elem() {
|
|
||||||
err = NewFatalError(fmt.Sprintf("which is not a %s", typeStr))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = errors.New("")
|
|
||||||
if c.Pointer() == e.Pointer() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstSlice(e reflect.Value, c reflect.Value) (err error) {
|
|
||||||
// Create a description of e's type, e.g. "[]int".
|
|
||||||
typeStr := fmt.Sprintf("[]%v", e.Type().Elem())
|
|
||||||
|
|
||||||
// Make sure c is a slice of the correct type.
|
|
||||||
if c.Kind() != reflect.Slice ||
|
|
||||||
c.Type().Elem() != e.Type().Elem() {
|
|
||||||
err = NewFatalError(fmt.Sprintf("which is not a %s", typeStr))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = errors.New("")
|
|
||||||
if c.Pointer() == e.Pointer() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstString(e reflect.Value, c reflect.Value) (err error) {
|
|
||||||
// Make sure c is a string.
|
|
||||||
if c.Kind() != reflect.String {
|
|
||||||
err = NewFatalError("which is not a string")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = errors.New("")
|
|
||||||
if c.String() == e.String() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstArray(e reflect.Value, c reflect.Value) (err error) {
|
|
||||||
// Create a description of e's type, e.g. "[2]int".
|
|
||||||
typeStr := fmt.Sprintf("%v", e.Type())
|
|
||||||
|
|
||||||
// Make sure c is the correct type.
|
|
||||||
if c.Type() != e.Type() {
|
|
||||||
err = NewFatalError(fmt.Sprintf("which is not %s", typeStr))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for equality.
|
|
||||||
if e.Interface() != c.Interface() {
|
|
||||||
err = errors.New("")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAgainstUnsafePointer(e reflect.Value, c reflect.Value) (err error) {
|
|
||||||
// Make sure c is a pointer.
|
|
||||||
if c.Kind() != reflect.UnsafePointer {
|
|
||||||
err = NewFatalError("which is not a unsafe.Pointer")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = errors.New("")
|
|
||||||
if c.Pointer() == e.Pointer() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkForNil(c reflect.Value) (err error) {
|
|
||||||
err = errors.New("")
|
|
||||||
|
|
||||||
// Make sure it is legal to call IsNil.
|
|
||||||
switch c.Kind() {
|
|
||||||
case reflect.Invalid:
|
|
||||||
case reflect.Chan:
|
|
||||||
case reflect.Func:
|
|
||||||
case reflect.Interface:
|
|
||||||
case reflect.Map:
|
|
||||||
case reflect.Ptr:
|
|
||||||
case reflect.Slice:
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = NewFatalError("which cannot be compared to nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask whether the value is nil. Handle a nil literal (kind Invalid)
|
|
||||||
// specially, since it's not legal to call IsNil there.
|
|
||||||
if c.Kind() == reflect.Invalid || c.IsNil() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public implementation
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
func (m *equalsMatcher) Matches(candidate interface{}) error {
|
|
||||||
e := m.expectedValue
|
|
||||||
c := reflect.ValueOf(candidate)
|
|
||||||
ek := e.Kind()
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case ek == reflect.Bool:
|
|
||||||
return checkAgainstBool(e.Bool(), c)
|
|
||||||
|
|
||||||
case isSignedInteger(e):
|
|
||||||
return checkAgainstInt64(e.Int(), c)
|
|
||||||
|
|
||||||
case isUnsignedInteger(e):
|
|
||||||
return checkAgainstUint64(e.Uint(), c)
|
|
||||||
|
|
||||||
case ek == reflect.Float32:
|
|
||||||
return checkAgainstFloat32(float32(e.Float()), c)
|
|
||||||
|
|
||||||
case ek == reflect.Float64:
|
|
||||||
return checkAgainstFloat64(e.Float(), c)
|
|
||||||
|
|
||||||
case ek == reflect.Complex64:
|
|
||||||
return checkAgainstComplex64(complex64(e.Complex()), c)
|
|
||||||
|
|
||||||
case ek == reflect.Complex128:
|
|
||||||
return checkAgainstComplex128(complex128(e.Complex()), c)
|
|
||||||
|
|
||||||
case ek == reflect.Chan:
|
|
||||||
return checkAgainstChan(e, c)
|
|
||||||
|
|
||||||
case ek == reflect.Func:
|
|
||||||
return checkAgainstFunc(e, c)
|
|
||||||
|
|
||||||
case ek == reflect.Map:
|
|
||||||
return checkAgainstMap(e, c)
|
|
||||||
|
|
||||||
case ek == reflect.Ptr:
|
|
||||||
return checkAgainstPtr(e, c)
|
|
||||||
|
|
||||||
case ek == reflect.Slice:
|
|
||||||
return checkAgainstSlice(e, c)
|
|
||||||
|
|
||||||
case ek == reflect.String:
|
|
||||||
return checkAgainstString(e, c)
|
|
||||||
|
|
||||||
case ek == reflect.Array:
|
|
||||||
return checkAgainstArray(e, c)
|
|
||||||
|
|
||||||
case ek == reflect.UnsafePointer:
|
|
||||||
return checkAgainstUnsafePointer(e, c)
|
|
||||||
|
|
||||||
case ek == reflect.Invalid:
|
|
||||||
return checkForNil(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
panic(fmt.Sprintf("equalsMatcher.Matches: unexpected kind: %v", ek))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *equalsMatcher) Description() string {
|
|
||||||
// Special case: handle nil.
|
|
||||||
if !m.expectedValue.IsValid() {
|
|
||||||
return "is nil"
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v", m.expectedValue.Interface())
|
|
||||||
}
|
|
51
vendor/github.com/smartystreets/assertions/internal/oglematchers/error.go
generated
vendored
51
vendor/github.com/smartystreets/assertions/internal/oglematchers/error.go
generated
vendored
@ -1,51 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
// Error returns a matcher that matches non-nil values implementing the
|
|
||||||
// built-in error interface for whom the return value of Error() matches the
|
|
||||||
// supplied matcher.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
//
|
|
||||||
// err := errors.New("taco burrito")
|
|
||||||
//
|
|
||||||
// Error(Equals("taco burrito")) // matches err
|
|
||||||
// Error(HasSubstr("taco")) // matches err
|
|
||||||
// Error(HasSubstr("enchilada")) // doesn't match err
|
|
||||||
//
|
|
||||||
func Error(m Matcher) Matcher {
|
|
||||||
return &errorMatcher{m}
|
|
||||||
}
|
|
||||||
|
|
||||||
type errorMatcher struct {
|
|
||||||
wrappedMatcher Matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *errorMatcher) Description() string {
|
|
||||||
return "error " + m.wrappedMatcher.Description()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *errorMatcher) Matches(c interface{}) error {
|
|
||||||
// Make sure that c is an error.
|
|
||||||
e, ok := c.(error)
|
|
||||||
if !ok {
|
|
||||||
return NewFatalError("which is not an error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass on the error text to the wrapped matcher.
|
|
||||||
return m.wrappedMatcher.Matches(e.Error())
|
|
||||||
}
|
|
39
vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go
generated
vendored
39
vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go
generated
vendored
@ -1,39 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GreaterOrEqual returns a matcher that matches integer, floating point, or
|
|
||||||
// strings values v such that v >= x. Comparison is not defined between numeric
|
|
||||||
// and string types, but is defined between all integer and floating point
|
|
||||||
// types.
|
|
||||||
//
|
|
||||||
// x must itself be an integer, floating point, or string type; otherwise,
|
|
||||||
// GreaterOrEqual will panic.
|
|
||||||
func GreaterOrEqual(x interface{}) Matcher {
|
|
||||||
desc := fmt.Sprintf("greater than or equal to %v", x)
|
|
||||||
|
|
||||||
// Special case: make it clear that strings are strings.
|
|
||||||
if reflect.TypeOf(x).Kind() == reflect.String {
|
|
||||||
desc = fmt.Sprintf("greater than or equal to \"%s\"", x)
|
|
||||||
}
|
|
||||||
|
|
||||||
return transformDescription(Not(LessThan(x)), desc)
|
|
||||||
}
|
|
39
vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go
generated
vendored
39
vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go
generated
vendored
@ -1,39 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GreaterThan returns a matcher that matches integer, floating point, or
|
|
||||||
// strings values v such that v > x. Comparison is not defined between numeric
|
|
||||||
// and string types, but is defined between all integer and floating point
|
|
||||||
// types.
|
|
||||||
//
|
|
||||||
// x must itself be an integer, floating point, or string type; otherwise,
|
|
||||||
// GreaterThan will panic.
|
|
||||||
func GreaterThan(x interface{}) Matcher {
|
|
||||||
desc := fmt.Sprintf("greater than %v", x)
|
|
||||||
|
|
||||||
// Special case: make it clear that strings are strings.
|
|
||||||
if reflect.TypeOf(x).Kind() == reflect.String {
|
|
||||||
desc = fmt.Sprintf("greater than \"%s\"", x)
|
|
||||||
}
|
|
||||||
|
|
||||||
return transformDescription(Not(LessOrEqual(x)), desc)
|
|
||||||
}
|
|
37
vendor/github.com/smartystreets/assertions/internal/oglematchers/has_same_type_as.go
generated
vendored
37
vendor/github.com/smartystreets/assertions/internal/oglematchers/has_same_type_as.go
generated
vendored
@ -1,37 +0,0 @@
|
|||||||
// Copyright 2015 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HasSameTypeAs returns a matcher that matches values with exactly the same
|
|
||||||
// type as the supplied prototype.
|
|
||||||
func HasSameTypeAs(p interface{}) Matcher {
|
|
||||||
expected := reflect.TypeOf(p)
|
|
||||||
pred := func(c interface{}) error {
|
|
||||||
actual := reflect.TypeOf(c)
|
|
||||||
if actual != expected {
|
|
||||||
return fmt.Errorf("which has type %v", actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewMatcher(pred, fmt.Sprintf("has type %v", expected))
|
|
||||||
}
|
|
46
vendor/github.com/smartystreets/assertions/internal/oglematchers/has_substr.go
generated
vendored
46
vendor/github.com/smartystreets/assertions/internal/oglematchers/has_substr.go
generated
vendored
@ -1,46 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HasSubstr returns a matcher that matches strings containing s as a
|
|
||||||
// substring.
|
|
||||||
func HasSubstr(s string) Matcher {
|
|
||||||
return NewMatcher(
|
|
||||||
func(c interface{}) error { return hasSubstr(s, c) },
|
|
||||||
fmt.Sprintf("has substring \"%s\"", s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasSubstr(needle string, c interface{}) error {
|
|
||||||
v := reflect.ValueOf(c)
|
|
||||||
if v.Kind() != reflect.String {
|
|
||||||
return NewFatalError("which is not a string")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform the substring search.
|
|
||||||
haystack := v.String()
|
|
||||||
if strings.Contains(haystack, needle) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("")
|
|
||||||
}
|
|
134
vendor/github.com/smartystreets/assertions/internal/oglematchers/identical_to.go
generated
vendored
134
vendor/github.com/smartystreets/assertions/internal/oglematchers/identical_to.go
generated
vendored
@ -1,134 +0,0 @@
|
|||||||
// Copyright 2012 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Is the type comparable according to the definition here?
|
|
||||||
//
|
|
||||||
// http://weekly.golang.org/doc/go_spec.html#Comparison_operators
|
|
||||||
//
|
|
||||||
func isComparable(t reflect.Type) bool {
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Array:
|
|
||||||
return isComparable(t.Elem())
|
|
||||||
|
|
||||||
case reflect.Struct:
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
if !isComparable(t.Field(i).Type) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
case reflect.Slice, reflect.Map, reflect.Func:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should the supplied type be allowed as an argument to IdenticalTo?
|
|
||||||
func isLegalForIdenticalTo(t reflect.Type) (bool, error) {
|
|
||||||
// Allow the zero type.
|
|
||||||
if t == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference types are always okay; we compare pointers.
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reject other non-comparable types.
|
|
||||||
if !isComparable(t) {
|
|
||||||
return false, errors.New(fmt.Sprintf("%v is not comparable", t))
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IdenticalTo(x) returns a matcher that matches values v with type identical
|
|
||||||
// to x such that:
|
|
||||||
//
|
|
||||||
// 1. If v and x are of a reference type (slice, map, function, channel), then
|
|
||||||
// they are either both nil or are references to the same object.
|
|
||||||
//
|
|
||||||
// 2. Otherwise, if v and x are not of a reference type but have a valid type,
|
|
||||||
// then v == x.
|
|
||||||
//
|
|
||||||
// If v and x are both the invalid type (which results from the predeclared nil
|
|
||||||
// value, or from nil interface variables), then the matcher is satisfied.
|
|
||||||
//
|
|
||||||
// This function will panic if x is of a value type that is not comparable. For
|
|
||||||
// example, x cannot be an array of functions.
|
|
||||||
func IdenticalTo(x interface{}) Matcher {
|
|
||||||
t := reflect.TypeOf(x)
|
|
||||||
|
|
||||||
// Reject illegal arguments.
|
|
||||||
if ok, err := isLegalForIdenticalTo(t); !ok {
|
|
||||||
panic("IdenticalTo: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &identicalToMatcher{x}
|
|
||||||
}
|
|
||||||
|
|
||||||
type identicalToMatcher struct {
|
|
||||||
x interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *identicalToMatcher) Description() string {
|
|
||||||
t := reflect.TypeOf(m.x)
|
|
||||||
return fmt.Sprintf("identical to <%v> %v", t, m.x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *identicalToMatcher) Matches(c interface{}) error {
|
|
||||||
// Make sure the candidate's type is correct.
|
|
||||||
t := reflect.TypeOf(m.x)
|
|
||||||
if ct := reflect.TypeOf(c); t != ct {
|
|
||||||
return NewFatalError(fmt.Sprintf("which is of type %v", ct))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case: two values of the invalid type are always identical.
|
|
||||||
if t == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle reference types.
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
|
|
||||||
xv := reflect.ValueOf(m.x)
|
|
||||||
cv := reflect.ValueOf(c)
|
|
||||||
if xv.Pointer() == cv.Pointer() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("which is not an identical reference")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Are the values equal?
|
|
||||||
if m.x == c {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("")
|
|
||||||
}
|
|
41
vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go
generated
vendored
41
vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go
generated
vendored
@ -1,41 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LessOrEqual returns a matcher that matches integer, floating point, or
|
|
||||||
// strings values v such that v <= x. Comparison is not defined between numeric
|
|
||||||
// and string types, but is defined between all integer and floating point
|
|
||||||
// types.
|
|
||||||
//
|
|
||||||
// x must itself be an integer, floating point, or string type; otherwise,
|
|
||||||
// LessOrEqual will panic.
|
|
||||||
func LessOrEqual(x interface{}) Matcher {
|
|
||||||
desc := fmt.Sprintf("less than or equal to %v", x)
|
|
||||||
|
|
||||||
// Special case: make it clear that strings are strings.
|
|
||||||
if reflect.TypeOf(x).Kind() == reflect.String {
|
|
||||||
desc = fmt.Sprintf("less than or equal to \"%s\"", x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put LessThan last so that its error messages will be used in the event of
|
|
||||||
// failure.
|
|
||||||
return transformDescription(AnyOf(Equals(x), LessThan(x)), desc)
|
|
||||||
}
|
|
152
vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go
generated
vendored
152
vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go
generated
vendored
@ -1,152 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LessThan returns a matcher that matches integer, floating point, or strings
|
|
||||||
// values v such that v < x. Comparison is not defined between numeric and
|
|
||||||
// string types, but is defined between all integer and floating point types.
|
|
||||||
//
|
|
||||||
// x must itself be an integer, floating point, or string type; otherwise,
|
|
||||||
// LessThan will panic.
|
|
||||||
func LessThan(x interface{}) Matcher {
|
|
||||||
v := reflect.ValueOf(x)
|
|
||||||
kind := v.Kind()
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case isInteger(v):
|
|
||||||
case isFloat(v):
|
|
||||||
case kind == reflect.String:
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("LessThan: unexpected kind %v", kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &lessThanMatcher{v}
|
|
||||||
}
|
|
||||||
|
|
||||||
type lessThanMatcher struct {
|
|
||||||
limit reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *lessThanMatcher) Description() string {
|
|
||||||
// Special case: make it clear that strings are strings.
|
|
||||||
if m.limit.Kind() == reflect.String {
|
|
||||||
return fmt.Sprintf("less than \"%s\"", m.limit.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("less than %v", m.limit.Interface())
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareIntegers(v1, v2 reflect.Value) (err error) {
|
|
||||||
err = errors.New("")
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case isSignedInteger(v1) && isSignedInteger(v2):
|
|
||||||
if v1.Int() < v2.Int() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
case isSignedInteger(v1) && isUnsignedInteger(v2):
|
|
||||||
if v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
case isUnsignedInteger(v1) && isSignedInteger(v2):
|
|
||||||
if v1.Uint() <= math.MaxInt64 && int64(v1.Uint()) < v2.Int() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
case isUnsignedInteger(v1) && isUnsignedInteger(v2):
|
|
||||||
if v1.Uint() < v2.Uint() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
panic(fmt.Sprintf("compareIntegers: %v %v", v1, v2))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFloat(v reflect.Value) float64 {
|
|
||||||
switch {
|
|
||||||
case isSignedInteger(v):
|
|
||||||
return float64(v.Int())
|
|
||||||
|
|
||||||
case isUnsignedInteger(v):
|
|
||||||
return float64(v.Uint())
|
|
||||||
|
|
||||||
case isFloat(v):
|
|
||||||
return v.Float()
|
|
||||||
}
|
|
||||||
|
|
||||||
panic(fmt.Sprintf("getFloat: %v", v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *lessThanMatcher) Matches(c interface{}) (err error) {
|
|
||||||
v1 := reflect.ValueOf(c)
|
|
||||||
v2 := m.limit
|
|
||||||
|
|
||||||
err = errors.New("")
|
|
||||||
|
|
||||||
// Handle strings as a special case.
|
|
||||||
if v1.Kind() == reflect.String && v2.Kind() == reflect.String {
|
|
||||||
if v1.String() < v2.String() {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we get here, we require that we are dealing with integers or floats.
|
|
||||||
v1Legal := isInteger(v1) || isFloat(v1)
|
|
||||||
v2Legal := isInteger(v2) || isFloat(v2)
|
|
||||||
if !v1Legal || !v2Legal {
|
|
||||||
err = NewFatalError("which is not comparable")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the various comparison cases.
|
|
||||||
switch {
|
|
||||||
// Both integers
|
|
||||||
case isInteger(v1) && isInteger(v2):
|
|
||||||
return compareIntegers(v1, v2)
|
|
||||||
|
|
||||||
// At least one float32
|
|
||||||
case v1.Kind() == reflect.Float32 || v2.Kind() == reflect.Float32:
|
|
||||||
if float32(getFloat(v1)) < float32(getFloat(v2)) {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
// At least one float64
|
|
||||||
case v1.Kind() == reflect.Float64 || v2.Kind() == reflect.Float64:
|
|
||||||
if getFloat(v1) < getFloat(v2) {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// We shouldn't get here.
|
|
||||||
panic(fmt.Sprintf("lessThanMatcher.Matches: Shouldn't get here: %v %v", v1, v2))
|
|
||||||
}
|
|
86
vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go
generated
vendored
86
vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go
generated
vendored
@ -1,86 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package oglematchers provides a set of matchers useful in a testing or
|
|
||||||
// mocking framework. These matchers are inspired by and mostly compatible with
|
|
||||||
// Google Test for C++ and Google JS Test.
|
|
||||||
//
|
|
||||||
// This package is used by github.com/smartystreets/assertions/internal/ogletest and
|
|
||||||
// github.com/smartystreets/assertions/internal/oglemock, which may be more directly useful if you're not
|
|
||||||
// writing your own testing package or defining your own matchers.
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
// A Matcher is some predicate implicitly defining a set of values that it
|
|
||||||
// matches. For example, GreaterThan(17) matches all numeric values greater
|
|
||||||
// than 17, and HasSubstr("taco") matches all strings with the substring
|
|
||||||
// "taco".
|
|
||||||
//
|
|
||||||
// Matchers are typically exposed to tests via constructor functions like
|
|
||||||
// HasSubstr. In order to implement such a function you can either define your
|
|
||||||
// own matcher type or use NewMatcher.
|
|
||||||
type Matcher interface {
|
|
||||||
// Check whether the supplied value belongs to the the set defined by the
|
|
||||||
// matcher. Return a non-nil error if and only if it does not.
|
|
||||||
//
|
|
||||||
// The error describes why the value doesn't match. The error text is a
|
|
||||||
// relative clause that is suitable for being placed after the value. For
|
|
||||||
// example, a predicate that matches strings with a particular substring may,
|
|
||||||
// when presented with a numerical value, return the following error text:
|
|
||||||
//
|
|
||||||
// "which is not a string"
|
|
||||||
//
|
|
||||||
// Then the failure message may look like:
|
|
||||||
//
|
|
||||||
// Expected: has substring "taco"
|
|
||||||
// Actual: 17, which is not a string
|
|
||||||
//
|
|
||||||
// If the error is self-apparent based on the description of the matcher, the
|
|
||||||
// error text may be empty (but the error still non-nil). For example:
|
|
||||||
//
|
|
||||||
// Expected: 17
|
|
||||||
// Actual: 19
|
|
||||||
//
|
|
||||||
// If you are implementing a new matcher, see also the documentation on
|
|
||||||
// FatalError.
|
|
||||||
Matches(candidate interface{}) error
|
|
||||||
|
|
||||||
// Description returns a string describing the property that values matching
|
|
||||||
// this matcher have, as a verb phrase where the subject is the value. For
|
|
||||||
// example, "is greather than 17" or "has substring "taco"".
|
|
||||||
Description() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FatalError is an implementation of the error interface that may be returned
|
|
||||||
// from matchers, indicating the error should be propagated. Returning a
|
|
||||||
// *FatalError indicates that the matcher doesn't process values of the
|
|
||||||
// supplied type, or otherwise doesn't know how to handle the value.
|
|
||||||
//
|
|
||||||
// For example, if GreaterThan(17) returned false for the value "taco" without
|
|
||||||
// a fatal error, then Not(GreaterThan(17)) would return true. This is
|
|
||||||
// technically correct, but is surprising and may mask failures where the wrong
|
|
||||||
// sort of matcher is accidentally used. Instead, GreaterThan(17) can return a
|
|
||||||
// fatal error, which will be propagated by Not().
|
|
||||||
type FatalError struct {
|
|
||||||
errorText string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFatalError creates a FatalError struct with the supplied error text.
|
|
||||||
func NewFatalError(s string) *FatalError {
|
|
||||||
return &FatalError{s}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *FatalError) Error() string {
|
|
||||||
return e.errorText
|
|
||||||
}
|
|
69
vendor/github.com/smartystreets/assertions/internal/oglematchers/matches_regexp.go
generated
vendored
69
vendor/github.com/smartystreets/assertions/internal/oglematchers/matches_regexp.go
generated
vendored
@ -1,69 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MatchesRegexp returns a matcher that matches strings and byte slices whose
|
|
||||||
// contents match the supplied regular expression. The semantics are those of
|
|
||||||
// regexp.Match. In particular, that means the match is not implicitly anchored
|
|
||||||
// to the ends of the string: MatchesRegexp("bar") will match "foo bar baz".
|
|
||||||
func MatchesRegexp(pattern string) Matcher {
|
|
||||||
re, err := regexp.Compile(pattern)
|
|
||||||
if err != nil {
|
|
||||||
panic("MatchesRegexp: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &matchesRegexpMatcher{re}
|
|
||||||
}
|
|
||||||
|
|
||||||
type matchesRegexpMatcher struct {
|
|
||||||
re *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *matchesRegexpMatcher) Description() string {
|
|
||||||
return fmt.Sprintf("matches regexp \"%s\"", m.re.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *matchesRegexpMatcher) Matches(c interface{}) (err error) {
|
|
||||||
v := reflect.ValueOf(c)
|
|
||||||
isString := v.Kind() == reflect.String
|
|
||||||
isByteSlice := v.Kind() == reflect.Slice && v.Elem().Kind() == reflect.Uint8
|
|
||||||
|
|
||||||
err = errors.New("")
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case isString:
|
|
||||||
if m.re.MatchString(v.String()) {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case isByteSlice:
|
|
||||||
if m.re.Match(v.Bytes()) {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = NewFatalError("which is not a string or []byte")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
43
vendor/github.com/smartystreets/assertions/internal/oglematchers/new_matcher.go
generated
vendored
43
vendor/github.com/smartystreets/assertions/internal/oglematchers/new_matcher.go
generated
vendored
@ -1,43 +0,0 @@
|
|||||||
// Copyright 2015 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
// Create a matcher with the given description and predicate function, which
|
|
||||||
// will be invoked to handle calls to Matchers.
|
|
||||||
//
|
|
||||||
// Using this constructor may be a convenience over defining your own type that
|
|
||||||
// implements Matcher if you do not need any logic in your Description method.
|
|
||||||
func NewMatcher(
|
|
||||||
predicate func(interface{}) error,
|
|
||||||
description string) Matcher {
|
|
||||||
return &predicateMatcher{
|
|
||||||
predicate: predicate,
|
|
||||||
description: description,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type predicateMatcher struct {
|
|
||||||
predicate func(interface{}) error
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pm *predicateMatcher) Matches(c interface{}) error {
|
|
||||||
return pm.predicate(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pm *predicateMatcher) Description() string {
|
|
||||||
return pm.description
|
|
||||||
}
|
|
53
vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go
generated
vendored
53
vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go
generated
vendored
@ -1,53 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Not returns a matcher that inverts the set of values matched by the wrapped
|
|
||||||
// matcher. It does not transform the result for values for which the wrapped
|
|
||||||
// matcher returns a fatal error.
|
|
||||||
func Not(m Matcher) Matcher {
|
|
||||||
return ¬Matcher{m}
|
|
||||||
}
|
|
||||||
|
|
||||||
type notMatcher struct {
|
|
||||||
wrapped Matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *notMatcher) Matches(c interface{}) (err error) {
|
|
||||||
err = m.wrapped.Matches(c)
|
|
||||||
|
|
||||||
// Did the wrapped matcher say yes?
|
|
||||||
if err == nil {
|
|
||||||
return errors.New("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Did the wrapped matcher return a fatal error?
|
|
||||||
if _, isFatal := err.(*FatalError); isFatal {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The wrapped matcher returned a non-fatal error.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *notMatcher) Description() string {
|
|
||||||
return fmt.Sprintf("not(%s)", m.wrapped.Description())
|
|
||||||
}
|
|
74
vendor/github.com/smartystreets/assertions/internal/oglematchers/panics.go
generated
vendored
74
vendor/github.com/smartystreets/assertions/internal/oglematchers/panics.go
generated
vendored
@ -1,74 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Panics matches zero-arg functions which, when invoked, panic with an error
|
|
||||||
// that matches the supplied matcher.
|
|
||||||
//
|
|
||||||
// NOTE(jacobsa): This matcher cannot detect the case where the function panics
|
|
||||||
// using panic(nil), by design of the language. See here for more info:
|
|
||||||
//
|
|
||||||
// http://goo.gl/9aIQL
|
|
||||||
//
|
|
||||||
func Panics(m Matcher) Matcher {
|
|
||||||
return &panicsMatcher{m}
|
|
||||||
}
|
|
||||||
|
|
||||||
type panicsMatcher struct {
|
|
||||||
wrappedMatcher Matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *panicsMatcher) Description() string {
|
|
||||||
return "panics with: " + m.wrappedMatcher.Description()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *panicsMatcher) Matches(c interface{}) (err error) {
|
|
||||||
// Make sure c is a zero-arg function.
|
|
||||||
v := reflect.ValueOf(c)
|
|
||||||
if v.Kind() != reflect.Func || v.Type().NumIn() != 0 {
|
|
||||||
err = NewFatalError("which is not a zero-arg function")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the function and check its panic error.
|
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
err = m.wrappedMatcher.Matches(e)
|
|
||||||
|
|
||||||
// Set a clearer error message if the matcher said no.
|
|
||||||
if err != nil {
|
|
||||||
wrappedClause := ""
|
|
||||||
if err.Error() != "" {
|
|
||||||
wrappedClause = ", " + err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = errors.New(fmt.Sprintf("which panicked with: %v%s", e, wrappedClause))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
v.Call([]reflect.Value{})
|
|
||||||
|
|
||||||
// If we get here, the function didn't panic.
|
|
||||||
err = errors.New("which didn't panic")
|
|
||||||
return
|
|
||||||
}
|
|
65
vendor/github.com/smartystreets/assertions/internal/oglematchers/pointee.go
generated
vendored
65
vendor/github.com/smartystreets/assertions/internal/oglematchers/pointee.go
generated
vendored
@ -1,65 +0,0 @@
|
|||||||
// Copyright 2012 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Return a matcher that matches non-nil pointers whose pointee matches the
|
|
||||||
// wrapped matcher.
|
|
||||||
func Pointee(m Matcher) Matcher {
|
|
||||||
return &pointeeMatcher{m}
|
|
||||||
}
|
|
||||||
|
|
||||||
type pointeeMatcher struct {
|
|
||||||
wrapped Matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *pointeeMatcher) Matches(c interface{}) (err error) {
|
|
||||||
// Make sure the candidate is of the appropriate type.
|
|
||||||
cv := reflect.ValueOf(c)
|
|
||||||
if !cv.IsValid() || cv.Kind() != reflect.Ptr {
|
|
||||||
return NewFatalError("which is not a pointer")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the candidate is non-nil.
|
|
||||||
if cv.IsNil() {
|
|
||||||
return NewFatalError("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defer to the wrapped matcher. Fix up empty errors so that failure messages
|
|
||||||
// are more helpful than just printing a pointer for "Actual".
|
|
||||||
pointee := cv.Elem().Interface()
|
|
||||||
err = m.wrapped.Matches(pointee)
|
|
||||||
if err != nil && err.Error() == "" {
|
|
||||||
s := fmt.Sprintf("whose pointee is %v", pointee)
|
|
||||||
|
|
||||||
if _, ok := err.(*FatalError); ok {
|
|
||||||
err = NewFatalError(s)
|
|
||||||
} else {
|
|
||||||
err = errors.New(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *pointeeMatcher) Description() string {
|
|
||||||
return fmt.Sprintf("pointee(%s)", m.wrapped.Description())
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
|
|
||||||
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package oglematchers
|
|
||||||
|
|
||||||
// transformDescription returns a matcher that is equivalent to the supplied
|
|
||||||
// one, except that it has the supplied description instead of the one attached
|
|
||||||
// to the existing matcher.
|
|
||||||
func transformDescription(m Matcher, newDesc string) Matcher {
|
|
||||||
return &transformDescriptionMatcher{newDesc, m}
|
|
||||||
}
|
|
||||||
|
|
||||||
type transformDescriptionMatcher struct {
|
|
||||||
desc string
|
|
||||||
wrappedMatcher Matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *transformDescriptionMatcher) Description() string {
|
|
||||||
return m.desc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *transformDescriptionMatcher) Matches(c interface{}) error {
|
|
||||||
return m.wrappedMatcher.Matches(c)
|
|
||||||
}
|
|
94
vendor/github.com/smartystreets/assertions/messages.go
generated
vendored
94
vendor/github.com/smartystreets/assertions/messages.go
generated
vendored
@ -1,94 +0,0 @@
|
|||||||
package assertions
|
|
||||||
|
|
||||||
const ( // equality
|
|
||||||
shouldHaveBeenEqual = "Expected: '%v'\nActual: '%v'\n(Should be equal)"
|
|
||||||
shouldNotHaveBeenEqual = "Expected '%v'\nto NOT equal '%v'\n(but it did)!"
|
|
||||||
shouldHaveBeenEqualTypeMismatch = "Expected: '%v' (%T)\nActual: '%v' (%T)\n(Should be equal, type mismatch)"
|
|
||||||
shouldHaveBeenAlmostEqual = "Expected '%v' to almost equal '%v' (but it didn't)!"
|
|
||||||
shouldHaveNotBeenAlmostEqual = "Expected '%v' to NOT almost equal '%v' (but it did)!"
|
|
||||||
shouldHaveResembled = "Expected: '%#v'\nActual: '%#v'\n(Should resemble)!"
|
|
||||||
shouldHaveResembledTypeMismatch = "Expected: '%#v' (%T)\nActual: '%#v' (%T)\n(Should resemble, type mismatch)"
|
|
||||||
shouldNotHaveResembled = "Expected '%#v'\nto NOT resemble '%#v'\n(but it did)!"
|
|
||||||
shouldBePointers = "Both arguments should be pointers "
|
|
||||||
shouldHaveBeenNonNilPointer = shouldBePointers + "(the %s was %s)!"
|
|
||||||
shouldHavePointedTo = "Expected '%+v' (address: '%v') and '%+v' (address: '%v') to be the same address (but their weren't)!"
|
|
||||||
shouldNotHavePointedTo = "Expected '%+v' and '%+v' to be different references (but they matched: '%v')!"
|
|
||||||
shouldHaveBeenNil = "Expected: nil\nActual: '%v'"
|
|
||||||
shouldNotHaveBeenNil = "Expected '%+v' to NOT be nil (but it was)!"
|
|
||||||
shouldHaveBeenTrue = "Expected: true\nActual: %v"
|
|
||||||
shouldHaveBeenFalse = "Expected: false\nActual: %v"
|
|
||||||
shouldHaveBeenZeroValue = "'%+v' should have been the zero value" //"Expected: (zero value)\nActual: %v"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ( // quantity comparisons
|
|
||||||
shouldHaveBeenGreater = "Expected '%v' to be greater than '%v' (but it wasn't)!"
|
|
||||||
shouldHaveBeenGreaterOrEqual = "Expected '%v' to be greater than or equal to '%v' (but it wasn't)!"
|
|
||||||
shouldHaveBeenLess = "Expected '%v' to be less than '%v' (but it wasn't)!"
|
|
||||||
shouldHaveBeenLessOrEqual = "Expected '%v' to be less than or equal to '%v' (but it wasn't)!"
|
|
||||||
shouldHaveBeenBetween = "Expected '%v' to be between '%v' and '%v' (but it wasn't)!"
|
|
||||||
shouldNotHaveBeenBetween = "Expected '%v' NOT to be between '%v' and '%v' (but it was)!"
|
|
||||||
shouldHaveDifferentUpperAndLower = "The lower and upper bounds must be different values (they were both '%v')."
|
|
||||||
shouldHaveBeenBetweenOrEqual = "Expected '%v' to be between '%v' and '%v' or equal to one of them (but it wasn't)!"
|
|
||||||
shouldNotHaveBeenBetweenOrEqual = "Expected '%v' NOT to be between '%v' and '%v' or equal to one of them (but it was)!"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ( // collections
|
|
||||||
shouldHaveContained = "Expected the container (%v) to contain: '%v' (but it didn't)!"
|
|
||||||
shouldNotHaveContained = "Expected the container (%v) NOT to contain: '%v' (but it did)!"
|
|
||||||
shouldHaveContainedKey = "Expected the %v to contain the key: %v (but it didn't)!"
|
|
||||||
shouldNotHaveContainedKey = "Expected the %v NOT to contain the key: %v (but it did)!"
|
|
||||||
shouldHaveBeenIn = "Expected '%v' to be in the container (%v), but it wasn't!"
|
|
||||||
shouldNotHaveBeenIn = "Expected '%v' NOT to be in the container (%v), but it was!"
|
|
||||||
shouldHaveBeenAValidCollection = "You must provide a valid container (was %v)!"
|
|
||||||
shouldHaveBeenAValidMap = "You must provide a valid map type (was %v)!"
|
|
||||||
shouldHaveBeenEmpty = "Expected %+v to be empty (but it wasn't)!"
|
|
||||||
shouldNotHaveBeenEmpty = "Expected %+v to NOT be empty (but it was)!"
|
|
||||||
shouldHaveBeenAValidInteger = "You must provide a valid integer (was %v)!"
|
|
||||||
shouldHaveBeenAValidLength = "You must provide a valid positive integer (was %v)!"
|
|
||||||
shouldHaveHadLength = "Expected %+v to have length equal to '%v', but it wasn't!"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ( // strings
|
|
||||||
shouldHaveStartedWith = "Expected '%v'\nto start with '%v'\n(but it didn't)!"
|
|
||||||
shouldNotHaveStartedWith = "Expected '%v'\nNOT to start with '%v'\n(but it did)!"
|
|
||||||
shouldHaveEndedWith = "Expected '%v'\nto end with '%v'\n(but it didn't)!"
|
|
||||||
shouldNotHaveEndedWith = "Expected '%v'\nNOT to end with '%v'\n(but it did)!"
|
|
||||||
shouldAllBeStrings = "All arguments to this assertion must be strings (you provided: %v)."
|
|
||||||
shouldBothBeStrings = "Both arguments to this assertion must be strings (you provided %v and %v)."
|
|
||||||
shouldBeString = "The argument to this assertion must be a string (you provided %v)."
|
|
||||||
shouldHaveContainedSubstring = "Expected '%s' to contain substring '%s' (but it didn't)!"
|
|
||||||
shouldNotHaveContainedSubstring = "Expected '%s' NOT to contain substring '%s' (but it did)!"
|
|
||||||
shouldHaveBeenBlank = "Expected '%s' to be blank (but it wasn't)!"
|
|
||||||
shouldNotHaveBeenBlank = "Expected value to NOT be blank (but it was)!"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ( // panics
|
|
||||||
shouldUseVoidNiladicFunction = "You must provide a void, niladic function as the first argument!"
|
|
||||||
shouldHavePanickedWith = "Expected func() to panic with '%v' (but it panicked with '%v')!"
|
|
||||||
shouldHavePanicked = "Expected func() to panic (but it didn't)!"
|
|
||||||
shouldNotHavePanicked = "Expected func() NOT to panic (error: '%+v')!"
|
|
||||||
shouldNotHavePanickedWith = "Expected func() NOT to panic with '%v' (but it did)!"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ( // type checking
|
|
||||||
shouldHaveBeenA = "Expected '%v' to be: '%v' (but was: '%v')!"
|
|
||||||
shouldNotHaveBeenA = "Expected '%v' to NOT be: '%v' (but it was)!"
|
|
||||||
|
|
||||||
shouldHaveImplemented = "Expected: '%v interface support'\nActual: '%v' does not implement the interface!"
|
|
||||||
shouldNotHaveImplemented = "Expected '%v'\nto NOT implement '%v'\n(but it did)!"
|
|
||||||
shouldCompareWithInterfacePointer = "The expected value must be a pointer to an interface type (eg. *fmt.Stringer)"
|
|
||||||
shouldNotBeNilActual = "The actual value was 'nil' and should be a value or a pointer to a value!"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ( // time comparisons
|
|
||||||
shouldUseTimes = "You must provide time instances as arguments to this assertion."
|
|
||||||
shouldUseTimeSlice = "You must provide a slice of time instances as the first argument to this assertion."
|
|
||||||
shouldUseDurationAndTime = "You must provide a duration and a time as arguments to this assertion."
|
|
||||||
shouldHaveHappenedBefore = "Expected '%v' to happen before '%v' (it happened '%v' after)!"
|
|
||||||
shouldHaveHappenedAfter = "Expected '%v' to happen after '%v' (it happened '%v' before)!"
|
|
||||||
shouldHaveHappenedBetween = "Expected '%v' to happen between '%v' and '%v' (it happened '%v' outside threshold)!"
|
|
||||||
shouldNotHaveHappenedOnOrBetween = "Expected '%v' to NOT happen on or between '%v' and '%v' (but it did)!"
|
|
||||||
|
|
||||||
// format params: incorrect-index, previous-index, previous-time, incorrect-index, incorrect-time
|
|
||||||
shouldHaveBeenChronological = "The 'Time' at index [%d] should have happened after the previous one (but it didn't!):\n [%d]: %s\n [%d]: %s (see, it happened before!)"
|
|
||||||
)
|
|
115
vendor/github.com/smartystreets/assertions/panic.go
generated
vendored
115
vendor/github.com/smartystreets/assertions/panic.go
generated
vendored
@ -1,115 +0,0 @@
|
|||||||
package assertions
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// ShouldPanic receives a void, niladic function and expects to recover a panic.
|
|
||||||
func ShouldPanic(actual interface{}, expected ...interface{}) (message string) {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
action, _ := actual.(func())
|
|
||||||
|
|
||||||
if action == nil {
|
|
||||||
message = shouldUseVoidNiladicFunction
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
recovered := recover()
|
|
||||||
if recovered == nil {
|
|
||||||
message = shouldHavePanicked
|
|
||||||
} else {
|
|
||||||
message = success
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
action()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotPanic receives a void, niladic function and expects to execute the function without any panic.
|
|
||||||
func ShouldNotPanic(actual interface{}, expected ...interface{}) (message string) {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
action, _ := actual.(func())
|
|
||||||
|
|
||||||
if action == nil {
|
|
||||||
message = shouldUseVoidNiladicFunction
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
recovered := recover()
|
|
||||||
if recovered != nil {
|
|
||||||
message = fmt.Sprintf(shouldNotHavePanicked, recovered)
|
|
||||||
} else {
|
|
||||||
message = success
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
action()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldPanicWith receives a void, niladic function and expects to recover a panic with the second argument as the content.
|
|
||||||
func ShouldPanicWith(actual interface{}, expected ...interface{}) (message string) {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
action, _ := actual.(func())
|
|
||||||
|
|
||||||
if action == nil {
|
|
||||||
message = shouldUseVoidNiladicFunction
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
recovered := recover()
|
|
||||||
if recovered == nil {
|
|
||||||
message = shouldHavePanicked
|
|
||||||
} else {
|
|
||||||
if equal := ShouldEqual(recovered, expected[0]); equal != success {
|
|
||||||
message = serializer.serialize(expected[0], recovered, fmt.Sprintf(shouldHavePanickedWith, expected[0], recovered))
|
|
||||||
} else {
|
|
||||||
message = success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
action()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotPanicWith receives a void, niladic function and expects to recover a panic whose content differs from the second argument.
|
|
||||||
func ShouldNotPanicWith(actual interface{}, expected ...interface{}) (message string) {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
action, _ := actual.(func())
|
|
||||||
|
|
||||||
if action == nil {
|
|
||||||
message = shouldUseVoidNiladicFunction
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
recovered := recover()
|
|
||||||
if recovered == nil {
|
|
||||||
message = success
|
|
||||||
} else {
|
|
||||||
if equal := ShouldEqual(recovered, expected[0]); equal == success {
|
|
||||||
message = fmt.Sprintf(shouldNotHavePanickedWith, expected[0])
|
|
||||||
} else {
|
|
||||||
message = success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
action()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
141
vendor/github.com/smartystreets/assertions/quantity.go
generated
vendored
141
vendor/github.com/smartystreets/assertions/quantity.go
generated
vendored
@ -1,141 +0,0 @@
|
|||||||
package assertions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/smartystreets/assertions/internal/oglematchers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ShouldBeGreaterThan receives exactly two parameters and ensures that the first is greater than the second.
|
|
||||||
func ShouldBeGreaterThan(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
if matchError := oglematchers.GreaterThan(expected[0]).Matches(actual); matchError != nil {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenGreater, actual, expected[0])
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeGreaterThanOrEqualTo receives exactly two parameters and ensures that the first is greater than or equal to the second.
|
|
||||||
func ShouldBeGreaterThanOrEqualTo(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
} else if matchError := oglematchers.GreaterOrEqual(expected[0]).Matches(actual); matchError != nil {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenGreaterOrEqual, actual, expected[0])
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeLessThan receives exactly two parameters and ensures that the first is less than the second.
|
|
||||||
func ShouldBeLessThan(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
} else if matchError := oglematchers.LessThan(expected[0]).Matches(actual); matchError != nil {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenLess, actual, expected[0])
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeLessThan receives exactly two parameters and ensures that the first is less than or equal to the second.
|
|
||||||
func ShouldBeLessThanOrEqualTo(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
} else if matchError := oglematchers.LessOrEqual(expected[0]).Matches(actual); matchError != nil {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenLess, actual, expected[0])
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeBetween receives exactly three parameters: an actual value, a lower bound, and an upper bound.
|
|
||||||
// It ensures that the actual value is between both bounds (but not equal to either of them).
|
|
||||||
func ShouldBeBetween(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(2, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
lower, upper, fail := deriveBounds(expected)
|
|
||||||
|
|
||||||
if fail != success {
|
|
||||||
return fail
|
|
||||||
} else if !isBetween(actual, lower, upper) {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenBetween, actual, lower, upper)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotBeBetween receives exactly three parameters: an actual value, a lower bound, and an upper bound.
|
|
||||||
// It ensures that the actual value is NOT between both bounds.
|
|
||||||
func ShouldNotBeBetween(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(2, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
lower, upper, fail := deriveBounds(expected)
|
|
||||||
|
|
||||||
if fail != success {
|
|
||||||
return fail
|
|
||||||
} else if isBetween(actual, lower, upper) {
|
|
||||||
return fmt.Sprintf(shouldNotHaveBeenBetween, actual, lower, upper)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
func deriveBounds(values []interface{}) (lower interface{}, upper interface{}, fail string) {
|
|
||||||
lower = values[0]
|
|
||||||
upper = values[1]
|
|
||||||
|
|
||||||
if ShouldNotEqual(lower, upper) != success {
|
|
||||||
return nil, nil, fmt.Sprintf(shouldHaveDifferentUpperAndLower, lower)
|
|
||||||
} else if ShouldBeLessThan(lower, upper) != success {
|
|
||||||
lower, upper = upper, lower
|
|
||||||
}
|
|
||||||
return lower, upper, success
|
|
||||||
}
|
|
||||||
func isBetween(value, lower, upper interface{}) bool {
|
|
||||||
if ShouldBeGreaterThan(value, lower) != success {
|
|
||||||
return false
|
|
||||||
} else if ShouldBeLessThan(value, upper) != success {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeBetweenOrEqual receives exactly three parameters: an actual value, a lower bound, and an upper bound.
|
|
||||||
// It ensures that the actual value is between both bounds or equal to one of them.
|
|
||||||
func ShouldBeBetweenOrEqual(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(2, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
lower, upper, fail := deriveBounds(expected)
|
|
||||||
|
|
||||||
if fail != success {
|
|
||||||
return fail
|
|
||||||
} else if !isBetweenOrEqual(actual, lower, upper) {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenBetweenOrEqual, actual, lower, upper)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotBeBetweenOrEqual receives exactly three parameters: an actual value, a lower bound, and an upper bound.
|
|
||||||
// It ensures that the actual value is nopt between the bounds nor equal to either of them.
|
|
||||||
func ShouldNotBeBetweenOrEqual(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(2, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
lower, upper, fail := deriveBounds(expected)
|
|
||||||
|
|
||||||
if fail != success {
|
|
||||||
return fail
|
|
||||||
} else if isBetweenOrEqual(actual, lower, upper) {
|
|
||||||
return fmt.Sprintf(shouldNotHaveBeenBetweenOrEqual, actual, lower, upper)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBetweenOrEqual(value, lower, upper interface{}) bool {
|
|
||||||
if ShouldBeGreaterThanOrEqualTo(value, lower) != success {
|
|
||||||
return false
|
|
||||||
} else if ShouldBeLessThanOrEqualTo(value, upper) != success {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
59
vendor/github.com/smartystreets/assertions/serializer.go
generated
vendored
59
vendor/github.com/smartystreets/assertions/serializer.go
generated
vendored
@ -1,59 +0,0 @@
|
|||||||
package assertions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/smartystreets/goconvey/convey/reporting"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Serializer interface {
|
|
||||||
serialize(expected, actual interface{}, message string) string
|
|
||||||
serializeDetailed(expected, actual interface{}, message string) string
|
|
||||||
}
|
|
||||||
|
|
||||||
type failureSerializer struct{}
|
|
||||||
|
|
||||||
func (self *failureSerializer) serializeDetailed(expected, actual interface{}, message string) string {
|
|
||||||
view := self.format(expected, actual, message, "%#v")
|
|
||||||
serialized, err := json.Marshal(view)
|
|
||||||
if err != nil {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
return string(serialized)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *failureSerializer) serialize(expected, actual interface{}, message string) string {
|
|
||||||
view := self.format(expected, actual, message, "%+v")
|
|
||||||
serialized, err := json.Marshal(view)
|
|
||||||
if err != nil {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
return string(serialized)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *failureSerializer) format(expected, actual interface{}, message string, format string) reporting.FailureView {
|
|
||||||
return reporting.FailureView{
|
|
||||||
Message: message,
|
|
||||||
Expected: fmt.Sprintf(format, expected),
|
|
||||||
Actual: fmt.Sprintf(format, actual),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSerializer() *failureSerializer {
|
|
||||||
return &failureSerializer{}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// noopSerializer just gives back the original message. This is useful when we are using
|
|
||||||
// the assertions from a context other than the web UI, that requires the JSON structure
|
|
||||||
// provided by the failureSerializer.
|
|
||||||
type noopSerializer struct{}
|
|
||||||
|
|
||||||
func (self *noopSerializer) serialize(expected, actual interface{}, message string) string {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
func (self *noopSerializer) serializeDetailed(expected, actual interface{}, message string) string {
|
|
||||||
return message
|
|
||||||
}
|
|
227
vendor/github.com/smartystreets/assertions/strings.go
generated
vendored
227
vendor/github.com/smartystreets/assertions/strings.go
generated
vendored
@ -1,227 +0,0 @@
|
|||||||
package assertions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ShouldStartWith receives exactly 2 string parameters and ensures that the first starts with the second.
|
|
||||||
func ShouldStartWith(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
value, valueIsString := actual.(string)
|
|
||||||
prefix, prefixIsString := expected[0].(string)
|
|
||||||
|
|
||||||
if !valueIsString || !prefixIsString {
|
|
||||||
return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldStartWith(value, prefix)
|
|
||||||
}
|
|
||||||
func shouldStartWith(value, prefix string) string {
|
|
||||||
if !strings.HasPrefix(value, prefix) {
|
|
||||||
shortval := value
|
|
||||||
if len(shortval) > len(prefix) {
|
|
||||||
shortval = shortval[:len(prefix)] + "..."
|
|
||||||
}
|
|
||||||
return serializer.serialize(prefix, shortval, fmt.Sprintf(shouldHaveStartedWith, value, prefix))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotStartWith receives exactly 2 string parameters and ensures that the first does not start with the second.
|
|
||||||
func ShouldNotStartWith(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
value, valueIsString := actual.(string)
|
|
||||||
prefix, prefixIsString := expected[0].(string)
|
|
||||||
|
|
||||||
if !valueIsString || !prefixIsString {
|
|
||||||
return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldNotStartWith(value, prefix)
|
|
||||||
}
|
|
||||||
func shouldNotStartWith(value, prefix string) string {
|
|
||||||
if strings.HasPrefix(value, prefix) {
|
|
||||||
if value == "" {
|
|
||||||
value = "<empty>"
|
|
||||||
}
|
|
||||||
if prefix == "" {
|
|
||||||
prefix = "<empty>"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(shouldNotHaveStartedWith, value, prefix)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldEndWith receives exactly 2 string parameters and ensures that the first ends with the second.
|
|
||||||
func ShouldEndWith(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
value, valueIsString := actual.(string)
|
|
||||||
suffix, suffixIsString := expected[0].(string)
|
|
||||||
|
|
||||||
if !valueIsString || !suffixIsString {
|
|
||||||
return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldEndWith(value, suffix)
|
|
||||||
}
|
|
||||||
func shouldEndWith(value, suffix string) string {
|
|
||||||
if !strings.HasSuffix(value, suffix) {
|
|
||||||
shortval := value
|
|
||||||
if len(shortval) > len(suffix) {
|
|
||||||
shortval = "..." + shortval[len(shortval)-len(suffix):]
|
|
||||||
}
|
|
||||||
return serializer.serialize(suffix, shortval, fmt.Sprintf(shouldHaveEndedWith, value, suffix))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldEndWith receives exactly 2 string parameters and ensures that the first does not end with the second.
|
|
||||||
func ShouldNotEndWith(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
value, valueIsString := actual.(string)
|
|
||||||
suffix, suffixIsString := expected[0].(string)
|
|
||||||
|
|
||||||
if !valueIsString || !suffixIsString {
|
|
||||||
return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldNotEndWith(value, suffix)
|
|
||||||
}
|
|
||||||
func shouldNotEndWith(value, suffix string) string {
|
|
||||||
if strings.HasSuffix(value, suffix) {
|
|
||||||
if value == "" {
|
|
||||||
value = "<empty>"
|
|
||||||
}
|
|
||||||
if suffix == "" {
|
|
||||||
suffix = "<empty>"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(shouldNotHaveEndedWith, value, suffix)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldContainSubstring receives exactly 2 string parameters and ensures that the first contains the second as a substring.
|
|
||||||
func ShouldContainSubstring(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
long, longOk := actual.(string)
|
|
||||||
short, shortOk := expected[0].(string)
|
|
||||||
|
|
||||||
if !longOk || !shortOk {
|
|
||||||
return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(long, short) {
|
|
||||||
return serializer.serialize(expected[0], actual, fmt.Sprintf(shouldHaveContainedSubstring, long, short))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotContainSubstring receives exactly 2 string parameters and ensures that the first does NOT contain the second as a substring.
|
|
||||||
func ShouldNotContainSubstring(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
long, longOk := actual.(string)
|
|
||||||
short, shortOk := expected[0].(string)
|
|
||||||
|
|
||||||
if !longOk || !shortOk {
|
|
||||||
return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Contains(long, short) {
|
|
||||||
return fmt.Sprintf(shouldNotHaveContainedSubstring, long, short)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeBlank receives exactly 1 string parameter and ensures that it is equal to "".
|
|
||||||
func ShouldBeBlank(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
value, ok := actual.(string)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Sprintf(shouldBeString, reflect.TypeOf(actual))
|
|
||||||
}
|
|
||||||
if value != "" {
|
|
||||||
return serializer.serialize("", value, fmt.Sprintf(shouldHaveBeenBlank, value))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotBeBlank receives exactly 1 string parameter and ensures that it is equal to "".
|
|
||||||
func ShouldNotBeBlank(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
value, ok := actual.(string)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Sprintf(shouldBeString, reflect.TypeOf(actual))
|
|
||||||
}
|
|
||||||
if value == "" {
|
|
||||||
return shouldNotHaveBeenBlank
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldEqualWithout receives exactly 3 string parameters and ensures that the first is equal to the second
|
|
||||||
// after removing all instances of the third from the first using strings.Replace(first, third, "", -1).
|
|
||||||
func ShouldEqualWithout(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(2, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
actualString, ok1 := actual.(string)
|
|
||||||
expectedString, ok2 := expected[0].(string)
|
|
||||||
replace, ok3 := expected[1].(string)
|
|
||||||
|
|
||||||
if !ok1 || !ok2 || !ok3 {
|
|
||||||
return fmt.Sprintf(shouldAllBeStrings, []reflect.Type{
|
|
||||||
reflect.TypeOf(actual),
|
|
||||||
reflect.TypeOf(expected[0]),
|
|
||||||
reflect.TypeOf(expected[1]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
replaced := strings.Replace(actualString, replace, "", -1)
|
|
||||||
if replaced == expectedString {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("Expected '%s' to equal '%s' but without any '%s' (but it didn't).", actualString, expectedString, replace)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldEqualTrimSpace receives exactly 2 string parameters and ensures that the first is equal to the second
|
|
||||||
// after removing all leading and trailing whitespace using strings.TrimSpace(first).
|
|
||||||
func ShouldEqualTrimSpace(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
actualString, valueIsString := actual.(string)
|
|
||||||
_, value2IsString := expected[0].(string)
|
|
||||||
|
|
||||||
if !valueIsString || !value2IsString {
|
|
||||||
return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
actualString = strings.TrimSpace(actualString)
|
|
||||||
return ShouldEqual(actualString, expected[0])
|
|
||||||
}
|
|
202
vendor/github.com/smartystreets/assertions/time.go
generated
vendored
202
vendor/github.com/smartystreets/assertions/time.go
generated
vendored
@ -1,202 +0,0 @@
|
|||||||
package assertions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ShouldHappenBefore receives exactly 2 time.Time arguments and asserts that the first happens before the second.
|
|
||||||
func ShouldHappenBefore(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
actualTime, firstOk := actual.(time.Time)
|
|
||||||
expectedTime, secondOk := expected[0].(time.Time)
|
|
||||||
|
|
||||||
if !firstOk || !secondOk {
|
|
||||||
return shouldUseTimes
|
|
||||||
}
|
|
||||||
|
|
||||||
if !actualTime.Before(expectedTime) {
|
|
||||||
return fmt.Sprintf(shouldHaveHappenedBefore, actualTime, expectedTime, actualTime.Sub(expectedTime))
|
|
||||||
}
|
|
||||||
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldHappenOnOrBefore receives exactly 2 time.Time arguments and asserts that the first happens on or before the second.
|
|
||||||
func ShouldHappenOnOrBefore(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
actualTime, firstOk := actual.(time.Time)
|
|
||||||
expectedTime, secondOk := expected[0].(time.Time)
|
|
||||||
|
|
||||||
if !firstOk || !secondOk {
|
|
||||||
return shouldUseTimes
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualTime.Equal(expectedTime) {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
return ShouldHappenBefore(actualTime, expectedTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldHappenAfter receives exactly 2 time.Time arguments and asserts that the first happens after the second.
|
|
||||||
func ShouldHappenAfter(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
actualTime, firstOk := actual.(time.Time)
|
|
||||||
expectedTime, secondOk := expected[0].(time.Time)
|
|
||||||
|
|
||||||
if !firstOk || !secondOk {
|
|
||||||
return shouldUseTimes
|
|
||||||
}
|
|
||||||
if !actualTime.After(expectedTime) {
|
|
||||||
return fmt.Sprintf(shouldHaveHappenedAfter, actualTime, expectedTime, expectedTime.Sub(actualTime))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldHappenOnOrAfter receives exactly 2 time.Time arguments and asserts that the first happens on or after the second.
|
|
||||||
func ShouldHappenOnOrAfter(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
actualTime, firstOk := actual.(time.Time)
|
|
||||||
expectedTime, secondOk := expected[0].(time.Time)
|
|
||||||
|
|
||||||
if !firstOk || !secondOk {
|
|
||||||
return shouldUseTimes
|
|
||||||
}
|
|
||||||
if actualTime.Equal(expectedTime) {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
return ShouldHappenAfter(actualTime, expectedTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldHappenBetween receives exactly 3 time.Time arguments and asserts that the first happens between (not on) the second and third.
|
|
||||||
func ShouldHappenBetween(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(2, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
actualTime, firstOk := actual.(time.Time)
|
|
||||||
min, secondOk := expected[0].(time.Time)
|
|
||||||
max, thirdOk := expected[1].(time.Time)
|
|
||||||
|
|
||||||
if !firstOk || !secondOk || !thirdOk {
|
|
||||||
return shouldUseTimes
|
|
||||||
}
|
|
||||||
|
|
||||||
if !actualTime.After(min) {
|
|
||||||
return fmt.Sprintf(shouldHaveHappenedBetween, actualTime, min, max, min.Sub(actualTime))
|
|
||||||
}
|
|
||||||
if !actualTime.Before(max) {
|
|
||||||
return fmt.Sprintf(shouldHaveHappenedBetween, actualTime, min, max, actualTime.Sub(max))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that the first happens between or on the second and third.
|
|
||||||
func ShouldHappenOnOrBetween(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(2, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
actualTime, firstOk := actual.(time.Time)
|
|
||||||
min, secondOk := expected[0].(time.Time)
|
|
||||||
max, thirdOk := expected[1].(time.Time)
|
|
||||||
|
|
||||||
if !firstOk || !secondOk || !thirdOk {
|
|
||||||
return shouldUseTimes
|
|
||||||
}
|
|
||||||
if actualTime.Equal(min) || actualTime.Equal(max) {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
return ShouldHappenBetween(actualTime, min, max)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that the first
|
|
||||||
// does NOT happen between or on the second or third.
|
|
||||||
func ShouldNotHappenOnOrBetween(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(2, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
actualTime, firstOk := actual.(time.Time)
|
|
||||||
min, secondOk := expected[0].(time.Time)
|
|
||||||
max, thirdOk := expected[1].(time.Time)
|
|
||||||
|
|
||||||
if !firstOk || !secondOk || !thirdOk {
|
|
||||||
return shouldUseTimes
|
|
||||||
}
|
|
||||||
if actualTime.Equal(min) || actualTime.Equal(max) {
|
|
||||||
return fmt.Sprintf(shouldNotHaveHappenedOnOrBetween, actualTime, min, max)
|
|
||||||
}
|
|
||||||
if actualTime.After(min) && actualTime.Before(max) {
|
|
||||||
return fmt.Sprintf(shouldNotHaveHappenedOnOrBetween, actualTime, min, max)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldHappenWithin receives a time.Time, a time.Duration, and a time.Time (3 arguments)
|
|
||||||
// and asserts that the first time.Time happens within or on the duration specified relative to
|
|
||||||
// the other time.Time.
|
|
||||||
func ShouldHappenWithin(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(2, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
actualTime, firstOk := actual.(time.Time)
|
|
||||||
tolerance, secondOk := expected[0].(time.Duration)
|
|
||||||
threshold, thirdOk := expected[1].(time.Time)
|
|
||||||
|
|
||||||
if !firstOk || !secondOk || !thirdOk {
|
|
||||||
return shouldUseDurationAndTime
|
|
||||||
}
|
|
||||||
|
|
||||||
min := threshold.Add(-tolerance)
|
|
||||||
max := threshold.Add(tolerance)
|
|
||||||
return ShouldHappenOnOrBetween(actualTime, min, max)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotHappenWithin receives a time.Time, a time.Duration, and a time.Time (3 arguments)
|
|
||||||
// and asserts that the first time.Time does NOT happen within or on the duration specified relative to
|
|
||||||
// the other time.Time.
|
|
||||||
func ShouldNotHappenWithin(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(2, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
actualTime, firstOk := actual.(time.Time)
|
|
||||||
tolerance, secondOk := expected[0].(time.Duration)
|
|
||||||
threshold, thirdOk := expected[1].(time.Time)
|
|
||||||
|
|
||||||
if !firstOk || !secondOk || !thirdOk {
|
|
||||||
return shouldUseDurationAndTime
|
|
||||||
}
|
|
||||||
|
|
||||||
min := threshold.Add(-tolerance)
|
|
||||||
max := threshold.Add(tolerance)
|
|
||||||
return ShouldNotHappenOnOrBetween(actualTime, min, max)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBeChronological receives a []time.Time slice and asserts that the are
|
|
||||||
// in chronological order starting with the first time.Time as the earliest.
|
|
||||||
func ShouldBeChronological(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(0, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
times, ok := actual.([]time.Time)
|
|
||||||
if !ok {
|
|
||||||
return shouldUseTimeSlice
|
|
||||||
}
|
|
||||||
|
|
||||||
var previous time.Time
|
|
||||||
for i, current := range times {
|
|
||||||
if i > 0 && current.Before(previous) {
|
|
||||||
return fmt.Sprintf(shouldHaveBeenChronological,
|
|
||||||
i, i-1, previous.String(), i, current.String())
|
|
||||||
}
|
|
||||||
previous = current
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
112
vendor/github.com/smartystreets/assertions/type.go
generated
vendored
112
vendor/github.com/smartystreets/assertions/type.go
generated
vendored
@ -1,112 +0,0 @@
|
|||||||
package assertions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ShouldHaveSameTypeAs receives exactly two parameters and compares their underlying types for equality.
|
|
||||||
func ShouldHaveSameTypeAs(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
first := reflect.TypeOf(actual)
|
|
||||||
second := reflect.TypeOf(expected[0])
|
|
||||||
|
|
||||||
if equal := ShouldEqual(first, second); equal != success {
|
|
||||||
return serializer.serialize(second, first, fmt.Sprintf(shouldHaveBeenA, actual, second, first))
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotHaveSameTypeAs receives exactly two parameters and compares their underlying types for inequality.
|
|
||||||
func ShouldNotHaveSameTypeAs(actual interface{}, expected ...interface{}) string {
|
|
||||||
if fail := need(1, expected); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
first := reflect.TypeOf(actual)
|
|
||||||
second := reflect.TypeOf(expected[0])
|
|
||||||
|
|
||||||
if equal := ShouldEqual(first, second); equal == success {
|
|
||||||
return fmt.Sprintf(shouldNotHaveBeenA, actual, second)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldImplement receives exactly two parameters and ensures
|
|
||||||
// that the first implements the interface type of the second.
|
|
||||||
func ShouldImplement(actual interface{}, expectedList ...interface{}) string {
|
|
||||||
if fail := need(1, expectedList); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := expectedList[0]
|
|
||||||
if fail := ShouldBeNil(expected); fail != success {
|
|
||||||
return shouldCompareWithInterfacePointer
|
|
||||||
}
|
|
||||||
|
|
||||||
if fail := ShouldNotBeNil(actual); fail != success {
|
|
||||||
return shouldNotBeNilActual
|
|
||||||
}
|
|
||||||
|
|
||||||
var actualType reflect.Type
|
|
||||||
if reflect.TypeOf(actual).Kind() != reflect.Ptr {
|
|
||||||
actualType = reflect.PtrTo(reflect.TypeOf(actual))
|
|
||||||
} else {
|
|
||||||
actualType = reflect.TypeOf(actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedType := reflect.TypeOf(expected)
|
|
||||||
if fail := ShouldNotBeNil(expectedType); fail != success {
|
|
||||||
return shouldCompareWithInterfacePointer
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedInterface := expectedType.Elem()
|
|
||||||
|
|
||||||
if actualType == nil {
|
|
||||||
return fmt.Sprintf(shouldHaveImplemented, expectedInterface, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !actualType.Implements(expectedInterface) {
|
|
||||||
return fmt.Sprintf(shouldHaveImplemented, expectedInterface, actualType)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldNotImplement receives exactly two parameters and ensures
|
|
||||||
// that the first does NOT implement the interface type of the second.
|
|
||||||
func ShouldNotImplement(actual interface{}, expectedList ...interface{}) string {
|
|
||||||
if fail := need(1, expectedList); fail != success {
|
|
||||||
return fail
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := expectedList[0]
|
|
||||||
if fail := ShouldBeNil(expected); fail != success {
|
|
||||||
return shouldCompareWithInterfacePointer
|
|
||||||
}
|
|
||||||
|
|
||||||
if fail := ShouldNotBeNil(actual); fail != success {
|
|
||||||
return shouldNotBeNilActual
|
|
||||||
}
|
|
||||||
|
|
||||||
var actualType reflect.Type
|
|
||||||
if reflect.TypeOf(actual).Kind() != reflect.Ptr {
|
|
||||||
actualType = reflect.PtrTo(reflect.TypeOf(actual))
|
|
||||||
} else {
|
|
||||||
actualType = reflect.TypeOf(actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedType := reflect.TypeOf(expected)
|
|
||||||
if fail := ShouldNotBeNil(expectedType); fail != success {
|
|
||||||
return shouldCompareWithInterfacePointer
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedInterface := expectedType.Elem()
|
|
||||||
|
|
||||||
if actualType.Implements(expectedInterface) {
|
|
||||||
return fmt.Sprintf(shouldNotHaveImplemented, actualType, expectedInterface)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
23
vendor/github.com/smartystreets/goconvey/LICENSE.md
generated
vendored
23
vendor/github.com/smartystreets/goconvey/LICENSE.md
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
Copyright (c) 2014 SmartyStreets, LLC
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
NOTE: Various optional and subordinate components carry their own licensing
|
|
||||||
requirements and restrictions. Use of those components is subject to the terms
|
|
||||||
and conditions outlined the respective license of each component.
|
|
68
vendor/github.com/smartystreets/goconvey/convey/assertions.go
generated
vendored
68
vendor/github.com/smartystreets/goconvey/convey/assertions.go
generated
vendored
@ -1,68 +0,0 @@
|
|||||||
package convey
|
|
||||||
|
|
||||||
import "github.com/smartystreets/assertions"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ShouldEqual = assertions.ShouldEqual
|
|
||||||
ShouldNotEqual = assertions.ShouldNotEqual
|
|
||||||
ShouldAlmostEqual = assertions.ShouldAlmostEqual
|
|
||||||
ShouldNotAlmostEqual = assertions.ShouldNotAlmostEqual
|
|
||||||
ShouldResemble = assertions.ShouldResemble
|
|
||||||
ShouldNotResemble = assertions.ShouldNotResemble
|
|
||||||
ShouldPointTo = assertions.ShouldPointTo
|
|
||||||
ShouldNotPointTo = assertions.ShouldNotPointTo
|
|
||||||
ShouldBeNil = assertions.ShouldBeNil
|
|
||||||
ShouldNotBeNil = assertions.ShouldNotBeNil
|
|
||||||
ShouldBeTrue = assertions.ShouldBeTrue
|
|
||||||
ShouldBeFalse = assertions.ShouldBeFalse
|
|
||||||
ShouldBeZeroValue = assertions.ShouldBeZeroValue
|
|
||||||
|
|
||||||
ShouldBeGreaterThan = assertions.ShouldBeGreaterThan
|
|
||||||
ShouldBeGreaterThanOrEqualTo = assertions.ShouldBeGreaterThanOrEqualTo
|
|
||||||
ShouldBeLessThan = assertions.ShouldBeLessThan
|
|
||||||
ShouldBeLessThanOrEqualTo = assertions.ShouldBeLessThanOrEqualTo
|
|
||||||
ShouldBeBetween = assertions.ShouldBeBetween
|
|
||||||
ShouldNotBeBetween = assertions.ShouldNotBeBetween
|
|
||||||
ShouldBeBetweenOrEqual = assertions.ShouldBeBetweenOrEqual
|
|
||||||
ShouldNotBeBetweenOrEqual = assertions.ShouldNotBeBetweenOrEqual
|
|
||||||
|
|
||||||
ShouldContain = assertions.ShouldContain
|
|
||||||
ShouldNotContain = assertions.ShouldNotContain
|
|
||||||
ShouldContainKey = assertions.ShouldContainKey
|
|
||||||
ShouldNotContainKey = assertions.ShouldNotContainKey
|
|
||||||
ShouldBeIn = assertions.ShouldBeIn
|
|
||||||
ShouldNotBeIn = assertions.ShouldNotBeIn
|
|
||||||
ShouldBeEmpty = assertions.ShouldBeEmpty
|
|
||||||
ShouldNotBeEmpty = assertions.ShouldNotBeEmpty
|
|
||||||
ShouldHaveLength = assertions.ShouldHaveLength
|
|
||||||
|
|
||||||
ShouldStartWith = assertions.ShouldStartWith
|
|
||||||
ShouldNotStartWith = assertions.ShouldNotStartWith
|
|
||||||
ShouldEndWith = assertions.ShouldEndWith
|
|
||||||
ShouldNotEndWith = assertions.ShouldNotEndWith
|
|
||||||
ShouldBeBlank = assertions.ShouldBeBlank
|
|
||||||
ShouldNotBeBlank = assertions.ShouldNotBeBlank
|
|
||||||
ShouldContainSubstring = assertions.ShouldContainSubstring
|
|
||||||
ShouldNotContainSubstring = assertions.ShouldNotContainSubstring
|
|
||||||
|
|
||||||
ShouldPanic = assertions.ShouldPanic
|
|
||||||
ShouldNotPanic = assertions.ShouldNotPanic
|
|
||||||
ShouldPanicWith = assertions.ShouldPanicWith
|
|
||||||
ShouldNotPanicWith = assertions.ShouldNotPanicWith
|
|
||||||
|
|
||||||
ShouldHaveSameTypeAs = assertions.ShouldHaveSameTypeAs
|
|
||||||
ShouldNotHaveSameTypeAs = assertions.ShouldNotHaveSameTypeAs
|
|
||||||
ShouldImplement = assertions.ShouldImplement
|
|
||||||
ShouldNotImplement = assertions.ShouldNotImplement
|
|
||||||
|
|
||||||
ShouldHappenBefore = assertions.ShouldHappenBefore
|
|
||||||
ShouldHappenOnOrBefore = assertions.ShouldHappenOnOrBefore
|
|
||||||
ShouldHappenAfter = assertions.ShouldHappenAfter
|
|
||||||
ShouldHappenOnOrAfter = assertions.ShouldHappenOnOrAfter
|
|
||||||
ShouldHappenBetween = assertions.ShouldHappenBetween
|
|
||||||
ShouldHappenOnOrBetween = assertions.ShouldHappenOnOrBetween
|
|
||||||
ShouldNotHappenOnOrBetween = assertions.ShouldNotHappenOnOrBetween
|
|
||||||
ShouldHappenWithin = assertions.ShouldHappenWithin
|
|
||||||
ShouldNotHappenWithin = assertions.ShouldNotHappenWithin
|
|
||||||
ShouldBeChronological = assertions.ShouldBeChronological
|
|
||||||
)
|
|
272
vendor/github.com/smartystreets/goconvey/convey/context.go
generated
vendored
272
vendor/github.com/smartystreets/goconvey/convey/context.go
generated
vendored
@ -1,272 +0,0 @@
|
|||||||
package convey
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jtolds/gls"
|
|
||||||
"github.com/smartystreets/goconvey/convey/reporting"
|
|
||||||
)
|
|
||||||
|
|
||||||
type conveyErr struct {
|
|
||||||
fmt string
|
|
||||||
params []interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *conveyErr) Error() string {
|
|
||||||
return fmt.Sprintf(e.fmt, e.params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func conveyPanic(fmt string, params ...interface{}) {
|
|
||||||
panic(&conveyErr{fmt, params})
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
missingGoTest = `Top-level calls to Convey(...) need a reference to the *testing.T.
|
|
||||||
Hint: Convey("description here", t, func() { /* notice that the second argument was the *testing.T (t)! */ }) `
|
|
||||||
extraGoTest = `Only the top-level call to Convey(...) needs a reference to the *testing.T.`
|
|
||||||
noStackContext = "Convey operation made without context on goroutine stack.\n" +
|
|
||||||
"Hint: Perhaps you meant to use `Convey(..., func(c C){...})` ?"
|
|
||||||
differentConveySituations = "Different set of Convey statements on subsequent pass!\nDid not expect %#v."
|
|
||||||
multipleIdenticalConvey = "Multiple convey suites with identical names: %#v"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
failureHalt = "___FAILURE_HALT___"
|
|
||||||
|
|
||||||
nodeKey = "node"
|
|
||||||
)
|
|
||||||
|
|
||||||
///////////////////////////////// Stack Context /////////////////////////////////
|
|
||||||
|
|
||||||
func getCurrentContext() *context {
|
|
||||||
ctx, ok := ctxMgr.GetValue(nodeKey)
|
|
||||||
if ok {
|
|
||||||
return ctx.(*context)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustGetCurrentContext() *context {
|
|
||||||
ctx := getCurrentContext()
|
|
||||||
if ctx == nil {
|
|
||||||
conveyPanic(noStackContext)
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////// Context ////////////////////////////////////
|
|
||||||
|
|
||||||
// context magically handles all coordination of Convey's and So assertions.
|
|
||||||
//
|
|
||||||
// It is tracked on the stack as goroutine-local-storage with the gls package,
|
|
||||||
// or explicitly if the user decides to call convey like:
|
|
||||||
//
|
|
||||||
// Convey(..., func(c C) {
|
|
||||||
// c.So(...)
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// This implements the `C` interface.
|
|
||||||
type context struct {
|
|
||||||
reporter reporting.Reporter
|
|
||||||
|
|
||||||
children map[string]*context
|
|
||||||
|
|
||||||
resets []func()
|
|
||||||
|
|
||||||
executedOnce bool
|
|
||||||
expectChildRun *bool
|
|
||||||
complete bool
|
|
||||||
|
|
||||||
focus bool
|
|
||||||
failureMode FailureMode
|
|
||||||
}
|
|
||||||
|
|
||||||
// rootConvey is the main entry point to a test suite. This is called when
|
|
||||||
// there's no context in the stack already, and items must contain a `t` object,
|
|
||||||
// or this panics.
|
|
||||||
func rootConvey(items ...interface{}) {
|
|
||||||
entry := discover(items)
|
|
||||||
|
|
||||||
if entry.Test == nil {
|
|
||||||
conveyPanic(missingGoTest)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectChildRun := true
|
|
||||||
ctx := &context{
|
|
||||||
reporter: buildReporter(),
|
|
||||||
|
|
||||||
children: make(map[string]*context),
|
|
||||||
|
|
||||||
expectChildRun: &expectChildRun,
|
|
||||||
|
|
||||||
focus: entry.Focus,
|
|
||||||
failureMode: defaultFailureMode.combine(entry.FailMode),
|
|
||||||
}
|
|
||||||
ctxMgr.SetValues(gls.Values{nodeKey: ctx}, func() {
|
|
||||||
ctx.reporter.BeginStory(reporting.NewStoryReport(entry.Test))
|
|
||||||
defer ctx.reporter.EndStory()
|
|
||||||
|
|
||||||
for ctx.shouldVisit() {
|
|
||||||
ctx.conveyInner(entry.Situation, entry.Func)
|
|
||||||
expectChildRun = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////// Methods ////////////////////////////////////
|
|
||||||
|
|
||||||
func (ctx *context) SkipConvey(items ...interface{}) {
|
|
||||||
ctx.Convey(items, skipConvey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *context) FocusConvey(items ...interface{}) {
|
|
||||||
ctx.Convey(items, focusConvey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *context) Convey(items ...interface{}) {
|
|
||||||
entry := discover(items)
|
|
||||||
|
|
||||||
// we're a branch, or leaf (on the wind)
|
|
||||||
if entry.Test != nil {
|
|
||||||
conveyPanic(extraGoTest)
|
|
||||||
}
|
|
||||||
if ctx.focus && !entry.Focus {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var inner_ctx *context
|
|
||||||
if ctx.executedOnce {
|
|
||||||
var ok bool
|
|
||||||
inner_ctx, ok = ctx.children[entry.Situation]
|
|
||||||
if !ok {
|
|
||||||
conveyPanic(differentConveySituations, entry.Situation)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, ok := ctx.children[entry.Situation]; ok {
|
|
||||||
conveyPanic(multipleIdenticalConvey, entry.Situation)
|
|
||||||
}
|
|
||||||
inner_ctx = &context{
|
|
||||||
reporter: ctx.reporter,
|
|
||||||
|
|
||||||
children: make(map[string]*context),
|
|
||||||
|
|
||||||
expectChildRun: ctx.expectChildRun,
|
|
||||||
|
|
||||||
focus: entry.Focus,
|
|
||||||
failureMode: ctx.failureMode.combine(entry.FailMode),
|
|
||||||
}
|
|
||||||
ctx.children[entry.Situation] = inner_ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
if inner_ctx.shouldVisit() {
|
|
||||||
ctxMgr.SetValues(gls.Values{nodeKey: inner_ctx}, func() {
|
|
||||||
inner_ctx.conveyInner(entry.Situation, entry.Func)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *context) SkipSo(stuff ...interface{}) {
|
|
||||||
ctx.assertionReport(reporting.NewSkipReport())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *context) So(actual interface{}, assert assertion, expected ...interface{}) {
|
|
||||||
if result := assert(actual, expected...); result == assertionSuccess {
|
|
||||||
ctx.assertionReport(reporting.NewSuccessReport())
|
|
||||||
} else {
|
|
||||||
ctx.assertionReport(reporting.NewFailureReport(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *context) Reset(action func()) {
|
|
||||||
/* TODO: Failure mode configuration */
|
|
||||||
ctx.resets = append(ctx.resets, action)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *context) Print(items ...interface{}) (int, error) {
|
|
||||||
fmt.Fprint(ctx.reporter, items...)
|
|
||||||
return fmt.Print(items...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *context) Println(items ...interface{}) (int, error) {
|
|
||||||
fmt.Fprintln(ctx.reporter, items...)
|
|
||||||
return fmt.Println(items...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *context) Printf(format string, items ...interface{}) (int, error) {
|
|
||||||
fmt.Fprintf(ctx.reporter, format, items...)
|
|
||||||
return fmt.Printf(format, items...)
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////// Private ////////////////////////////////////
|
|
||||||
|
|
||||||
// shouldVisit returns true iff we should traverse down into a Convey. Note
|
|
||||||
// that just because we don't traverse a Convey this time, doesn't mean that
|
|
||||||
// we may not traverse it on a subsequent pass.
|
|
||||||
func (c *context) shouldVisit() bool {
|
|
||||||
return !c.complete && *c.expectChildRun
|
|
||||||
}
|
|
||||||
|
|
||||||
// conveyInner is the function which actually executes the user's anonymous test
|
|
||||||
// function body. At this point, Convey or RootConvey has decided that this
|
|
||||||
// function should actually run.
|
|
||||||
func (ctx *context) conveyInner(situation string, f func(C)) {
|
|
||||||
// Record/Reset state for next time.
|
|
||||||
defer func() {
|
|
||||||
ctx.executedOnce = true
|
|
||||||
|
|
||||||
// This is only needed at the leaves, but there's no harm in also setting it
|
|
||||||
// when returning from branch Convey's
|
|
||||||
*ctx.expectChildRun = false
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Set up+tear down our scope for the reporter
|
|
||||||
ctx.reporter.Enter(reporting.NewScopeReport(situation))
|
|
||||||
defer ctx.reporter.Exit()
|
|
||||||
|
|
||||||
// Recover from any panics in f, and assign the `complete` status for this
|
|
||||||
// node of the tree.
|
|
||||||
defer func() {
|
|
||||||
ctx.complete = true
|
|
||||||
if problem := recover(); problem != nil {
|
|
||||||
if problem, ok := problem.(*conveyErr); ok {
|
|
||||||
panic(problem)
|
|
||||||
}
|
|
||||||
if problem != failureHalt {
|
|
||||||
ctx.reporter.Report(reporting.NewErrorReport(problem))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, child := range ctx.children {
|
|
||||||
if !child.complete {
|
|
||||||
ctx.complete = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Resets are registered as the `f` function executes, so nil them here.
|
|
||||||
// All resets are run in registration order (FIFO).
|
|
||||||
ctx.resets = []func(){}
|
|
||||||
defer func() {
|
|
||||||
for _, r := range ctx.resets {
|
|
||||||
// panics handled by the previous defer
|
|
||||||
r()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if f == nil {
|
|
||||||
// if f is nil, this was either a Convey(..., nil), or a SkipConvey
|
|
||||||
ctx.reporter.Report(reporting.NewSkipReport())
|
|
||||||
} else {
|
|
||||||
f(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assertionReport is a helper for So and SkipSo which makes the report and
|
|
||||||
// then possibly panics, depending on the current context's failureMode.
|
|
||||||
func (ctx *context) assertionReport(r *reporting.AssertionResult) {
|
|
||||||
ctx.reporter.Report(r)
|
|
||||||
if r.Failure != "" && ctx.failureMode == FailureHalts {
|
|
||||||
panic(failureHalt)
|
|
||||||
}
|
|
||||||
}
|
|
4
vendor/github.com/smartystreets/goconvey/convey/convey.goconvey
generated
vendored
4
vendor/github.com/smartystreets/goconvey/convey/convey.goconvey
generated
vendored
@ -1,4 +0,0 @@
|
|||||||
#ignore
|
|
||||||
-timeout=1s
|
|
||||||
#-covermode=count
|
|
||||||
#-coverpkg=github.com/smartystreets/goconvey/convey,github.com/smartystreets/goconvey/convey/gotest,github.com/smartystreets/goconvey/convey/reporting
|
|
103
vendor/github.com/smartystreets/goconvey/convey/discovery.go
generated
vendored
103
vendor/github.com/smartystreets/goconvey/convey/discovery.go
generated
vendored
@ -1,103 +0,0 @@
|
|||||||
package convey
|
|
||||||
|
|
||||||
type actionSpecifier uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
noSpecifier actionSpecifier = iota
|
|
||||||
skipConvey
|
|
||||||
focusConvey
|
|
||||||
)
|
|
||||||
|
|
||||||
type suite struct {
|
|
||||||
Situation string
|
|
||||||
Test t
|
|
||||||
Focus bool
|
|
||||||
Func func(C) // nil means skipped
|
|
||||||
FailMode FailureMode
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSuite(situation string, failureMode FailureMode, f func(C), test t, specifier actionSpecifier) *suite {
|
|
||||||
ret := &suite{
|
|
||||||
Situation: situation,
|
|
||||||
Test: test,
|
|
||||||
Func: f,
|
|
||||||
FailMode: failureMode,
|
|
||||||
}
|
|
||||||
switch specifier {
|
|
||||||
case skipConvey:
|
|
||||||
ret.Func = nil
|
|
||||||
case focusConvey:
|
|
||||||
ret.Focus = true
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func discover(items []interface{}) *suite {
|
|
||||||
name, items := parseName(items)
|
|
||||||
test, items := parseGoTest(items)
|
|
||||||
failure, items := parseFailureMode(items)
|
|
||||||
action, items := parseAction(items)
|
|
||||||
specifier, items := parseSpecifier(items)
|
|
||||||
|
|
||||||
if len(items) != 0 {
|
|
||||||
conveyPanic(parseError)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newSuite(name, failure, action, test, specifier)
|
|
||||||
}
|
|
||||||
func item(items []interface{}) interface{} {
|
|
||||||
if len(items) == 0 {
|
|
||||||
conveyPanic(parseError)
|
|
||||||
}
|
|
||||||
return items[0]
|
|
||||||
}
|
|
||||||
func parseName(items []interface{}) (string, []interface{}) {
|
|
||||||
if name, parsed := item(items).(string); parsed {
|
|
||||||
return name, items[1:]
|
|
||||||
}
|
|
||||||
conveyPanic(parseError)
|
|
||||||
panic("never get here")
|
|
||||||
}
|
|
||||||
func parseGoTest(items []interface{}) (t, []interface{}) {
|
|
||||||
if test, parsed := item(items).(t); parsed {
|
|
||||||
return test, items[1:]
|
|
||||||
}
|
|
||||||
return nil, items
|
|
||||||
}
|
|
||||||
func parseFailureMode(items []interface{}) (FailureMode, []interface{}) {
|
|
||||||
if mode, parsed := item(items).(FailureMode); parsed {
|
|
||||||
return mode, items[1:]
|
|
||||||
}
|
|
||||||
return FailureInherits, items
|
|
||||||
}
|
|
||||||
func parseAction(items []interface{}) (func(C), []interface{}) {
|
|
||||||
switch x := item(items).(type) {
|
|
||||||
case nil:
|
|
||||||
return nil, items[1:]
|
|
||||||
case func(C):
|
|
||||||
return x, items[1:]
|
|
||||||
case func():
|
|
||||||
return func(C) { x() }, items[1:]
|
|
||||||
}
|
|
||||||
conveyPanic(parseError)
|
|
||||||
panic("never get here")
|
|
||||||
}
|
|
||||||
func parseSpecifier(items []interface{}) (actionSpecifier, []interface{}) {
|
|
||||||
if len(items) == 0 {
|
|
||||||
return noSpecifier, items
|
|
||||||
}
|
|
||||||
if spec, ok := items[0].(actionSpecifier); ok {
|
|
||||||
return spec, items[1:]
|
|
||||||
}
|
|
||||||
conveyPanic(parseError)
|
|
||||||
panic("never get here")
|
|
||||||
}
|
|
||||||
|
|
||||||
// This interface allows us to pass the *testing.T struct
|
|
||||||
// throughout the internals of this package without ever
|
|
||||||
// having to import the "testing" package.
|
|
||||||
type t interface {
|
|
||||||
Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseError = "You must provide a name (string), then a *testing.T (if in outermost scope), an optional FailureMode, and then an action (func())."
|
|
218
vendor/github.com/smartystreets/goconvey/convey/doc.go
generated
vendored
218
vendor/github.com/smartystreets/goconvey/convey/doc.go
generated
vendored
@ -1,218 +0,0 @@
|
|||||||
// Package convey contains all of the public-facing entry points to this project.
|
|
||||||
// This means that it should never be required of the user to import any other
|
|
||||||
// packages from this project as they serve internal purposes.
|
|
||||||
package convey
|
|
||||||
|
|
||||||
import "github.com/smartystreets/goconvey/convey/reporting"
|
|
||||||
|
|
||||||
////////////////////////////////// suite //////////////////////////////////
|
|
||||||
|
|
||||||
// C is the Convey context which you can optionally obtain in your action
|
|
||||||
// by calling Convey like:
|
|
||||||
//
|
|
||||||
// Convey(..., func(c C) {
|
|
||||||
// ...
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// See the documentation on Convey for more details.
|
|
||||||
//
|
|
||||||
// All methods in this context behave identically to the global functions of the
|
|
||||||
// same name in this package.
|
|
||||||
type C interface {
|
|
||||||
Convey(items ...interface{})
|
|
||||||
SkipConvey(items ...interface{})
|
|
||||||
FocusConvey(items ...interface{})
|
|
||||||
|
|
||||||
So(actual interface{}, assert assertion, expected ...interface{})
|
|
||||||
SkipSo(stuff ...interface{})
|
|
||||||
|
|
||||||
Reset(action func())
|
|
||||||
|
|
||||||
Println(items ...interface{}) (int, error)
|
|
||||||
Print(items ...interface{}) (int, error)
|
|
||||||
Printf(format string, items ...interface{}) (int, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convey is the method intended for use when declaring the scopes of
|
|
||||||
// a specification. Each scope has a description and a func() which may contain
|
|
||||||
// other calls to Convey(), Reset() or Should-style assertions. Convey calls can
|
|
||||||
// be nested as far as you see fit.
|
|
||||||
//
|
|
||||||
// IMPORTANT NOTE: The top-level Convey() within a Test method
|
|
||||||
// must conform to the following signature:
|
|
||||||
//
|
|
||||||
// Convey(description string, t *testing.T, action func())
|
|
||||||
//
|
|
||||||
// All other calls should look like this (no need to pass in *testing.T):
|
|
||||||
//
|
|
||||||
// Convey(description string, action func())
|
|
||||||
//
|
|
||||||
// Don't worry, goconvey will panic if you get it wrong so you can fix it.
|
|
||||||
//
|
|
||||||
// Additionally, you may explicitly obtain access to the Convey context by doing:
|
|
||||||
//
|
|
||||||
// Convey(description string, action func(c C))
|
|
||||||
//
|
|
||||||
// You may need to do this if you want to pass the context through to a
|
|
||||||
// goroutine, or to close over the context in a handler to a library which
|
|
||||||
// calls your handler in a goroutine (httptest comes to mind).
|
|
||||||
//
|
|
||||||
// All Convey()-blocks also accept an optional parameter of FailureMode which sets
|
|
||||||
// how goconvey should treat failures for So()-assertions in the block and
|
|
||||||
// nested blocks. See the constants in this file for the available options.
|
|
||||||
//
|
|
||||||
// By default it will inherit from its parent block and the top-level blocks
|
|
||||||
// default to the FailureHalts setting.
|
|
||||||
//
|
|
||||||
// This parameter is inserted before the block itself:
|
|
||||||
//
|
|
||||||
// Convey(description string, t *testing.T, mode FailureMode, action func())
|
|
||||||
// Convey(description string, mode FailureMode, action func())
|
|
||||||
//
|
|
||||||
// See the examples package for, well, examples.
|
|
||||||
func Convey(items ...interface{}) {
|
|
||||||
if ctx := getCurrentContext(); ctx == nil {
|
|
||||||
rootConvey(items...)
|
|
||||||
} else {
|
|
||||||
ctx.Convey(items...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SkipConvey is analagous to Convey except that the scope is not executed
|
|
||||||
// (which means that child scopes defined within this scope are not run either).
|
|
||||||
// The reporter will be notified that this step was skipped.
|
|
||||||
func SkipConvey(items ...interface{}) {
|
|
||||||
Convey(append(items, skipConvey)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FocusConvey is has the inverse effect of SkipConvey. If the top-level
|
|
||||||
// Convey is changed to `FocusConvey`, only nested scopes that are defined
|
|
||||||
// with FocusConvey will be run. The rest will be ignored completely. This
|
|
||||||
// is handy when debugging a large suite that runs a misbehaving function
|
|
||||||
// repeatedly as you can disable all but one of that function
|
|
||||||
// without swaths of `SkipConvey` calls, just a targeted chain of calls
|
|
||||||
// to FocusConvey.
|
|
||||||
func FocusConvey(items ...interface{}) {
|
|
||||||
Convey(append(items, focusConvey)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset registers a cleanup function to be run after each Convey()
|
|
||||||
// in the same scope. See the examples package for a simple use case.
|
|
||||||
func Reset(action func()) {
|
|
||||||
mustGetCurrentContext().Reset(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////// Assertions ///////////////////////////////////
|
|
||||||
|
|
||||||
// assertion is an alias for a function with a signature that the convey.So()
|
|
||||||
// method can handle. Any future or custom assertions should conform to this
|
|
||||||
// method signature. The return value should be an empty string if the assertion
|
|
||||||
// passes and a well-formed failure message if not.
|
|
||||||
type assertion func(actual interface{}, expected ...interface{}) string
|
|
||||||
|
|
||||||
const assertionSuccess = ""
|
|
||||||
|
|
||||||
// So is the means by which assertions are made against the system under test.
|
|
||||||
// The majority of exported names in the assertions package begin with the word
|
|
||||||
// 'Should' and describe how the first argument (actual) should compare with any
|
|
||||||
// of the final (expected) arguments. How many final arguments are accepted
|
|
||||||
// depends on the particular assertion that is passed in as the assert argument.
|
|
||||||
// See the examples package for use cases and the assertions package for
|
|
||||||
// documentation on specific assertion methods. A failing assertion will
|
|
||||||
// cause t.Fail() to be invoked--you should never call this method (or other
|
|
||||||
// failure-inducing methods) in your test code. Leave that to GoConvey.
|
|
||||||
func So(actual interface{}, assert assertion, expected ...interface{}) {
|
|
||||||
mustGetCurrentContext().So(actual, assert, expected...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SkipSo is analagous to So except that the assertion that would have been passed
|
|
||||||
// to So is not executed and the reporter is notified that the assertion was skipped.
|
|
||||||
func SkipSo(stuff ...interface{}) {
|
|
||||||
mustGetCurrentContext().SkipSo()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FailureMode is a type which determines how the So() blocks should fail
|
|
||||||
// if their assertion fails. See constants further down for acceptable values
|
|
||||||
type FailureMode string
|
|
||||||
|
|
||||||
const (
|
|
||||||
|
|
||||||
// FailureContinues is a failure mode which prevents failing
|
|
||||||
// So()-assertions from halting Convey-block execution, instead
|
|
||||||
// allowing the test to continue past failing So()-assertions.
|
|
||||||
FailureContinues FailureMode = "continue"
|
|
||||||
|
|
||||||
// FailureHalts is the default setting for a top-level Convey()-block
|
|
||||||
// and will cause all failing So()-assertions to halt further execution
|
|
||||||
// in that test-arm and continue on to the next arm.
|
|
||||||
FailureHalts FailureMode = "halt"
|
|
||||||
|
|
||||||
// FailureInherits is the default setting for failure-mode, it will
|
|
||||||
// default to the failure-mode of the parent block. You should never
|
|
||||||
// need to specify this mode in your tests..
|
|
||||||
FailureInherits FailureMode = "inherits"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f FailureMode) combine(other FailureMode) FailureMode {
|
|
||||||
if other == FailureInherits {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
return other
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultFailureMode FailureMode = FailureHalts
|
|
||||||
|
|
||||||
// SetDefaultFailureMode allows you to specify the default failure mode
|
|
||||||
// for all Convey blocks. It is meant to be used in an init function to
|
|
||||||
// allow the default mode to be changdd across all tests for an entire packgae
|
|
||||||
// but it can be used anywhere.
|
|
||||||
func SetDefaultFailureMode(mode FailureMode) {
|
|
||||||
if mode == FailureContinues || mode == FailureHalts {
|
|
||||||
defaultFailureMode = mode
|
|
||||||
} else {
|
|
||||||
panic("You may only use the constants named 'FailureContinues' and 'FailureHalts' as default failure modes.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////// Print functions ////////////////////////////////////
|
|
||||||
|
|
||||||
// Print is analogous to fmt.Print (and it even calls fmt.Print). It ensures that
|
|
||||||
// output is aligned with the corresponding scopes in the web UI.
|
|
||||||
func Print(items ...interface{}) (written int, err error) {
|
|
||||||
return mustGetCurrentContext().Print(items...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print is analogous to fmt.Println (and it even calls fmt.Println). It ensures that
|
|
||||||
// output is aligned with the corresponding scopes in the web UI.
|
|
||||||
func Println(items ...interface{}) (written int, err error) {
|
|
||||||
return mustGetCurrentContext().Println(items...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print is analogous to fmt.Printf (and it even calls fmt.Printf). It ensures that
|
|
||||||
// output is aligned with the corresponding scopes in the web UI.
|
|
||||||
func Printf(format string, items ...interface{}) (written int, err error) {
|
|
||||||
return mustGetCurrentContext().Printf(format, items...)
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// SuppressConsoleStatistics prevents automatic printing of console statistics.
|
|
||||||
// Calling PrintConsoleStatistics explicitly will force printing of statistics.
|
|
||||||
func SuppressConsoleStatistics() {
|
|
||||||
reporting.SuppressConsoleStatistics()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConsoleStatistics may be called at any time to print assertion statistics.
|
|
||||||
// Generally, the best place to do this would be in a TestMain function,
|
|
||||||
// after all tests have been run. Something like this:
|
|
||||||
//
|
|
||||||
// func TestMain(m *testing.M) {
|
|
||||||
// convey.SuppressConsoleStatistics()
|
|
||||||
// result := m.Run()
|
|
||||||
// convey.PrintConsoleStatistics()
|
|
||||||
// os.Exit(result)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
func PrintConsoleStatistics() {
|
|
||||||
reporting.PrintConsoleStatistics()
|
|
||||||
}
|
|
28
vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go
generated
vendored
28
vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go
generated
vendored
@ -1,28 +0,0 @@
|
|||||||
// Package gotest contains internal functionality. Although this package
|
|
||||||
// contains one or more exported names it is not intended for public
|
|
||||||
// consumption. See the examples package for how to use this project.
|
|
||||||
package gotest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ResolveExternalCaller() (file string, line int, name string) {
|
|
||||||
var caller_id uintptr
|
|
||||||
callers := runtime.Callers(0, callStack)
|
|
||||||
|
|
||||||
for x := 0; x < callers; x++ {
|
|
||||||
caller_id, file, line, _ = runtime.Caller(x)
|
|
||||||
if strings.HasSuffix(file, "_test.go") || strings.HasSuffix(file, "_tests.go") {
|
|
||||||
name = runtime.FuncForPC(caller_id).Name()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file, line, name = "<unkown file>", -1, "<unknown name>"
|
|
||||||
return // panic?
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxStackDepth = 100 // This had better be enough...
|
|
||||||
|
|
||||||
var callStack []uintptr = make([]uintptr, maxStackDepth, maxStackDepth)
|
|
81
vendor/github.com/smartystreets/goconvey/convey/init.go
generated
vendored
81
vendor/github.com/smartystreets/goconvey/convey/init.go
generated
vendored
@ -1,81 +0,0 @@
|
|||||||
package convey
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/jtolds/gls"
|
|
||||||
"github.com/smartystreets/assertions"
|
|
||||||
"github.com/smartystreets/goconvey/convey/reporting"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
assertions.GoConveyMode(true)
|
|
||||||
|
|
||||||
declareFlags()
|
|
||||||
|
|
||||||
ctxMgr = gls.NewContextManager()
|
|
||||||
}
|
|
||||||
|
|
||||||
func declareFlags() {
|
|
||||||
flag.BoolVar(&json, "json", false, "When true, emits results in JSON blocks. Default: 'false'")
|
|
||||||
flag.BoolVar(&silent, "silent", false, "When true, all output from GoConvey is suppressed.")
|
|
||||||
flag.BoolVar(&story, "story", false, "When true, emits story output, otherwise emits dot output. When not provided, this flag mirros the value of the '-test.v' flag")
|
|
||||||
|
|
||||||
if noStoryFlagProvided() {
|
|
||||||
story = verboseEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// FYI: flag.Parse() is called from the testing package.
|
|
||||||
}
|
|
||||||
|
|
||||||
func noStoryFlagProvided() bool {
|
|
||||||
return !story && !storyDisabled
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildReporter() reporting.Reporter {
|
|
||||||
selectReporter := os.Getenv("GOCONVEY_REPORTER")
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case testReporter != nil:
|
|
||||||
return testReporter
|
|
||||||
case json || selectReporter == "json":
|
|
||||||
return reporting.BuildJsonReporter()
|
|
||||||
case silent || selectReporter == "silent":
|
|
||||||
return reporting.BuildSilentReporter()
|
|
||||||
case selectReporter == "dot":
|
|
||||||
// Story is turned on when verbose is set, so we need to check for dot reporter first.
|
|
||||||
return reporting.BuildDotReporter()
|
|
||||||
case story || selectReporter == "story":
|
|
||||||
return reporting.BuildStoryReporter()
|
|
||||||
default:
|
|
||||||
return reporting.BuildDotReporter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ctxMgr *gls.ContextManager
|
|
||||||
|
|
||||||
// only set by internal tests
|
|
||||||
testReporter reporting.Reporter
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
json bool
|
|
||||||
silent bool
|
|
||||||
story bool
|
|
||||||
|
|
||||||
verboseEnabled = flagFound("-test.v=true")
|
|
||||||
storyDisabled = flagFound("-story=false")
|
|
||||||
)
|
|
||||||
|
|
||||||
// flagFound parses the command line args manually for flags defined in other
|
|
||||||
// packages. Like the '-v' flag from the "testing" package, for instance.
|
|
||||||
func flagFound(flagValue string) bool {
|
|
||||||
for _, arg := range os.Args {
|
|
||||||
if arg == flagValue {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
15
vendor/github.com/smartystreets/goconvey/convey/nilReporter.go
generated
vendored
15
vendor/github.com/smartystreets/goconvey/convey/nilReporter.go
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
package convey
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/smartystreets/goconvey/convey/reporting"
|
|
||||||
)
|
|
||||||
|
|
||||||
type nilReporter struct{}
|
|
||||||
|
|
||||||
func (self *nilReporter) BeginStory(story *reporting.StoryReport) {}
|
|
||||||
func (self *nilReporter) Enter(scope *reporting.ScopeReport) {}
|
|
||||||
func (self *nilReporter) Report(report *reporting.AssertionResult) {}
|
|
||||||
func (self *nilReporter) Exit() {}
|
|
||||||
func (self *nilReporter) EndStory() {}
|
|
||||||
func (self *nilReporter) Write(p []byte) (int, error) { return len(p), nil }
|
|
||||||
func newNilReporter() *nilReporter { return &nilReporter{} }
|
|
16
vendor/github.com/smartystreets/goconvey/convey/reporting/console.go
generated
vendored
16
vendor/github.com/smartystreets/goconvey/convey/reporting/console.go
generated
vendored
@ -1,16 +0,0 @@
|
|||||||
package reporting
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
type console struct{}
|
|
||||||
|
|
||||||
func (self *console) Write(p []byte) (n int, err error) {
|
|
||||||
return fmt.Print(string(p))
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConsole() io.Writer {
|
|
||||||
return new(console)
|
|
||||||
}
|
|
5
vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go
generated
vendored
5
vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
// Package reporting contains internal functionality related
|
|
||||||
// to console reporting and output. Although this package has
|
|
||||||
// exported names is not intended for public consumption. See the
|
|
||||||
// examples package for how to use this project.
|
|
||||||
package reporting
|
|
40
vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go
generated
vendored
40
vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go
generated
vendored
@ -1,40 +0,0 @@
|
|||||||
package reporting
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type dot struct{ out *Printer }
|
|
||||||
|
|
||||||
func (self *dot) BeginStory(story *StoryReport) {}
|
|
||||||
|
|
||||||
func (self *dot) Enter(scope *ScopeReport) {}
|
|
||||||
|
|
||||||
func (self *dot) Report(report *AssertionResult) {
|
|
||||||
if report.Error != nil {
|
|
||||||
fmt.Print(redColor)
|
|
||||||
self.out.Insert(dotError)
|
|
||||||
} else if report.Failure != "" {
|
|
||||||
fmt.Print(yellowColor)
|
|
||||||
self.out.Insert(dotFailure)
|
|
||||||
} else if report.Skipped {
|
|
||||||
fmt.Print(yellowColor)
|
|
||||||
self.out.Insert(dotSkip)
|
|
||||||
} else {
|
|
||||||
fmt.Print(greenColor)
|
|
||||||
self.out.Insert(dotSuccess)
|
|
||||||
}
|
|
||||||
fmt.Print(resetColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *dot) Exit() {}
|
|
||||||
|
|
||||||
func (self *dot) EndStory() {}
|
|
||||||
|
|
||||||
func (self *dot) Write(content []byte) (written int, err error) {
|
|
||||||
return len(content), nil // no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDotReporter(out *Printer) *dot {
|
|
||||||
self := new(dot)
|
|
||||||
self.out = out
|
|
||||||
return self
|
|
||||||
}
|
|
33
vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go
generated
vendored
33
vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go
generated
vendored
@ -1,33 +0,0 @@
|
|||||||
package reporting
|
|
||||||
|
|
||||||
type gotestReporter struct{ test T }
|
|
||||||
|
|
||||||
func (self *gotestReporter) BeginStory(story *StoryReport) {
|
|
||||||
self.test = story.Test
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *gotestReporter) Enter(scope *ScopeReport) {}
|
|
||||||
|
|
||||||
func (self *gotestReporter) Report(r *AssertionResult) {
|
|
||||||
if !passed(r) {
|
|
||||||
self.test.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *gotestReporter) Exit() {}
|
|
||||||
|
|
||||||
func (self *gotestReporter) EndStory() {
|
|
||||||
self.test = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *gotestReporter) Write(content []byte) (written int, err error) {
|
|
||||||
return len(content), nil // no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGoTestReporter() *gotestReporter {
|
|
||||||
return new(gotestReporter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func passed(r *AssertionResult) bool {
|
|
||||||
return r.Error == nil && r.Failure == ""
|
|
||||||
}
|
|
97
vendor/github.com/smartystreets/goconvey/convey/reporting/init.go
generated
vendored
97
vendor/github.com/smartystreets/goconvey/convey/reporting/init.go
generated
vendored
@ -1,97 +0,0 @@
|
|||||||
package reporting
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if !isXterm() {
|
|
||||||
monochrome()
|
|
||||||
}
|
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
success, failure, error_ = dotSuccess, dotFailure, dotError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BuildJsonReporter() Reporter {
|
|
||||||
out := NewPrinter(NewConsole())
|
|
||||||
return NewReporters(
|
|
||||||
NewGoTestReporter(),
|
|
||||||
NewJsonReporter(out))
|
|
||||||
}
|
|
||||||
func BuildDotReporter() Reporter {
|
|
||||||
out := NewPrinter(NewConsole())
|
|
||||||
return NewReporters(
|
|
||||||
NewGoTestReporter(),
|
|
||||||
NewDotReporter(out),
|
|
||||||
NewProblemReporter(out),
|
|
||||||
consoleStatistics)
|
|
||||||
}
|
|
||||||
func BuildStoryReporter() Reporter {
|
|
||||||
out := NewPrinter(NewConsole())
|
|
||||||
return NewReporters(
|
|
||||||
NewGoTestReporter(),
|
|
||||||
NewStoryReporter(out),
|
|
||||||
NewProblemReporter(out),
|
|
||||||
consoleStatistics)
|
|
||||||
}
|
|
||||||
func BuildSilentReporter() Reporter {
|
|
||||||
out := NewPrinter(NewConsole())
|
|
||||||
return NewReporters(
|
|
||||||
NewGoTestReporter(),
|
|
||||||
NewSilentProblemReporter(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
newline = "\n"
|
|
||||||
success = "✔"
|
|
||||||
failure = "✘"
|
|
||||||
error_ = "🔥"
|
|
||||||
skip = "⚠"
|
|
||||||
dotSuccess = "."
|
|
||||||
dotFailure = "x"
|
|
||||||
dotError = "E"
|
|
||||||
dotSkip = "S"
|
|
||||||
errorTemplate = "* %s \nLine %d: - %v \n%s\n"
|
|
||||||
failureTemplate = "* %s \nLine %d:\n%s\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
greenColor = "\033[32m"
|
|
||||||
yellowColor = "\033[33m"
|
|
||||||
redColor = "\033[31m"
|
|
||||||
resetColor = "\033[0m"
|
|
||||||
)
|
|
||||||
|
|
||||||
var consoleStatistics = NewStatisticsReporter(NewPrinter(NewConsole()))
|
|
||||||
|
|
||||||
func SuppressConsoleStatistics() { consoleStatistics.Suppress() }
|
|
||||||
func PrintConsoleStatistics() { consoleStatistics.PrintSummary() }
|
|
||||||
|
|
||||||
// QuiteMode disables all console output symbols. This is only meant to be used
|
|
||||||
// for tests that are internal to goconvey where the output is distracting or
|
|
||||||
// otherwise not needed in the test output.
|
|
||||||
func QuietMode() {
|
|
||||||
success, failure, error_, skip, dotSuccess, dotFailure, dotError, dotSkip = "", "", "", "", "", "", "", ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func monochrome() {
|
|
||||||
greenColor, yellowColor, redColor, resetColor = "", "", "", ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func isXterm() bool {
|
|
||||||
env := fmt.Sprintf("%v", os.Environ())
|
|
||||||
return strings.Contains(env, " TERM=isXterm") ||
|
|
||||||
strings.Contains(env, " TERM=xterm")
|
|
||||||
}
|
|
||||||
|
|
||||||
// This interface allows us to pass the *testing.T struct
|
|
||||||
// throughout the internals of this tool without ever
|
|
||||||
// having to import the "testing" package.
|
|
||||||
type T interface {
|
|
||||||
Fail()
|
|
||||||
}
|
|
88
vendor/github.com/smartystreets/goconvey/convey/reporting/json.go
generated
vendored
88
vendor/github.com/smartystreets/goconvey/convey/reporting/json.go
generated
vendored
@ -1,88 +0,0 @@
|
|||||||
// TODO: under unit test
|
|
||||||
|
|
||||||
package reporting
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JsonReporter struct {
|
|
||||||
out *Printer
|
|
||||||
currentKey []string
|
|
||||||
current *ScopeResult
|
|
||||||
index map[string]*ScopeResult
|
|
||||||
scopes []*ScopeResult
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *JsonReporter) depth() int { return len(self.currentKey) }
|
|
||||||
|
|
||||||
func (self *JsonReporter) BeginStory(story *StoryReport) {}
|
|
||||||
|
|
||||||
func (self *JsonReporter) Enter(scope *ScopeReport) {
|
|
||||||
self.currentKey = append(self.currentKey, scope.Title)
|
|
||||||
ID := strings.Join(self.currentKey, "|")
|
|
||||||
if _, found := self.index[ID]; !found {
|
|
||||||
next := newScopeResult(scope.Title, self.depth(), scope.File, scope.Line)
|
|
||||||
self.scopes = append(self.scopes, next)
|
|
||||||
self.index[ID] = next
|
|
||||||
}
|
|
||||||
self.current = self.index[ID]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *JsonReporter) Report(report *AssertionResult) {
|
|
||||||
self.current.Assertions = append(self.current.Assertions, report)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *JsonReporter) Exit() {
|
|
||||||
self.currentKey = self.currentKey[:len(self.currentKey)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *JsonReporter) EndStory() {
|
|
||||||
self.report()
|
|
||||||
self.reset()
|
|
||||||
}
|
|
||||||
func (self *JsonReporter) report() {
|
|
||||||
scopes := []string{}
|
|
||||||
for _, scope := range self.scopes {
|
|
||||||
serialized, err := json.Marshal(scope)
|
|
||||||
if err != nil {
|
|
||||||
self.out.Println(jsonMarshalFailure)
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
json.Indent(&buffer, serialized, "", " ")
|
|
||||||
scopes = append(scopes, buffer.String())
|
|
||||||
}
|
|
||||||
self.out.Print(fmt.Sprintf("%s\n%s,\n%s\n", OpenJson, strings.Join(scopes, ","), CloseJson))
|
|
||||||
}
|
|
||||||
func (self *JsonReporter) reset() {
|
|
||||||
self.scopes = []*ScopeResult{}
|
|
||||||
self.index = map[string]*ScopeResult{}
|
|
||||||
self.currentKey = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *JsonReporter) Write(content []byte) (written int, err error) {
|
|
||||||
self.current.Output += string(content)
|
|
||||||
return len(content), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewJsonReporter(out *Printer) *JsonReporter {
|
|
||||||
self := new(JsonReporter)
|
|
||||||
self.out = out
|
|
||||||
self.reset()
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
const OpenJson = ">->->OPEN-JSON->->->" // "⌦"
|
|
||||||
const CloseJson = "<-<-<-CLOSE-JSON<-<-<" // "⌫"
|
|
||||||
const jsonMarshalFailure = `
|
|
||||||
|
|
||||||
GOCONVEY_JSON_MARSHALL_FAILURE: There was an error when attempting to convert test results to JSON.
|
|
||||||
Please file a bug report and reference the code that caused this failure if possible.
|
|
||||||
|
|
||||||
Here's the panic:
|
|
||||||
|
|
||||||
`
|
|
57
vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go
generated
vendored
57
vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go
generated
vendored
@ -1,57 +0,0 @@
|
|||||||
package reporting
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Printer struct {
|
|
||||||
out io.Writer
|
|
||||||
prefix string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Printer) Println(message string, values ...interface{}) {
|
|
||||||
formatted := self.format(message, values...) + newline
|
|
||||||
self.out.Write([]byte(formatted))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Printer) Print(message string, values ...interface{}) {
|
|
||||||
formatted := self.format(message, values...)
|
|
||||||
self.out.Write([]byte(formatted))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Printer) Insert(text string) {
|
|
||||||
self.out.Write([]byte(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Printer) format(message string, values ...interface{}) string {
|
|
||||||
var formatted string
|
|
||||||
if len(values) == 0 {
|
|
||||||
formatted = self.prefix + message
|
|
||||||
} else {
|
|
||||||
formatted = self.prefix + fmt.Sprintf(message, values...)
|
|
||||||
}
|
|
||||||
indented := strings.Replace(formatted, newline, newline+self.prefix, -1)
|
|
||||||
return strings.TrimRight(indented, space)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Printer) Indent() {
|
|
||||||
self.prefix += pad
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Printer) Dedent() {
|
|
||||||
if len(self.prefix) >= padLength {
|
|
||||||
self.prefix = self.prefix[:len(self.prefix)-padLength]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrinter(out io.Writer) *Printer {
|
|
||||||
self := new(Printer)
|
|
||||||
self.out = out
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
const space = " "
|
|
||||||
const pad = space + space
|
|
||||||
const padLength = len(pad)
|
|
80
vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go
generated
vendored
80
vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go
generated
vendored
@ -1,80 +0,0 @@
|
|||||||
package reporting
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type problem struct {
|
|
||||||
silent bool
|
|
||||||
out *Printer
|
|
||||||
errors []*AssertionResult
|
|
||||||
failures []*AssertionResult
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *problem) BeginStory(story *StoryReport) {}
|
|
||||||
|
|
||||||
func (self *problem) Enter(scope *ScopeReport) {}
|
|
||||||
|
|
||||||
func (self *problem) Report(report *AssertionResult) {
|
|
||||||
if report.Error != nil {
|
|
||||||
self.errors = append(self.errors, report)
|
|
||||||
} else if report.Failure != "" {
|
|
||||||
self.failures = append(self.failures, report)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *problem) Exit() {}
|
|
||||||
|
|
||||||
func (self *problem) EndStory() {
|
|
||||||
self.show(self.showErrors, redColor)
|
|
||||||
self.show(self.showFailures, yellowColor)
|
|
||||||
self.prepareForNextStory()
|
|
||||||
}
|
|
||||||
func (self *problem) show(display func(), color string) {
|
|
||||||
if !self.silent {
|
|
||||||
fmt.Print(color)
|
|
||||||
}
|
|
||||||
display()
|
|
||||||
if !self.silent {
|
|
||||||
fmt.Print(resetColor)
|
|
||||||
}
|
|
||||||
self.out.Dedent()
|
|
||||||
}
|
|
||||||
func (self *problem) showErrors() {
|
|
||||||
for i, e := range self.errors {
|
|
||||||
if i == 0 {
|
|
||||||
self.out.Println("\nErrors:\n")
|
|
||||||
self.out.Indent()
|
|
||||||
}
|
|
||||||
self.out.Println(errorTemplate, e.File, e.Line, e.Error, e.StackTrace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (self *problem) showFailures() {
|
|
||||||
for i, f := range self.failures {
|
|
||||||
if i == 0 {
|
|
||||||
self.out.Println("\nFailures:\n")
|
|
||||||
self.out.Indent()
|
|
||||||
}
|
|
||||||
self.out.Println(failureTemplate, f.File, f.Line, f.Failure)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *problem) Write(content []byte) (written int, err error) {
|
|
||||||
return len(content), nil // no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProblemReporter(out *Printer) *problem {
|
|
||||||
self := new(problem)
|
|
||||||
self.out = out
|
|
||||||
self.prepareForNextStory()
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSilentProblemReporter(out *Printer) *problem {
|
|
||||||
self := NewProblemReporter(out)
|
|
||||||
self.silent = true
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *problem) prepareForNextStory() {
|
|
||||||
self.errors = []*AssertionResult{}
|
|
||||||
self.failures = []*AssertionResult{}
|
|
||||||
}
|
|
39
vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go
generated
vendored
39
vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go
generated
vendored
@ -1,39 +0,0 @@
|
|||||||
package reporting
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
type Reporter interface {
|
|
||||||
BeginStory(story *StoryReport)
|
|
||||||
Enter(scope *ScopeReport)
|
|
||||||
Report(r *AssertionResult)
|
|
||||||
Exit()
|
|
||||||
EndStory()
|
|
||||||
io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
type reporters struct{ collection []Reporter }
|
|
||||||
|
|
||||||
func (self *reporters) BeginStory(s *StoryReport) { self.foreach(func(r Reporter) { r.BeginStory(s) }) }
|
|
||||||
func (self *reporters) Enter(s *ScopeReport) { self.foreach(func(r Reporter) { r.Enter(s) }) }
|
|
||||||
func (self *reporters) Report(a *AssertionResult) { self.foreach(func(r Reporter) { r.Report(a) }) }
|
|
||||||
func (self *reporters) Exit() { self.foreach(func(r Reporter) { r.Exit() }) }
|
|
||||||
func (self *reporters) EndStory() { self.foreach(func(r Reporter) { r.EndStory() }) }
|
|
||||||
|
|
||||||
func (self *reporters) Write(contents []byte) (written int, err error) {
|
|
||||||
self.foreach(func(r Reporter) {
|
|
||||||
written, err = r.Write(contents)
|
|
||||||
})
|
|
||||||
return written, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *reporters) foreach(action func(Reporter)) {
|
|
||||||
for _, r := range self.collection {
|
|
||||||
action(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewReporters(collection ...Reporter) *reporters {
|
|
||||||
self := new(reporters)
|
|
||||||
self.collection = collection
|
|
||||||
return self
|
|
||||||
}
|
|
2
vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey
generated
vendored
2
vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey
generated
vendored
@ -1,2 +0,0 @@
|
|||||||
#ignore
|
|
||||||
-timeout=1s
|
|
177
vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go
generated
vendored
177
vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go
generated
vendored
@ -1,177 +0,0 @@
|
|||||||
package reporting
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/smartystreets/goconvey/convey/gotest"
|
|
||||||
)
|
|
||||||
|
|
||||||
////////////////// ScopeReport ////////////////////
|
|
||||||
|
|
||||||
type ScopeReport struct {
|
|
||||||
Title string
|
|
||||||
File string
|
|
||||||
Line int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewScopeReport(title string) *ScopeReport {
|
|
||||||
file, line, _ := gotest.ResolveExternalCaller()
|
|
||||||
self := new(ScopeReport)
|
|
||||||
self.Title = title
|
|
||||||
self.File = file
|
|
||||||
self.Line = line
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////// ScopeResult ////////////////////
|
|
||||||
|
|
||||||
type ScopeResult struct {
|
|
||||||
Title string
|
|
||||||
File string
|
|
||||||
Line int
|
|
||||||
Depth int
|
|
||||||
Assertions []*AssertionResult
|
|
||||||
Output string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newScopeResult(title string, depth int, file string, line int) *ScopeResult {
|
|
||||||
self := new(ScopeResult)
|
|
||||||
self.Title = title
|
|
||||||
self.Depth = depth
|
|
||||||
self.File = file
|
|
||||||
self.Line = line
|
|
||||||
self.Assertions = []*AssertionResult{}
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////// StoryReport /////////////////////
|
|
||||||
|
|
||||||
type StoryReport struct {
|
|
||||||
Test T
|
|
||||||
Name string
|
|
||||||
File string
|
|
||||||
Line int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStoryReport(test T) *StoryReport {
|
|
||||||
file, line, name := gotest.ResolveExternalCaller()
|
|
||||||
name = removePackagePath(name)
|
|
||||||
self := new(StoryReport)
|
|
||||||
self.Test = test
|
|
||||||
self.Name = name
|
|
||||||
self.File = file
|
|
||||||
self.Line = line
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
// name comes in looking like "github.com/smartystreets/goconvey/examples.TestName".
|
|
||||||
// We only want the stuff after the last '.', which is the name of the test function.
|
|
||||||
func removePackagePath(name string) string {
|
|
||||||
parts := strings.Split(name, ".")
|
|
||||||
return parts[len(parts)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////// FailureView ////////////////////////
|
|
||||||
|
|
||||||
type FailureView struct {
|
|
||||||
Message string
|
|
||||||
Expected string
|
|
||||||
Actual string
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////AssertionResult //////////////////////
|
|
||||||
|
|
||||||
type AssertionResult struct {
|
|
||||||
File string
|
|
||||||
Line int
|
|
||||||
Expected string
|
|
||||||
Actual string
|
|
||||||
Failure string
|
|
||||||
Error interface{}
|
|
||||||
StackTrace string
|
|
||||||
Skipped bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFailureReport(failure string) *AssertionResult {
|
|
||||||
report := new(AssertionResult)
|
|
||||||
report.File, report.Line = caller()
|
|
||||||
report.StackTrace = stackTrace()
|
|
||||||
parseFailure(failure, report)
|
|
||||||
return report
|
|
||||||
}
|
|
||||||
func parseFailure(failure string, report *AssertionResult) {
|
|
||||||
view := new(FailureView)
|
|
||||||
err := json.Unmarshal([]byte(failure), view)
|
|
||||||
if err == nil {
|
|
||||||
report.Failure = view.Message
|
|
||||||
report.Expected = view.Expected
|
|
||||||
report.Actual = view.Actual
|
|
||||||
} else {
|
|
||||||
report.Failure = failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func NewErrorReport(err interface{}) *AssertionResult {
|
|
||||||
report := new(AssertionResult)
|
|
||||||
report.File, report.Line = caller()
|
|
||||||
report.StackTrace = fullStackTrace()
|
|
||||||
report.Error = fmt.Sprintf("%v", err)
|
|
||||||
return report
|
|
||||||
}
|
|
||||||
func NewSuccessReport() *AssertionResult {
|
|
||||||
return new(AssertionResult)
|
|
||||||
}
|
|
||||||
func NewSkipReport() *AssertionResult {
|
|
||||||
report := new(AssertionResult)
|
|
||||||
report.File, report.Line = caller()
|
|
||||||
report.StackTrace = fullStackTrace()
|
|
||||||
report.Skipped = true
|
|
||||||
return report
|
|
||||||
}
|
|
||||||
|
|
||||||
func caller() (file string, line int) {
|
|
||||||
file, line, _ = gotest.ResolveExternalCaller()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func stackTrace() string {
|
|
||||||
buffer := make([]byte, 1024*64)
|
|
||||||
n := runtime.Stack(buffer, false)
|
|
||||||
return removeInternalEntries(string(buffer[:n]))
|
|
||||||
}
|
|
||||||
func fullStackTrace() string {
|
|
||||||
buffer := make([]byte, 1024*64)
|
|
||||||
n := runtime.Stack(buffer, true)
|
|
||||||
return removeInternalEntries(string(buffer[:n]))
|
|
||||||
}
|
|
||||||
func removeInternalEntries(stack string) string {
|
|
||||||
lines := strings.Split(stack, newline)
|
|
||||||
filtered := []string{}
|
|
||||||
for _, line := range lines {
|
|
||||||
if !isExternal(line) {
|
|
||||||
filtered = append(filtered, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(filtered, newline)
|
|
||||||
}
|
|
||||||
func isExternal(line string) bool {
|
|
||||||
for _, p := range internalPackages {
|
|
||||||
if strings.Contains(line, p) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: any new packages that host goconvey packages will need to be added here!
|
|
||||||
// An alternative is to scan the goconvey directory and then exclude stuff like
|
|
||||||
// the examples package but that's nasty too.
|
|
||||||
var internalPackages = []string{
|
|
||||||
"goconvey/assertions",
|
|
||||||
"goconvey/convey",
|
|
||||||
"goconvey/execution",
|
|
||||||
"goconvey/gotest",
|
|
||||||
"goconvey/reporting",
|
|
||||||
}
|
|
89
vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go
generated
vendored
89
vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go
generated
vendored
@ -1,89 +0,0 @@
|
|||||||
package reporting
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func (self *statistics) BeginStory(story *StoryReport) {}
|
|
||||||
|
|
||||||
func (self *statistics) Enter(scope *ScopeReport) {}
|
|
||||||
|
|
||||||
func (self *statistics) Report(report *AssertionResult) {
|
|
||||||
if !self.failing && report.Failure != "" {
|
|
||||||
self.failing = true
|
|
||||||
}
|
|
||||||
if !self.erroring && report.Error != nil {
|
|
||||||
self.erroring = true
|
|
||||||
}
|
|
||||||
if report.Skipped {
|
|
||||||
self.skipped += 1
|
|
||||||
} else {
|
|
||||||
self.total++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *statistics) Exit() {}
|
|
||||||
|
|
||||||
func (self *statistics) EndStory() {
|
|
||||||
if !self.suppressed {
|
|
||||||
self.PrintSummary()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *statistics) Suppress() {
|
|
||||||
self.suppressed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *statistics) PrintSummary() {
|
|
||||||
self.reportAssertions()
|
|
||||||
self.reportSkippedSections()
|
|
||||||
self.completeReport()
|
|
||||||
}
|
|
||||||
func (self *statistics) reportAssertions() {
|
|
||||||
self.decideColor()
|
|
||||||
self.out.Print("\n%d total %s", self.total, plural("assertion", self.total))
|
|
||||||
}
|
|
||||||
func (self *statistics) decideColor() {
|
|
||||||
if self.failing && !self.erroring {
|
|
||||||
fmt.Print(yellowColor)
|
|
||||||
} else if self.erroring {
|
|
||||||
fmt.Print(redColor)
|
|
||||||
} else {
|
|
||||||
fmt.Print(greenColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (self *statistics) reportSkippedSections() {
|
|
||||||
if self.skipped > 0 {
|
|
||||||
fmt.Print(yellowColor)
|
|
||||||
self.out.Print(" (one or more sections skipped)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (self *statistics) completeReport() {
|
|
||||||
fmt.Print(resetColor)
|
|
||||||
self.out.Print("\n")
|
|
||||||
self.out.Print("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *statistics) Write(content []byte) (written int, err error) {
|
|
||||||
return len(content), nil // no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStatisticsReporter(out *Printer) *statistics {
|
|
||||||
self := statistics{}
|
|
||||||
self.out = out
|
|
||||||
return &self
|
|
||||||
}
|
|
||||||
|
|
||||||
type statistics struct {
|
|
||||||
out *Printer
|
|
||||||
total int
|
|
||||||
failing bool
|
|
||||||
erroring bool
|
|
||||||
skipped int
|
|
||||||
suppressed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func plural(word string, count int) string {
|
|
||||||
if count == 1 {
|
|
||||||
return word
|
|
||||||
}
|
|
||||||
return word + "s"
|
|
||||||
}
|
|
73
vendor/github.com/smartystreets/goconvey/convey/reporting/story.go
generated
vendored
73
vendor/github.com/smartystreets/goconvey/convey/reporting/story.go
generated
vendored
@ -1,73 +0,0 @@
|
|||||||
// TODO: in order for this reporter to be completely honest
|
|
||||||
// we need to retrofit to be more like the json reporter such that:
|
|
||||||
// 1. it maintains ScopeResult collections, which count assertions
|
|
||||||
// 2. it reports only after EndStory(), so that all tick marks
|
|
||||||
// are placed near the appropriate title.
|
|
||||||
// 3. Under unit test
|
|
||||||
|
|
||||||
package reporting
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type story struct {
|
|
||||||
out *Printer
|
|
||||||
titlesById map[string]string
|
|
||||||
currentKey []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *story) BeginStory(story *StoryReport) {}
|
|
||||||
|
|
||||||
func (self *story) Enter(scope *ScopeReport) {
|
|
||||||
self.out.Indent()
|
|
||||||
|
|
||||||
self.currentKey = append(self.currentKey, scope.Title)
|
|
||||||
ID := strings.Join(self.currentKey, "|")
|
|
||||||
|
|
||||||
if _, found := self.titlesById[ID]; !found {
|
|
||||||
self.out.Println("")
|
|
||||||
self.out.Print(scope.Title)
|
|
||||||
self.out.Insert(" ")
|
|
||||||
self.titlesById[ID] = scope.Title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *story) Report(report *AssertionResult) {
|
|
||||||
if report.Error != nil {
|
|
||||||
fmt.Print(redColor)
|
|
||||||
self.out.Insert(error_)
|
|
||||||
} else if report.Failure != "" {
|
|
||||||
fmt.Print(yellowColor)
|
|
||||||
self.out.Insert(failure)
|
|
||||||
} else if report.Skipped {
|
|
||||||
fmt.Print(yellowColor)
|
|
||||||
self.out.Insert(skip)
|
|
||||||
} else {
|
|
||||||
fmt.Print(greenColor)
|
|
||||||
self.out.Insert(success)
|
|
||||||
}
|
|
||||||
fmt.Print(resetColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *story) Exit() {
|
|
||||||
self.out.Dedent()
|
|
||||||
self.currentKey = self.currentKey[:len(self.currentKey)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *story) EndStory() {
|
|
||||||
self.titlesById = make(map[string]string)
|
|
||||||
self.out.Println("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *story) Write(content []byte) (written int, err error) {
|
|
||||||
return len(content), nil // no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStoryReporter(out *Printer) *story {
|
|
||||||
self := new(story)
|
|
||||||
self.out = out
|
|
||||||
self.titlesById = make(map[string]string)
|
|
||||||
return self
|
|
||||||
}
|
|
58
vendor/vendor.json
vendored
58
vendor/vendor.json
vendored
@ -5,14 +5,14 @@
|
|||||||
{
|
{
|
||||||
"checksumSHA1": "OeOvZ+3A1tRBdMD8GuS6R9zcnqA=",
|
"checksumSHA1": "OeOvZ+3A1tRBdMD8GuS6R9zcnqA=",
|
||||||
"path": "github.com/astaxie/beego/swagger",
|
"path": "github.com/astaxie/beego/swagger",
|
||||||
"revision": "a20ccde90d75695e4f0ae0353c88dba9781d366c",
|
"revision": "323a1c4214101331a4b71922c23d19b7409ac71f",
|
||||||
"revisionTime": "2016-12-05T14:58:29Z"
|
"revisionTime": "2017-03-06T13:59:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "95wt85GTZG0zi3Zk8g2jOVFV6Zg=",
|
"checksumSHA1": "epd3Y7nD7QVzTW0ppwK+q4pKo/4=",
|
||||||
"path": "github.com/astaxie/beego/utils",
|
"path": "github.com/astaxie/beego/utils",
|
||||||
"revision": "a20ccde90d75695e4f0ae0353c88dba9781d366c",
|
"revision": "323a1c4214101331a4b71922c23d19b7409ac71f",
|
||||||
"revisionTime": "2016-12-05T14:58:29Z"
|
"revisionTime": "2017-03-06T13:59:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "hveFTNQ9YEyYRs6SWuXM+XU9qRI=",
|
"checksumSHA1": "hveFTNQ9YEyYRs6SWuXM+XU9qRI=",
|
||||||
@ -27,22 +27,10 @@
|
|||||||
"revisionTime": "2016-01-13T11:48:05Z"
|
"revisionTime": "2016-01-13T11:48:05Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "dyGQP5AipmInoTQmnqvbFmUORpk=",
|
"checksumSHA1": "G778A9feKkdW7ZjeDdj5qoU0Ku4=",
|
||||||
"path": "github.com/gorilla/websocket",
|
"path": "github.com/gorilla/websocket",
|
||||||
"revision": "a68708917c6a4f06314ab4e52493cc61359c9d42",
|
"revision": "b258b4fadb573ac412f187b9f31974ea99d32f50",
|
||||||
"revisionTime": "2016-06-06T23:20:22Z"
|
"revisionTime": "2017-03-02T22:46:13Z"
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "ZxzYc1JwJ3U6kZbw/KGuPko5lSY=",
|
|
||||||
"path": "github.com/howeyc/fsnotify",
|
|
||||||
"revision": "f0c08ee9c60704c1879025f2ae0ff3e000082c13",
|
|
||||||
"revisionTime": "2015-10-03T19:46:02Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "tewA7jXVGCw1zb5mA0BDecWi4iQ=",
|
|
||||||
"path": "github.com/jtolds/gls",
|
|
||||||
"revision": "8ddce2a84170772b95dd5d576c48d517b22cac63",
|
|
||||||
"revisionTime": "2016-01-05T22:08:40Z"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "dNYxHiBLalTqluak2/Z8c3RsSEM=",
|
"checksumSHA1": "dNYxHiBLalTqluak2/Z8c3RsSEM=",
|
||||||
@ -56,36 +44,6 @@
|
|||||||
"revision": "80f8150043c80fb52dee6bc863a709cdac7ec8f8",
|
"revision": "80f8150043c80fb52dee6bc863a709cdac7ec8f8",
|
||||||
"revisionTime": "2016-08-06T14:40:29Z"
|
"revisionTime": "2016-08-06T14:40:29Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"checksumSHA1": "0DtXHkHCB8kOYM15F+Y5WYMMMuc=",
|
|
||||||
"path": "github.com/smartystreets/assertions",
|
|
||||||
"revision": "edb6e295a22c57a2aa353b2f0729b6eff9f9f4b7",
|
|
||||||
"revisionTime": "2015-12-10T17:08:24Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "QCsUvPHx/Ifqm+sJmocjSvePAIc=",
|
|
||||||
"path": "github.com/smartystreets/assertions/internal/oglematchers",
|
|
||||||
"revision": "edb6e295a22c57a2aa353b2f0729b6eff9f9f4b7",
|
|
||||||
"revisionTime": "2015-12-10T17:08:24Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "fQeXVv5U9dlo3ufH2vjk1GNf4Lo=",
|
|
||||||
"path": "github.com/smartystreets/goconvey/convey",
|
|
||||||
"revision": "a2d5d788f3afbce0989463251ccc5492fad832df",
|
|
||||||
"revisionTime": "2015-12-28T21:47:19Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "9LakndErFi5uCXtY1KWl0iRnT4c=",
|
|
||||||
"path": "github.com/smartystreets/goconvey/convey/gotest",
|
|
||||||
"revision": "a2d5d788f3afbce0989463251ccc5492fad832df",
|
|
||||||
"revisionTime": "2015-12-28T21:47:19Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "Ht2/XG8aSW7vWbwav9z5jKyTzzw=",
|
|
||||||
"path": "github.com/smartystreets/goconvey/convey/reporting",
|
|
||||||
"revision": "a2d5d788f3afbce0989463251ccc5492fad832df",
|
|
||||||
"revisionTime": "2015-12-28T21:47:19Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"checksumSHA1": "NIg5qnKx/D8Th7YX882++siXTDY=",
|
"checksumSHA1": "NIg5qnKx/D8Th7YX882++siXTDY=",
|
||||||
"path": "golang.org/x/sys/unix",
|
"path": "golang.org/x/sys/unix",
|
||||||
|
Loading…
Reference in New Issue
Block a user