mirror of
https://github.com/minio/minio.git
synced 2025-01-27 06:33:18 -05:00
commit
86bcfed2da
16
Godeps/Godeps.json
generated
16
Godeps/Godeps.json
generated
@ -21,6 +21,10 @@
|
|||||||
"ImportPath": "github.com/gorilla/mux",
|
"ImportPath": "github.com/gorilla/mux",
|
||||||
"Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf"
|
"Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/gorilla/rpc/v2",
|
||||||
|
"Rev": "f6dbf92d77c723632269bf29154cc91f2507693b"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/minio/check",
|
"ImportPath": "github.com/minio/check",
|
||||||
"Rev": "67f8c16c6c27bb03c82e41c2be533ace00035ab4"
|
"Rev": "67f8c16c6c27bb03c82e41c2be533ace00035ab4"
|
||||||
@ -29,18 +33,6 @@
|
|||||||
"ImportPath": "github.com/minio/cli",
|
"ImportPath": "github.com/minio/cli",
|
||||||
"Comment": "1.2.0-112-g823349c",
|
"Comment": "1.2.0-112-g823349c",
|
||||||
"Rev": "823349ce91e76834a4af0119d5bbc58fd4d2c6b0"
|
"Rev": "823349ce91e76834a4af0119d5bbc58fd4d2c6b0"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/stretchr/objx",
|
|
||||||
"Rev": "cbeaeb16a013161a98496fad62933b1d21786672"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/stretchr/testify/assert",
|
|
||||||
"Rev": "e4ec8152c15fc46bd5056ce65997a07c7d415325"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/stretchr/testify/mock",
|
|
||||||
"Rev": "e4ec8152c15fc46bd5056ce65997a07c7d415325"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
27
Godeps/_workspace/src/github.com/gorilla/rpc/v2/LICENSE
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/gorilla/rpc/v2/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2012 Rodrigo Moraes. 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.
|
6
Godeps/_workspace/src/github.com/gorilla/rpc/v2/README.md
generated
vendored
Normal file
6
Godeps/_workspace/src/github.com/gorilla/rpc/v2/README.md
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
rpc
|
||||||
|
===
|
||||||
|
|
||||||
|
gorilla/rpc is a foundation for RPC over HTTP services, providing access to the exported methods of an object through HTTP requests.
|
||||||
|
|
||||||
|
Read the full documentation here: http://www.gorillatoolkit.org/pkg/rpc
|
90
Godeps/_workspace/src/github.com/gorilla/rpc/v2/compression_selector.go
generated
vendored
Normal file
90
Godeps/_workspace/src/github.com/gorilla/rpc/v2/compression_selector.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/flate"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// gzipWriter writes and closes the gzip writer.
|
||||||
|
type gzipWriter struct {
|
||||||
|
w *gzip.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gw *gzipWriter) Write(p []byte) (n int, err error) {
|
||||||
|
defer gw.w.Close()
|
||||||
|
return gw.w.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gzipEncoder implements the gzip compressed http encoder.
|
||||||
|
type gzipEncoder struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *gzipEncoder) Encode(w http.ResponseWriter) io.Writer {
|
||||||
|
w.Header().Set("Content-Encoding", "gzip")
|
||||||
|
return &gzipWriter{gzip.NewWriter(w)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flateWriter writes and closes the flate writer.
|
||||||
|
type flateWriter struct {
|
||||||
|
w *flate.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fw *flateWriter) Write(p []byte) (n int, err error) {
|
||||||
|
defer fw.w.Close()
|
||||||
|
return fw.w.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// flateEncoder implements the flate compressed http encoder.
|
||||||
|
type flateEncoder struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *flateEncoder) Encode(w http.ResponseWriter) io.Writer {
|
||||||
|
fw, err := flate.NewWriter(w, flate.DefaultCompression)
|
||||||
|
if err != nil {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Encoding", "deflate")
|
||||||
|
return &flateWriter{fw}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompressionSelector generates the compressed http encoder.
|
||||||
|
type CompressionSelector struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// acceptedEnc returns the first compression type in "Accept-Encoding" header
|
||||||
|
// field of the request.
|
||||||
|
func acceptedEnc(req *http.Request) string {
|
||||||
|
encHeader := req.Header.Get("Accept-Encoding")
|
||||||
|
if encHeader == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
encTypes := strings.FieldsFunc(encHeader, func(r rune) bool {
|
||||||
|
return unicode.IsSpace(r) || r == ','
|
||||||
|
})
|
||||||
|
for _, enc := range encTypes {
|
||||||
|
if enc == "gzip" || enc == "deflate" {
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select method selects the correct compression encoder based on http HEADER.
|
||||||
|
func (_ *CompressionSelector) Select(r *http.Request) Encoder {
|
||||||
|
switch acceptedEnc(r) {
|
||||||
|
case "gzip":
|
||||||
|
return &gzipEncoder{}
|
||||||
|
case "flate":
|
||||||
|
return &flateEncoder{}
|
||||||
|
}
|
||||||
|
return DefaultEncoder
|
||||||
|
}
|
81
Godeps/_workspace/src/github.com/gorilla/rpc/v2/doc.go
generated
vendored
Normal file
81
Godeps/_workspace/src/github.com/gorilla/rpc/v2/doc.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package gorilla/rpc is a foundation for RPC over HTTP services, providing
|
||||||
|
access to the exported methods of an object through HTTP requests.
|
||||||
|
|
||||||
|
This package derives from the standard net/rpc package but uses a single HTTP
|
||||||
|
request per call instead of persistent connections. Other differences
|
||||||
|
compared to net/rpc:
|
||||||
|
|
||||||
|
- Multiple codecs can be registered in the same server.
|
||||||
|
- A codec is chosen based on the "Content-Type" header from the request.
|
||||||
|
- Service methods also receive http.Request as parameter.
|
||||||
|
- This package can be used on Google App Engine.
|
||||||
|
|
||||||
|
Let's setup a server and register a codec and service:
|
||||||
|
|
||||||
|
import (
|
||||||
|
"http"
|
||||||
|
"github.com/gorilla/rpc/v2"
|
||||||
|
"github.com/gorilla/rpc/v2/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
s := rpc.NewServer()
|
||||||
|
s.RegisterCodec(json.NewCodec(), "application/json")
|
||||||
|
s.RegisterService(new(HelloService), "")
|
||||||
|
http.Handle("/rpc", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
This server handles requests to the "/rpc" path using a JSON codec.
|
||||||
|
A codec is tied to a content type. In the example above, the JSON codec is
|
||||||
|
registered to serve requests with "application/json" as the value for the
|
||||||
|
"Content-Type" header. If the header includes a charset definition, it is
|
||||||
|
ignored; only the media-type part is taken into account.
|
||||||
|
|
||||||
|
A service can be registered using a name. If the name is empty, like in the
|
||||||
|
example above, it will be inferred from the service type.
|
||||||
|
|
||||||
|
That's all about the server setup. Now let's define a simple service:
|
||||||
|
|
||||||
|
type HelloArgs struct {
|
||||||
|
Who string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelloReply struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelloService struct {}
|
||||||
|
|
||||||
|
func (h *HelloService) Say(r *http.Request, args *HelloArgs, reply *HelloReply) error {
|
||||||
|
reply.Message = "Hello, " + args.Who + "!"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
The example above defines a service with a method "HelloService.Say" and
|
||||||
|
the arguments and reply related to that method.
|
||||||
|
|
||||||
|
The service must be exported (begin with an upper case letter) or local
|
||||||
|
(defined in the package registering the service).
|
||||||
|
|
||||||
|
When a service is registered, the server inspects the service methods
|
||||||
|
and make available the ones that follow these rules:
|
||||||
|
|
||||||
|
- The method name is exported.
|
||||||
|
- The method has three arguments: *http.Request, *args, *reply.
|
||||||
|
- All three arguments are pointers.
|
||||||
|
- The second and third arguments are exported or local.
|
||||||
|
- The method has return type error.
|
||||||
|
|
||||||
|
All other methods are ignored.
|
||||||
|
|
||||||
|
Gorilla has packages with common RPC codecs. Check out their documentation:
|
||||||
|
|
||||||
|
JSON: http://gorilla-web.appspot.com/pkg/rpc/json
|
||||||
|
*/
|
||||||
|
package rpc
|
43
Godeps/_workspace/src/github.com/gorilla/rpc/v2/encoder_selector.go
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/gorilla/rpc/v2/encoder_selector.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encoder interface contains the encoder for http response.
|
||||||
|
// Eg. gzip, flate compressions.
|
||||||
|
type Encoder interface {
|
||||||
|
Encode(w http.ResponseWriter) io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
type encoder struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ *encoder) Encode(w http.ResponseWriter) io.Writer {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultEncoder = &encoder{}
|
||||||
|
|
||||||
|
// EncoderSelector interface provides a way to select encoder using the http
|
||||||
|
// request. Typically people can use this to check HEADER of the request and
|
||||||
|
// figure out client capabilities.
|
||||||
|
// Eg. "Accept-Encoding" tells about supported compressions.
|
||||||
|
type EncoderSelector interface {
|
||||||
|
Select(r *http.Request) Encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
type encoderSelector struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ *encoderSelector) Select(_ *http.Request) Encoder {
|
||||||
|
return DefaultEncoder
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultEncoderSelector = &encoderSelector{}
|
57
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json/client.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json/client.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012-2013 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Request and Response
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// clientRequest represents a JSON-RPC request sent by a client.
|
||||||
|
type clientRequest struct {
|
||||||
|
// A String containing the name of the method to be invoked.
|
||||||
|
Method string `json:"method"`
|
||||||
|
// Object to pass as request parameter to the method.
|
||||||
|
Params [1]interface{} `json:"params"`
|
||||||
|
// The request id. This can be of any type. It is used to match the
|
||||||
|
// response with the request that it is replying to.
|
||||||
|
Id uint64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// clientResponse represents a JSON-RPC response returned to a client.
|
||||||
|
type clientResponse struct {
|
||||||
|
Result *json.RawMessage `json:"result"`
|
||||||
|
Error interface{} `json:"error"`
|
||||||
|
Id uint64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeClientRequest encodes parameters for a JSON-RPC client request.
|
||||||
|
func EncodeClientRequest(method string, args interface{}) ([]byte, error) {
|
||||||
|
c := &clientRequest{
|
||||||
|
Method: method,
|
||||||
|
Params: [1]interface{}{args},
|
||||||
|
Id: uint64(rand.Int63()),
|
||||||
|
}
|
||||||
|
return json.Marshal(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeClientResponse decodes the response body of a client request into
|
||||||
|
// the interface reply.
|
||||||
|
func DecodeClientResponse(r io.Reader, reply interface{}) error {
|
||||||
|
var c clientResponse
|
||||||
|
if err := json.NewDecoder(r).Decode(&c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.Error != nil {
|
||||||
|
return &Error{Data: c.Error}
|
||||||
|
}
|
||||||
|
return json.Unmarshal(*c.Result, reply)
|
||||||
|
}
|
58
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json/doc.go
generated
vendored
Normal file
58
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json/doc.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package gorilla/rpc/json provides a codec for JSON-RPC over HTTP services.
|
||||||
|
|
||||||
|
To register the codec in a RPC server:
|
||||||
|
|
||||||
|
import (
|
||||||
|
"http"
|
||||||
|
"github.com/gorilla/rpc/v2"
|
||||||
|
"github.com/gorilla/rpc/v2/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
s := rpc.NewServer()
|
||||||
|
s.RegisterCodec(json.NewCodec(), "application/json")
|
||||||
|
// [...]
|
||||||
|
http.Handle("/rpc", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
A codec is tied to a content type. In the example above, the server will use
|
||||||
|
the JSON codec for requests with "application/json" as the value for the
|
||||||
|
"Content-Type" header.
|
||||||
|
|
||||||
|
This package follows the JSON-RPC 1.0 specification:
|
||||||
|
|
||||||
|
http://json-rpc.org/wiki/specification
|
||||||
|
|
||||||
|
Request format is:
|
||||||
|
|
||||||
|
method:
|
||||||
|
The name of the method to be invoked, as a string in dotted notation
|
||||||
|
as in "Service.Method".
|
||||||
|
params:
|
||||||
|
An array with a single object to pass as argument to the method.
|
||||||
|
id:
|
||||||
|
The request id, a uint. It is used to match the response with the
|
||||||
|
request that it is replying to.
|
||||||
|
|
||||||
|
Response format is:
|
||||||
|
|
||||||
|
result:
|
||||||
|
The Object that was returned by the invoked method,
|
||||||
|
or null in case there was an error invoking the method.
|
||||||
|
error:
|
||||||
|
An Error object if there was an error invoking the method,
|
||||||
|
or null if there was no error.
|
||||||
|
id:
|
||||||
|
The same id as the request it is responding to.
|
||||||
|
|
||||||
|
Check the gorilla/rpc documentation for more details:
|
||||||
|
|
||||||
|
http://gorilla-web.appspot.com/pkg/rpc
|
||||||
|
*/
|
||||||
|
package json
|
117
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json/json_test.go
generated
vendored
Normal file
117
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json/json_test.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gorilla/rpc/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrResponseError = errors.New("response error")
|
||||||
|
ErrResponseJsonError = &Error{Data: map[string]interface{}{
|
||||||
|
"stackstrace": map[string]interface{}{"0": "foo()"},
|
||||||
|
"error": "a message",
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service1Request struct {
|
||||||
|
A int
|
||||||
|
B int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service1Response struct {
|
||||||
|
Result int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service1 struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error {
|
||||||
|
res.Result = req.A * req.B
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Service1) ResponseError(r *http.Request, req *Service1Request, res *Service1Response) error {
|
||||||
|
return ErrResponseError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Service1) ResponseJsonError(r *http.Request, req *Service1Request, res *Service1Response) error {
|
||||||
|
return ErrResponseJsonError
|
||||||
|
}
|
||||||
|
|
||||||
|
func execute(t *testing.T, s *rpc.Server, method string, req, res interface{}) error {
|
||||||
|
if !s.HasMethod(method) {
|
||||||
|
t.Fatal("Expected to be registered:", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := EncodeClientRequest(method, req)
|
||||||
|
body := bytes.NewBuffer(buf)
|
||||||
|
r, _ := http.NewRequest("POST", "http://localhost:8080/", body)
|
||||||
|
r.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
s.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
return DecodeClientResponse(w.Body, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeRaw(t *testing.T, s *rpc.Server, req json.RawMessage) (int, *bytes.Buffer) {
|
||||||
|
r, _ := http.NewRequest("POST", "http://localhost:8080/", bytes.NewBuffer(req))
|
||||||
|
r.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
s.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
return w.Code, w.Body
|
||||||
|
}
|
||||||
|
|
||||||
|
func field(name string, blob json.RawMessage) (v interface{}, ok bool) {
|
||||||
|
var obj map[string]interface{}
|
||||||
|
if err := json.Unmarshal(blob, &obj); err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
v, ok = obj[name]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService(t *testing.T) {
|
||||||
|
s := rpc.NewServer()
|
||||||
|
s.RegisterCodec(NewCodec(), "application/json")
|
||||||
|
s.RegisterService(new(Service1), "")
|
||||||
|
|
||||||
|
var res Service1Response
|
||||||
|
if err := execute(t, s, "Service1.Multiply", &Service1Request{4, 2}, &res); err != nil {
|
||||||
|
t.Error("Expected err to be nil, but got", err)
|
||||||
|
}
|
||||||
|
if res.Result != 8 {
|
||||||
|
t.Error("Expected res.Result to be 8, but got", res.Result)
|
||||||
|
}
|
||||||
|
if err := execute(t, s, "Service1.ResponseError", &Service1Request{4, 2}, &res); err == nil {
|
||||||
|
t.Errorf("Expected to get %q, but got nil", ErrResponseError)
|
||||||
|
} else if err.Error() != ErrResponseError.Error() {
|
||||||
|
t.Errorf("Expected to get %q, but got %q", ErrResponseError, err)
|
||||||
|
}
|
||||||
|
if code, res := executeRaw(t, s, json.RawMessage(`{"method":"Service1.Multiply","params":null,"id":5}`)); code != 400 {
|
||||||
|
t.Error("Expected response code to be 400, but got", code)
|
||||||
|
} else if v, ok := field("result", res.Bytes()); !ok || v != nil {
|
||||||
|
t.Errorf("Expected ok to be true and v to be nil, but got %v and %v", ok, v)
|
||||||
|
}
|
||||||
|
if err := execute(t, s, "Service1.ResponseJsonError", &Service1Request{4, 2}, &res); err == nil {
|
||||||
|
t.Errorf("Expected to get %q, but got nil", ErrResponseError)
|
||||||
|
} else if jsonErr, ok := err.(*Error); !ok {
|
||||||
|
t.Error("Expected err to be of a *json.Error type")
|
||||||
|
} else if !reflect.DeepEqual(jsonErr.Data, ErrResponseJsonError.Data) {
|
||||||
|
t.Errorf("Expected jsonErr to be %q, but got %q", ErrResponseJsonError, jsonErr)
|
||||||
|
}
|
||||||
|
}
|
155
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json/server.go
generated
vendored
Normal file
155
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json/server.go
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/rpc/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var null = json.RawMessage([]byte("null"))
|
||||||
|
|
||||||
|
// An Error is a wrapper for a JSON interface value. It can be used by either
|
||||||
|
// a service's handler func to write more complex JSON data to an error field
|
||||||
|
// of a server's response, or by a client to read it.
|
||||||
|
type Error struct {
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return fmt.Sprintf("%v", e.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Request and Response
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// serverRequest represents a JSON-RPC request received by the server.
|
||||||
|
type serverRequest struct {
|
||||||
|
// A String containing the name of the method to be invoked.
|
||||||
|
Method string `json:"method"`
|
||||||
|
// An Array of objects to pass as arguments to the method.
|
||||||
|
Params *json.RawMessage `json:"params"`
|
||||||
|
// The request id. This can be of any type. It is used to match the
|
||||||
|
// response with the request that it is replying to.
|
||||||
|
Id *json.RawMessage `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverResponse represents a JSON-RPC response returned by the server.
|
||||||
|
type serverResponse struct {
|
||||||
|
// The Object that was returned by the invoked method. This must be null
|
||||||
|
// in case there was an error invoking the method.
|
||||||
|
Result interface{} `json:"result"`
|
||||||
|
// An Error object if there was an error invoking the method. It must be
|
||||||
|
// null if there was no error.
|
||||||
|
Error interface{} `json:"error"`
|
||||||
|
// This must be the same id as the request it is responding to.
|
||||||
|
Id *json.RawMessage `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Codec
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// NewCodec returns a new JSON Codec.
|
||||||
|
func NewCodec() *Codec {
|
||||||
|
return &Codec{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Codec creates a CodecRequest to process each request.
|
||||||
|
type Codec struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest returns a CodecRequest.
|
||||||
|
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
|
||||||
|
return newCodecRequest(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// CodecRequest
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// newCodecRequest returns a new CodecRequest.
|
||||||
|
func newCodecRequest(r *http.Request) rpc.CodecRequest {
|
||||||
|
// Decode the request body and check if RPC method is valid.
|
||||||
|
req := new(serverRequest)
|
||||||
|
err := json.NewDecoder(r.Body).Decode(req)
|
||||||
|
r.Body.Close()
|
||||||
|
return &CodecRequest{request: req, err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodecRequest decodes and encodes a single request.
|
||||||
|
type CodecRequest struct {
|
||||||
|
request *serverRequest
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method returns the RPC method for the current request.
|
||||||
|
//
|
||||||
|
// The method uses a dotted notation as in "Service.Method".
|
||||||
|
func (c *CodecRequest) Method() (string, error) {
|
||||||
|
if c.err == nil {
|
||||||
|
return c.request.Method, nil
|
||||||
|
}
|
||||||
|
return "", c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadRequest fills the request object for the RPC method.
|
||||||
|
func (c *CodecRequest) ReadRequest(args interface{}) error {
|
||||||
|
if c.err == nil {
|
||||||
|
if c.request.Params != nil {
|
||||||
|
// JSON params is array value. RPC params is struct.
|
||||||
|
// Unmarshal into array containing the request struct.
|
||||||
|
params := [1]interface{}{args}
|
||||||
|
c.err = json.Unmarshal(*c.request.Params, ¶ms)
|
||||||
|
} else {
|
||||||
|
c.err = errors.New("rpc: method request ill-formed: missing params field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteResponse encodes the response and writes it to the ResponseWriter.
|
||||||
|
func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}) {
|
||||||
|
if c.request.Id != nil {
|
||||||
|
// Id is null for notifications and they don't have a response.
|
||||||
|
res := &serverResponse{
|
||||||
|
Result: reply,
|
||||||
|
Error: &null,
|
||||||
|
Id: c.request.Id,
|
||||||
|
}
|
||||||
|
c.writeServerResponse(w, 200, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CodecRequest) WriteError(w http.ResponseWriter, _ int, err error) {
|
||||||
|
res := &serverResponse{
|
||||||
|
Result: &null,
|
||||||
|
Id: c.request.Id,
|
||||||
|
}
|
||||||
|
if jsonErr, ok := err.(*Error); ok {
|
||||||
|
res.Error = jsonErr.Data
|
||||||
|
} else {
|
||||||
|
res.Error = err.Error()
|
||||||
|
}
|
||||||
|
c.writeServerResponse(w, 400, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CodecRequest) writeServerResponse(w http.ResponseWriter, status int, res *serverResponse) {
|
||||||
|
b, err := json.Marshal(res)
|
||||||
|
if err == nil {
|
||||||
|
w.WriteHeader(status)
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
w.Write(b)
|
||||||
|
} else {
|
||||||
|
// Not sure in which case will this happen. But seems harmless.
|
||||||
|
rpc.WriteError(w, 400, err.Error())
|
||||||
|
}
|
||||||
|
}
|
75
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/client.go
generated
vendored
Normal file
75
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/client.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Request and Response
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// clientRequest represents a JSON-RPC request sent by a client.
|
||||||
|
type clientRequest struct {
|
||||||
|
// JSON-RPC protocol.
|
||||||
|
Version string `json:"jsonrpc"`
|
||||||
|
|
||||||
|
// A String containing the name of the method to be invoked.
|
||||||
|
Method string `json:"method"`
|
||||||
|
|
||||||
|
// Object to pass as request parameter to the method.
|
||||||
|
Params interface{} `json:"params"`
|
||||||
|
|
||||||
|
// The request id. This can be of any type. It is used to match the
|
||||||
|
// response with the request that it is replying to.
|
||||||
|
Id uint64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// clientResponse represents a JSON-RPC response returned to a client.
|
||||||
|
type clientResponse struct {
|
||||||
|
Version string `json:"jsonrpc"`
|
||||||
|
Result *json.RawMessage `json:"result"`
|
||||||
|
Error *json.RawMessage `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeClientRequest encodes parameters for a JSON-RPC client request.
|
||||||
|
func EncodeClientRequest(method string, args interface{}) ([]byte, error) {
|
||||||
|
c := &clientRequest{
|
||||||
|
Version: "2.0",
|
||||||
|
Method: method,
|
||||||
|
Params: args,
|
||||||
|
Id: uint64(rand.Int63()),
|
||||||
|
}
|
||||||
|
return json.Marshal(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeClientResponse decodes the response body of a client request into
|
||||||
|
// the interface reply.
|
||||||
|
func DecodeClientResponse(r io.Reader, reply interface{}) error {
|
||||||
|
var c clientResponse
|
||||||
|
if err := json.NewDecoder(r).Decode(&c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.Error != nil {
|
||||||
|
jsonErr := &Error{}
|
||||||
|
if err := json.Unmarshal(*c.Error, jsonErr); err != nil {
|
||||||
|
return &Error{
|
||||||
|
Code: E_SERVER,
|
||||||
|
Message: string(*c.Error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Result == nil {
|
||||||
|
return ErrNullResult
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Unmarshal(*c.Result, reply)
|
||||||
|
}
|
39
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/error.go
generated
vendored
Normal file
39
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/error.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrorCode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
E_PARSE ErrorCode = -32700
|
||||||
|
E_INVALID_REQ ErrorCode = -32600
|
||||||
|
E_NO_METHOD ErrorCode = -32601
|
||||||
|
E_BAD_PARAMS ErrorCode = -32602
|
||||||
|
E_INTERNAL ErrorCode = -32603
|
||||||
|
E_SERVER ErrorCode = -32000
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrNullResult = errors.New("result is null")
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
// A Number that indicates the error type that occurred.
|
||||||
|
Code ErrorCode `json:"code"` /* required */
|
||||||
|
|
||||||
|
// A String providing a short description of the error.
|
||||||
|
// The message SHOULD be limited to a concise single sentence.
|
||||||
|
Message string `json:"message"` /* required */
|
||||||
|
|
||||||
|
// A Primitive or Structured value that contains additional information about the error.
|
||||||
|
Data interface{} `json:"data"` /* optional */
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
161
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/json_test.go
generated
vendored
Normal file
161
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/json_test.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gorilla/rpc/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResponseRecorder is an implementation of http.ResponseWriter that
|
||||||
|
// records its mutations for later inspection in tests.
|
||||||
|
type ResponseRecorder struct {
|
||||||
|
Code int // the HTTP response code from WriteHeader
|
||||||
|
HeaderMap http.Header // the HTTP response headers
|
||||||
|
Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
|
||||||
|
Flushed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRecorder returns an initialized ResponseRecorder.
|
||||||
|
func NewRecorder() *ResponseRecorder {
|
||||||
|
return &ResponseRecorder{
|
||||||
|
HeaderMap: make(http.Header),
|
||||||
|
Body: new(bytes.Buffer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
|
||||||
|
// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
|
||||||
|
const DefaultRemoteAddr = "1.2.3.4"
|
||||||
|
|
||||||
|
// Header returns the response headers.
|
||||||
|
func (rw *ResponseRecorder) Header() http.Header {
|
||||||
|
return rw.HeaderMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write always succeeds and writes to rw.Body, if not nil.
|
||||||
|
func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
|
||||||
|
if rw.Body != nil {
|
||||||
|
rw.Body.Write(buf)
|
||||||
|
}
|
||||||
|
if rw.Code == 0 {
|
||||||
|
rw.Code = http.StatusOK
|
||||||
|
}
|
||||||
|
return len(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader sets rw.Code.
|
||||||
|
func (rw *ResponseRecorder) WriteHeader(code int) {
|
||||||
|
rw.Code = code
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush sets rw.Flushed to true.
|
||||||
|
func (rw *ResponseRecorder) Flush() {
|
||||||
|
rw.Flushed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var ErrResponseError = errors.New("response error")
|
||||||
|
|
||||||
|
type Service1Request struct {
|
||||||
|
A int
|
||||||
|
B int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service1BadRequest struct {
|
||||||
|
V string `json:"jsonrpc"`
|
||||||
|
M string `json:"method"`
|
||||||
|
ID uint64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service1Response struct {
|
||||||
|
Result int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service1 struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error {
|
||||||
|
res.Result = req.A * req.B
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Service1) ResponseError(r *http.Request, req *Service1Request, res *Service1Response) error {
|
||||||
|
return ErrResponseError
|
||||||
|
}
|
||||||
|
|
||||||
|
func execute(t *testing.T, s *rpc.Server, method string, req, res interface{}) error {
|
||||||
|
if !s.HasMethod(method) {
|
||||||
|
t.Fatal("Expected to be registered:", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := EncodeClientRequest(method, req)
|
||||||
|
body := bytes.NewBuffer(buf)
|
||||||
|
r, _ := http.NewRequest("POST", "http://localhost:8080/", body)
|
||||||
|
r.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
w := NewRecorder()
|
||||||
|
s.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
return DecodeClientResponse(w.Body, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeRaw(t *testing.T, s *rpc.Server, req interface{}, res interface{}) error {
|
||||||
|
j, _ := json.Marshal(req)
|
||||||
|
r, _ := http.NewRequest("POST", "http://localhost:8080/", bytes.NewBuffer(j))
|
||||||
|
r.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
w := NewRecorder()
|
||||||
|
s.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
return DecodeClientResponse(w.Body, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService(t *testing.T) {
|
||||||
|
s := rpc.NewServer()
|
||||||
|
s.RegisterCodec(NewCodec(), "application/json")
|
||||||
|
s.RegisterService(new(Service1), "")
|
||||||
|
|
||||||
|
var res Service1Response
|
||||||
|
if err := execute(t, s, "Service1.Multiply", &Service1Request{4, 2}, &res); err != nil {
|
||||||
|
t.Error("Expected err to be nil, but got:", err)
|
||||||
|
}
|
||||||
|
if res.Result != 8 {
|
||||||
|
t.Errorf("Wrong response: %v.", res.Result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := execute(t, s, "Service1.ResponseError", &Service1Request{4, 2}, &res); err == nil {
|
||||||
|
t.Errorf("Expected to get %q, but got nil", ErrResponseError)
|
||||||
|
} else if err.Error() != ErrResponseError.Error() {
|
||||||
|
t.Errorf("Expected to get %q, but got %q", ErrResponseError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := executeRaw(t, s, &Service1BadRequest{"2.0", "Service1.Multiply", 1}, &res); err == nil {
|
||||||
|
t.Errorf("Expected error but error in nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeNullResult(t *testing.T) {
|
||||||
|
data := `{"jsonrpc": "2.0", "id": 12345, "result": null}`
|
||||||
|
reader := bytes.NewReader([]byte(data))
|
||||||
|
var result interface{}
|
||||||
|
|
||||||
|
err := DecodeClientResponse(reader, &result)
|
||||||
|
|
||||||
|
if err != ErrNullResult {
|
||||||
|
t.Error("Expected err no be ErrNullResult, but got:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
t.Error("Expected result to be nil, but got:", result)
|
||||||
|
}
|
||||||
|
}
|
190
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/server.go
generated
vendored
Normal file
190
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/server.go
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/rpc/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var null = json.RawMessage([]byte("null"))
|
||||||
|
var Version = "2.0"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Request and Response
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// serverRequest represents a JSON-RPC request received by the server.
|
||||||
|
type serverRequest struct {
|
||||||
|
// JSON-RPC protocol.
|
||||||
|
Version string `json:"jsonrpc"`
|
||||||
|
|
||||||
|
// A String containing the name of the method to be invoked.
|
||||||
|
Method string `json:"method"`
|
||||||
|
|
||||||
|
// A Structured value to pass as arguments to the method.
|
||||||
|
Params *json.RawMessage `json:"params"`
|
||||||
|
|
||||||
|
// The request id. MUST be a string, number or null.
|
||||||
|
// Our implementation will not do type checking for id.
|
||||||
|
// It will be copied as it is.
|
||||||
|
Id *json.RawMessage `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverResponse represents a JSON-RPC response returned by the server.
|
||||||
|
type serverResponse struct {
|
||||||
|
// JSON-RPC protocol.
|
||||||
|
Version string `json:"jsonrpc"`
|
||||||
|
|
||||||
|
// The Object that was returned by the invoked method. This must be null
|
||||||
|
// in case there was an error invoking the method.
|
||||||
|
// As per spec the member will be omitted if there was an error.
|
||||||
|
Result interface{} `json:"result,omitempty"`
|
||||||
|
|
||||||
|
// An Error object if there was an error invoking the method. It must be
|
||||||
|
// null if there was no error.
|
||||||
|
// As per spec the member will be omitted if there was no error.
|
||||||
|
Error *Error `json:"error,omitempty"`
|
||||||
|
|
||||||
|
// This must be the same id as the request it is responding to.
|
||||||
|
Id *json.RawMessage `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Codec
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// NewcustomCodec returns a new JSON Codec based on passed encoder selector.
|
||||||
|
func NewCustomCodec(encSel rpc.EncoderSelector) *Codec {
|
||||||
|
return &Codec{encSel: encSel}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCodec returns a new JSON Codec.
|
||||||
|
func NewCodec() *Codec {
|
||||||
|
return NewCustomCodec(rpc.DefaultEncoderSelector)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Codec creates a CodecRequest to process each request.
|
||||||
|
type Codec struct {
|
||||||
|
encSel rpc.EncoderSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest returns a CodecRequest.
|
||||||
|
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
|
||||||
|
return newCodecRequest(r, c.encSel.Select(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// CodecRequest
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// newCodecRequest returns a new CodecRequest.
|
||||||
|
func newCodecRequest(r *http.Request, encoder rpc.Encoder) rpc.CodecRequest {
|
||||||
|
// Decode the request body and check if RPC method is valid.
|
||||||
|
req := new(serverRequest)
|
||||||
|
err := json.NewDecoder(r.Body).Decode(req)
|
||||||
|
if err != nil {
|
||||||
|
err = &Error{
|
||||||
|
Code: E_PARSE,
|
||||||
|
Message: err.Error(),
|
||||||
|
Data: req,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if req.Version != Version {
|
||||||
|
err = &Error{
|
||||||
|
Code: E_INVALID_REQ,
|
||||||
|
Message: "jsonrpc must be " + Version,
|
||||||
|
Data: req,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.Body.Close()
|
||||||
|
return &CodecRequest{request: req, err: err, encoder: encoder}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodecRequest decodes and encodes a single request.
|
||||||
|
type CodecRequest struct {
|
||||||
|
request *serverRequest
|
||||||
|
err error
|
||||||
|
encoder rpc.Encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method returns the RPC method for the current request.
|
||||||
|
//
|
||||||
|
// The method uses a dotted notation as in "Service.Method".
|
||||||
|
func (c *CodecRequest) Method() (string, error) {
|
||||||
|
if c.err == nil {
|
||||||
|
return c.request.Method, nil
|
||||||
|
}
|
||||||
|
return "", c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadRe<quest fills the request object for the RPC method.
|
||||||
|
func (c *CodecRequest) ReadRequest(args interface{}) error {
|
||||||
|
if c.err == nil {
|
||||||
|
if c.request.Params != nil {
|
||||||
|
// JSON params structured object. Unmarshal to the args object.
|
||||||
|
err := json.Unmarshal(*c.request.Params, args)
|
||||||
|
if err != nil {
|
||||||
|
c.err = &Error{
|
||||||
|
Code: E_INVALID_REQ,
|
||||||
|
Message: err.Error(),
|
||||||
|
Data: c.request.Params,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.err = &Error{
|
||||||
|
Code: E_INVALID_REQ,
|
||||||
|
Message: "rpc: method request ill-formed: missing params field",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteResponse encodes the response and writes it to the ResponseWriter.
|
||||||
|
func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}) {
|
||||||
|
res := &serverResponse{
|
||||||
|
Version: Version,
|
||||||
|
Result: reply,
|
||||||
|
Id: c.request.Id,
|
||||||
|
}
|
||||||
|
c.writeServerResponse(w, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CodecRequest) WriteError(w http.ResponseWriter, status int, err error) {
|
||||||
|
jsonErr, ok := err.(*Error)
|
||||||
|
if !ok {
|
||||||
|
jsonErr = &Error{
|
||||||
|
Code: E_SERVER,
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res := &serverResponse{
|
||||||
|
Version: Version,
|
||||||
|
Error: jsonErr,
|
||||||
|
Id: c.request.Id,
|
||||||
|
}
|
||||||
|
c.writeServerResponse(w, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CodecRequest) writeServerResponse(w http.ResponseWriter, res *serverResponse) {
|
||||||
|
// Id is null for notifications and they don't have a response.
|
||||||
|
if c.request.Id != nil {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
encoder := json.NewEncoder(c.encoder.Encode(w))
|
||||||
|
err := encoder.Encode(res)
|
||||||
|
|
||||||
|
// Not sure in which case will this happen. But seems harmless.
|
||||||
|
if err != nil {
|
||||||
|
rpc.WriteError(w, 400, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmptyResponse struct {
|
||||||
|
}
|
10
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/testapp/README.md
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/testapp/README.md
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Counter (TestApp)
|
||||||
|
=================
|
||||||
|
|
||||||
|
1) Build
|
||||||
|
|
||||||
|
$ go build counter.go
|
||||||
|
$ ./counter
|
||||||
|
|
||||||
|
|
||||||
|
2) Navigate to localhost:65534 on the browser to access the app.
|
50
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/testapp/counter.go
generated
vendored
Normal file
50
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/testapp/counter.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2013 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/rpc/v2"
|
||||||
|
"github.com/gorilla/rpc/v2/json2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Counter struct {
|
||||||
|
Count int
|
||||||
|
}
|
||||||
|
|
||||||
|
type IncrReq struct {
|
||||||
|
Delta int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification.
|
||||||
|
func (c *Counter) Incr(r *http.Request, req *IncrReq, res *json2.EmptyResponse) error {
|
||||||
|
log.Printf("<- Incr %+v", *req)
|
||||||
|
c.Count += req.Delta
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetReq struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Counter) Get(r *http.Request, req *GetReq, res *Counter) error {
|
||||||
|
log.Printf("<- Get %+v", *req)
|
||||||
|
*res = *c
|
||||||
|
log.Printf("-> %v", *res)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
address := flag.String("address", ":65534", "")
|
||||||
|
s := rpc.NewServer()
|
||||||
|
s.RegisterCodec(json2.NewCustomCodec(&rpc.CompressionSelector{}), "application/json")
|
||||||
|
s.RegisterService(new(Counter), "")
|
||||||
|
http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("./"))))
|
||||||
|
http.Handle("/jsonrpc/", s)
|
||||||
|
log.Fatal(http.ListenAndServe(*address, nil))
|
||||||
|
}
|
64
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/testapp/counter.js
generated
vendored
Normal file
64
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/testapp/counter.js
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
function log(m, label) {
|
||||||
|
msg = $("<li><span>" + m + "</span></li>");
|
||||||
|
msg.find("span").addClass(label);
|
||||||
|
out = $("#output");
|
||||||
|
out.append(msg);
|
||||||
|
out.animate({"scrollTop": out[0].scrollHeight}, "fast");
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$("#incr").click(function() {
|
||||||
|
req = {
|
||||||
|
method : "Counter.Incr",
|
||||||
|
params : {delta: 1},
|
||||||
|
};
|
||||||
|
log("<- " + JSON.stringify(req), "secondary label");
|
||||||
|
$.jsonrpc(req);
|
||||||
|
});
|
||||||
|
$("#get").click(function() {
|
||||||
|
req = {
|
||||||
|
method : "Counter.Get",
|
||||||
|
params : {},
|
||||||
|
};
|
||||||
|
log("<- " + JSON.stringify(req), "label");
|
||||||
|
$.jsonrpc(req, {
|
||||||
|
success : function(result) {
|
||||||
|
$("#get").addClass("success");
|
||||||
|
setTimeout(function() {
|
||||||
|
$("#get").removeClass("success");
|
||||||
|
}, 2000);
|
||||||
|
log("-> " + JSON.stringify(result), "success label");
|
||||||
|
},
|
||||||
|
error : function(error) {
|
||||||
|
$("#get").addClass("alert");
|
||||||
|
setTimeout(function() {
|
||||||
|
$("#get").removeClass("alert");
|
||||||
|
}, 2000);
|
||||||
|
log("-> " + JSON.stringify(error), "alert label");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$("#nan").click(function() {
|
||||||
|
req = {
|
||||||
|
method : "Counter.Nan",
|
||||||
|
params : {},
|
||||||
|
};
|
||||||
|
log("<- " + JSON.stringify(req), "label");
|
||||||
|
$.jsonrpc(req, {
|
||||||
|
success : function(result) {
|
||||||
|
$("#nan").addClass("success");
|
||||||
|
setTimeout(function() {
|
||||||
|
$("#nan").removeClass("success");
|
||||||
|
}, 2000);
|
||||||
|
log("-> " + JSON.stringify(result), "success label");
|
||||||
|
},
|
||||||
|
error : function(error) {
|
||||||
|
$("#nan").addClass("alert");
|
||||||
|
setTimeout(function() {
|
||||||
|
$("#nan").removeClass("alert");
|
||||||
|
}, 2000);
|
||||||
|
log("-> " + JSON.stringify(error), "alert label");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
32
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/testapp/index.html
generated
vendored
Normal file
32
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/testapp/index.html
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="//code.jquery.com/jquery-1.9.1.js"></script>
|
||||||
|
<link href="//cdn.jsdelivr.net/foundation/4.1.2/css/foundation.min.css" rel='stylesheet' type='text/css'>
|
||||||
|
<link href="//fonts.googleapis.com/css?family=Inconsolata" rel='stylesheet' type='text/css'>
|
||||||
|
<script src="jquery.jsonrpc.js"></script>
|
||||||
|
<script src="counter.js"></script>
|
||||||
|
<title>Counter</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Inconsolata', sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="padding:20px">
|
||||||
|
<div class="row">
|
||||||
|
<div class="large-1 columns"><a id="incr" href="#" class="small secondary button">Incr</a></div>
|
||||||
|
<div class="large-1 columns"><a id="get" href="#" class="small button">Get </a></div>
|
||||||
|
<div class="large-1 columns"><a id="nan" href="#" class="small button">Nan </a></div>
|
||||||
|
<div class="large-9 columns"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="large-12 columns panel">
|
||||||
|
<ul id="output" style="height:600px;overflow:scroll;list-style-type:none;">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
158
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/testapp/jquery.jsonrpc.js
generated
vendored
Normal file
158
Godeps/_workspace/src/github.com/gorilla/rpc/v2/json2/testapp/jquery.jsonrpc.js
generated
vendored
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* jQuery JSON-RPC Plugin
|
||||||
|
*
|
||||||
|
* @version: 0.3(2012-05-17)
|
||||||
|
* @author hagino3000 <http://twitter.com/hagino3000> (Takashi Nishibayashi)
|
||||||
|
* @author alanjds <http://twitter.com/alanjds> (Alan Justino da Silva)
|
||||||
|
*
|
||||||
|
* A JSON-RPC 2.0 implementation for jQuery.
|
||||||
|
* JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol.
|
||||||
|
* Read more in the <http://groups.google.com/group/json-rpc/web/json-rpc-2-0>
|
||||||
|
*
|
||||||
|
* Requires json2.js<http://www.json.org/json2.js> if browser has not window.JSON.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* $.jsonrpc(data [, callbacks [, debug]]);
|
||||||
|
*
|
||||||
|
* where data = {url: '/rpc/', method:'simplefunc', params:['posi', 'tional']}
|
||||||
|
* or data = {url: '/rpc/', method:'complexfunc', params:{nam:'ed', par:'ams'}}
|
||||||
|
* and callbacks = {success: successFunc, error: errorFunc}
|
||||||
|
*
|
||||||
|
* Setting no callback produces a JSON-RPC Notification.
|
||||||
|
* 'data' accepts 'timeout' keyword too, who sets the $.ajax request timeout.
|
||||||
|
* Setting 'debug' to true prints responses to Firebug's console.info
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* // A RPC call with named parameters
|
||||||
|
* $.jsonrpc({
|
||||||
|
* url : '/rpc',
|
||||||
|
* method : 'createUser',
|
||||||
|
* params : {name : 'John Smith', userId : '1000'}
|
||||||
|
* }, {
|
||||||
|
* success : function(result) {
|
||||||
|
* //doSomething
|
||||||
|
* },
|
||||||
|
* error : function(error) {
|
||||||
|
* //doSomething
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // Once set defaultUrl, url option is no need
|
||||||
|
* $.jsonrpc.defaultUrl = '/rpc';
|
||||||
|
*
|
||||||
|
* // A Notification
|
||||||
|
* $.jsonrpc({
|
||||||
|
* method : 'notify',
|
||||||
|
* params : {action : 'logout', userId : '1000'}
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // A Notification using console to debug and with timeout set
|
||||||
|
* $.jsonrpc({
|
||||||
|
* method : 'notify',
|
||||||
|
* params : {action : 'logout', userId : '1000'},
|
||||||
|
* debug : true,
|
||||||
|
* timeout : 500,
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // Set DataFilter. It is useful for buggy API that returns sometimes not json but html (when 500, 403..).
|
||||||
|
* $.jsonrpc({
|
||||||
|
* method : 'getUser',
|
||||||
|
* dataFilter : function(data, type) {
|
||||||
|
* try {
|
||||||
|
* return JSON.parse(data);
|
||||||
|
* } catch(e) {
|
||||||
|
* return {error : {message : 'Cannot parse response', data : data}};
|
||||||
|
* }
|
||||||
|
* }, function(result){ doSomething... }
|
||||||
|
* }, {
|
||||||
|
* success : handleSuccess
|
||||||
|
* error : handleFailure
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* This document is licensed as free software under the terms of the
|
||||||
|
* MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
(function($) {
|
||||||
|
|
||||||
|
var rpcid = 1,
|
||||||
|
emptyFn = function() {};
|
||||||
|
|
||||||
|
$.jsonrpc = $.jsonrpc || function(data, callbacks, debug) {
|
||||||
|
debug = debug || false;
|
||||||
|
|
||||||
|
var postdata = {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
method: data.method || '',
|
||||||
|
params: data.params || {}
|
||||||
|
};
|
||||||
|
if (callbacks) {
|
||||||
|
postdata.id = data.id || rpcid++;
|
||||||
|
} else {
|
||||||
|
callbacks = emptyFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(callbacks) === 'function') {
|
||||||
|
callbacks = {
|
||||||
|
success: callbacks,
|
||||||
|
error: callbacks
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataFilter = data.dataFilter;
|
||||||
|
|
||||||
|
var ajaxopts = {
|
||||||
|
url: data.url || $.jsonrpc.defaultUrl,
|
||||||
|
contentType: 'application/json',
|
||||||
|
dataType: 'text',
|
||||||
|
dataFilter: function(data, type) {
|
||||||
|
if (dataFilter) {
|
||||||
|
return dataFilter(data);
|
||||||
|
} else {
|
||||||
|
if (data != "") return JSON.parse(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: 'POST',
|
||||||
|
processData: false,
|
||||||
|
data: JSON.stringify(postdata),
|
||||||
|
success: function(resp) {
|
||||||
|
if (resp && !resp.error) {
|
||||||
|
return callbacks.success && callbacks.success(resp.result);
|
||||||
|
} else if (resp && resp.error) {
|
||||||
|
return callbacks.error && callbacks.error(resp.error);
|
||||||
|
} else {
|
||||||
|
return callbacks.error && callbacks.error(resp);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
if (error === 'timeout') {
|
||||||
|
callbacks.error({
|
||||||
|
status: status,
|
||||||
|
code: 0,
|
||||||
|
message: 'Request Timeout'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If response code is 404, 400, 500, server returns error object
|
||||||
|
try {
|
||||||
|
var res = JSON.parse(xhr.responseText);
|
||||||
|
callbacks.error(res.error);
|
||||||
|
} catch (e) {
|
||||||
|
callbacks.error({
|
||||||
|
status: status,
|
||||||
|
code: 0,
|
||||||
|
message: error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (data.timeout) {
|
||||||
|
ajaxopts['timeout'] = data.timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax(ajaxopts);
|
||||||
|
|
||||||
|
return $;
|
||||||
|
}
|
||||||
|
$.jsonrpc.defaultUrl = $.jsonrpc.defaultUrl || '/jsonrpc/';
|
||||||
|
|
||||||
|
})(jQuery);
|
164
Godeps/_workspace/src/github.com/gorilla/rpc/v2/map.go
generated
vendored
Normal file
164
Godeps/_workspace/src/github.com/gorilla/rpc/v2/map.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Precompute the reflect.Type of error and http.Request
|
||||||
|
typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
|
typeOfRequest = reflect.TypeOf((*http.Request)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// service
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type service struct {
|
||||||
|
name string // name of service
|
||||||
|
rcvr reflect.Value // receiver of methods for the service
|
||||||
|
rcvrType reflect.Type // type of the receiver
|
||||||
|
methods map[string]*serviceMethod // registered methods
|
||||||
|
}
|
||||||
|
|
||||||
|
type serviceMethod struct {
|
||||||
|
method reflect.Method // receiver method
|
||||||
|
argsType reflect.Type // type of the request argument
|
||||||
|
replyType reflect.Type // type of the response argument
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// serviceMap
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// serviceMap is a registry for services.
|
||||||
|
type serviceMap struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
services map[string]*service
|
||||||
|
}
|
||||||
|
|
||||||
|
// register adds a new service using reflection to extract its methods.
|
||||||
|
func (m *serviceMap) register(rcvr interface{}, name string) error {
|
||||||
|
// Setup service.
|
||||||
|
s := &service{
|
||||||
|
name: name,
|
||||||
|
rcvr: reflect.ValueOf(rcvr),
|
||||||
|
rcvrType: reflect.TypeOf(rcvr),
|
||||||
|
methods: make(map[string]*serviceMethod),
|
||||||
|
}
|
||||||
|
if name == "" {
|
||||||
|
s.name = reflect.Indirect(s.rcvr).Type().Name()
|
||||||
|
if !isExported(s.name) {
|
||||||
|
return fmt.Errorf("rpc: type %q is not exported", s.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.name == "" {
|
||||||
|
return fmt.Errorf("rpc: no service name for type %q",
|
||||||
|
s.rcvrType.String())
|
||||||
|
}
|
||||||
|
// Setup methods.
|
||||||
|
for i := 0; i < s.rcvrType.NumMethod(); i++ {
|
||||||
|
method := s.rcvrType.Method(i)
|
||||||
|
mtype := method.Type
|
||||||
|
// Method must be exported.
|
||||||
|
if method.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Method needs four ins: receiver, *http.Request, *args, *reply.
|
||||||
|
if mtype.NumIn() != 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// First argument must be a pointer and must be http.Request.
|
||||||
|
reqType := mtype.In(1)
|
||||||
|
if reqType.Kind() != reflect.Ptr || reqType.Elem() != typeOfRequest {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Second argument must be a pointer and must be exported.
|
||||||
|
args := mtype.In(2)
|
||||||
|
if args.Kind() != reflect.Ptr || !isExportedOrBuiltin(args) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Third argument must be a pointer and must be exported.
|
||||||
|
reply := mtype.In(3)
|
||||||
|
if reply.Kind() != reflect.Ptr || !isExportedOrBuiltin(reply) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Method needs one out: error.
|
||||||
|
if mtype.NumOut() != 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if returnType := mtype.Out(0); returnType != typeOfError {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.methods[method.Name] = &serviceMethod{
|
||||||
|
method: method,
|
||||||
|
argsType: args.Elem(),
|
||||||
|
replyType: reply.Elem(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(s.methods) == 0 {
|
||||||
|
return fmt.Errorf("rpc: %q has no exported methods of suitable type",
|
||||||
|
s.name)
|
||||||
|
}
|
||||||
|
// Add to the map.
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
if m.services == nil {
|
||||||
|
m.services = make(map[string]*service)
|
||||||
|
} else if _, ok := m.services[s.name]; ok {
|
||||||
|
return fmt.Errorf("rpc: service already defined: %q", s.name)
|
||||||
|
}
|
||||||
|
m.services[s.name] = s
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get returns a registered service given a method name.
|
||||||
|
//
|
||||||
|
// The method name uses a dotted notation as in "Service.Method".
|
||||||
|
func (m *serviceMap) get(method string) (*service, *serviceMethod, error) {
|
||||||
|
parts := strings.Split(method, ".")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
err := fmt.Errorf("rpc: service/method request ill-formed: %q", method)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
m.mutex.Lock()
|
||||||
|
service := m.services[parts[0]]
|
||||||
|
m.mutex.Unlock()
|
||||||
|
if service == nil {
|
||||||
|
err := fmt.Errorf("rpc: can't find service %q", method)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
serviceMethod := service.methods[parts[1]]
|
||||||
|
if serviceMethod == nil {
|
||||||
|
err := fmt.Errorf("rpc: can't find method %q", method)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return service, serviceMethod, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isExported returns true of a string is an exported (upper case) name.
|
||||||
|
func isExported(name string) bool {
|
||||||
|
rune, _ := utf8.DecodeRuneInString(name)
|
||||||
|
return unicode.IsUpper(rune)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isExportedOrBuiltin returns true if a type is exported or a builtin.
|
||||||
|
func isExportedOrBuiltin(t reflect.Type) bool {
|
||||||
|
for t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
// PkgPath will be non-empty even for an exported type,
|
||||||
|
// so we need to check the type name as well.
|
||||||
|
return isExported(t.Name()) || t.PkgPath() == ""
|
||||||
|
}
|
48
Godeps/_workspace/src/github.com/gorilla/rpc/v2/protorpc/doc.go
generated
vendored
Normal file
48
Godeps/_workspace/src/github.com/gorilla/rpc/v2/protorpc/doc.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package gorilla/rpc/protorpc provides a codec for ProtoRPC over HTTP services.
|
||||||
|
|
||||||
|
To register the codec in a RPC server:
|
||||||
|
|
||||||
|
import (
|
||||||
|
"http"
|
||||||
|
"github.com/gorilla/rpc/v2"
|
||||||
|
"github.com/gorilla/rpc/v2/protorpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
s := rpc.NewServer()
|
||||||
|
s.RegisterCodec(protorpc.NewCodec(), "application/json")
|
||||||
|
// [...]
|
||||||
|
http.Handle("/rpc", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
A codec is tied to a content type. In the example above, the server
|
||||||
|
will use the ProtoRPC codec for requests with "application/json" as
|
||||||
|
the value for the "Content-Type" header.
|
||||||
|
|
||||||
|
This package implement ProtoRPC, based on the JSON-RPC transport, it
|
||||||
|
differs in that it uses HTTP as its envelope.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
POST /Service.Method
|
||||||
|
Request:
|
||||||
|
{
|
||||||
|
"requestField1": "value1",
|
||||||
|
"requestField2": "value2",
|
||||||
|
}
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"responseField1": "value1",
|
||||||
|
"responseField2": "value2",
|
||||||
|
}
|
||||||
|
|
||||||
|
Check the gorilla/rpc documentation for more details:
|
||||||
|
|
||||||
|
http://gorilla-web.appspot.com/pkg/rpc
|
||||||
|
*/
|
||||||
|
package protorpc
|
87
Godeps/_workspace/src/github.com/gorilla/rpc/v2/protorpc/protorpc_test.go
generated
vendored
Normal file
87
Godeps/_workspace/src/github.com/gorilla/rpc/v2/protorpc/protorpc_test.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package protorpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gorilla/rpc/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrResponseError = errors.New("response error")
|
||||||
|
|
||||||
|
type Service1Request struct {
|
||||||
|
A int
|
||||||
|
B int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service1BadRequest struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service1Response struct {
|
||||||
|
Result int
|
||||||
|
ErrorMessage string `json:"error_message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service1 struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error {
|
||||||
|
res.Result = req.A * req.B
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Service1) ResponseError(r *http.Request, req *Service1Request, res *Service1Response) error {
|
||||||
|
return ErrResponseError
|
||||||
|
}
|
||||||
|
|
||||||
|
func execute(t *testing.T, s *rpc.Server, method string, req, res interface{}) (int, error) {
|
||||||
|
if !s.HasMethod(method) {
|
||||||
|
t.Fatal("Expected to be registered:", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := json.Marshal(req)
|
||||||
|
body := bytes.NewBuffer(buf)
|
||||||
|
r, _ := http.NewRequest("POST", "http://localhost:8080/"+method, body)
|
||||||
|
r.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
s.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
err := json.NewDecoder(w.Body).Decode(res)
|
||||||
|
return w.Code, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService(t *testing.T) {
|
||||||
|
s := rpc.NewServer()
|
||||||
|
s.RegisterCodec(NewCodec(), "application/json")
|
||||||
|
s.RegisterService(new(Service1), "")
|
||||||
|
|
||||||
|
var res Service1Response
|
||||||
|
if _, err := execute(t, s, "Service1.Multiply", &Service1Request{4, 2}, &res); err != nil {
|
||||||
|
t.Error("Expected err to be nil, but got:", err)
|
||||||
|
}
|
||||||
|
if res.Result != 8 {
|
||||||
|
t.Error("Expected res.Result to be 8, but got:", res.Result)
|
||||||
|
}
|
||||||
|
if res.ErrorMessage != "" {
|
||||||
|
t.Error("Expected error_message to be empty, but got:", res.ErrorMessage)
|
||||||
|
}
|
||||||
|
if code, err := execute(t, s, "Service1.ResponseError", &Service1Request{4, 2}, &res); err != nil || code != 400 {
|
||||||
|
t.Errorf("Expected code to be 400 and error to be nil, but got %v (%v)", code, err)
|
||||||
|
}
|
||||||
|
if res.ErrorMessage == "" {
|
||||||
|
t.Errorf("Expected error_message to be %q, but got %q", ErrResponseError, res.ErrorMessage)
|
||||||
|
}
|
||||||
|
if code, _ := execute(t, s, "Service1.Multiply", nil, &res); code != 400 {
|
||||||
|
t.Error("Expected http response code 400, but got", code)
|
||||||
|
}
|
||||||
|
}
|
147
Godeps/_workspace/src/github.com/gorilla/rpc/v2/protorpc/server.go
generated
vendored
Normal file
147
Godeps/_workspace/src/github.com/gorilla/rpc/v2/protorpc/server.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package protorpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/rpc/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var null = json.RawMessage([]byte("null"))
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Request and Response
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// serverRequest represents a ProtoRPC request received by the server.
|
||||||
|
type serverRequest struct {
|
||||||
|
// A String containing the name of the method to be invoked.
|
||||||
|
Method string `json:"method"`
|
||||||
|
// An Array of objects to pass as arguments to the method.
|
||||||
|
Params *json.RawMessage `json:"params"`
|
||||||
|
// The request id. This can be of any type. It is used to match the
|
||||||
|
// response with the request that it is replying to.
|
||||||
|
Id *json.RawMessage `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverResponse represents a ProtoRPC response returned by the server.
|
||||||
|
type serverResponse struct {
|
||||||
|
// The Object that was returned by the invoked method. This must be null
|
||||||
|
// in case there was an error invoking the method.
|
||||||
|
Result interface{} `json:"result"`
|
||||||
|
// An Error object if there was an error invoking the method. It must be
|
||||||
|
// null if there was no error.
|
||||||
|
Error interface{} `json:"error"`
|
||||||
|
// This must be the same id as the request it is responding to.
|
||||||
|
Id *json.RawMessage `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Codec
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// NewCodec returns a new ProtoRPC Codec.
|
||||||
|
func NewCodec() *Codec {
|
||||||
|
return &Codec{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Codec creates a CodecRequest to process each request.
|
||||||
|
type Codec struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest returns a CodecRequest.
|
||||||
|
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
|
||||||
|
return newCodecRequest(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// CodecRequest
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// newCodecRequest returns a new CodecRequest.
|
||||||
|
func newCodecRequest(r *http.Request) rpc.CodecRequest {
|
||||||
|
// Decode the request body and check if RPC method is valid.
|
||||||
|
req := new(serverRequest)
|
||||||
|
path := r.URL.Path
|
||||||
|
index := strings.LastIndex(path, "/")
|
||||||
|
if index < 0 {
|
||||||
|
return &CodecRequest{request: req, err: fmt.Errorf("rpc: no method: %s", path)}
|
||||||
|
}
|
||||||
|
req.Method = path[index+1:]
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&req.Params)
|
||||||
|
r.Body.Close()
|
||||||
|
var errr error
|
||||||
|
if err != io.EOF {
|
||||||
|
errr = err
|
||||||
|
}
|
||||||
|
return &CodecRequest{request: req, err: errr}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodecRequest decodes and encodes a single request.
|
||||||
|
type CodecRequest struct {
|
||||||
|
request *serverRequest
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method returns the RPC method for the current request.
|
||||||
|
//
|
||||||
|
// The method uses a dotted notation as in "Service.Method".
|
||||||
|
func (c *CodecRequest) Method() (string, error) {
|
||||||
|
if c.err == nil {
|
||||||
|
return c.request.Method, nil
|
||||||
|
}
|
||||||
|
return "", c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadRequest fills the request object for the RPC method.
|
||||||
|
func (c *CodecRequest) ReadRequest(args interface{}) error {
|
||||||
|
if c.err == nil {
|
||||||
|
if c.request.Params != nil {
|
||||||
|
c.err = json.Unmarshal(*c.request.Params, args)
|
||||||
|
} else {
|
||||||
|
c.err = errors.New("rpc: method request ill-formed: missing params field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteResponse encodes the response and writes it to the ResponseWriter.
|
||||||
|
func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}) {
|
||||||
|
res := &serverResponse{
|
||||||
|
Result: reply,
|
||||||
|
Error: &null,
|
||||||
|
Id: c.request.Id,
|
||||||
|
}
|
||||||
|
c.writeServerResponse(w, 200, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CodecRequest) WriteError(w http.ResponseWriter, status int, err error) {
|
||||||
|
res := &serverResponse{
|
||||||
|
Result: &struct {
|
||||||
|
ErrorMessage interface{} `json:"error_message"`
|
||||||
|
}{err.Error()},
|
||||||
|
Id: c.request.Id,
|
||||||
|
}
|
||||||
|
c.writeServerResponse(w, status, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CodecRequest) writeServerResponse(w http.ResponseWriter, status int, res *serverResponse) {
|
||||||
|
b, err := json.Marshal(res.Result)
|
||||||
|
if err == nil {
|
||||||
|
w.WriteHeader(status)
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
w.Write(b)
|
||||||
|
} else {
|
||||||
|
// Not sure in which case will this happen. But seems harmless.
|
||||||
|
rpc.WriteError(w, 400, err.Error())
|
||||||
|
}
|
||||||
|
}
|
158
Godeps/_workspace/src/github.com/gorilla/rpc/v2/server.go
generated
vendored
Normal file
158
Godeps/_workspace/src/github.com/gorilla/rpc/v2/server.go
generated
vendored
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Codec
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Codec creates a CodecRequest to process each request.
|
||||||
|
type Codec interface {
|
||||||
|
NewRequest(*http.Request) CodecRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodecRequest decodes a request and encodes a response using a specific
|
||||||
|
// serialization scheme.
|
||||||
|
type CodecRequest interface {
|
||||||
|
// Reads the request and returns the RPC method name.
|
||||||
|
Method() (string, error)
|
||||||
|
// Reads the request filling the RPC method args.
|
||||||
|
ReadRequest(interface{}) error
|
||||||
|
// Writes the response using the RPC method reply.
|
||||||
|
WriteResponse(http.ResponseWriter, interface{})
|
||||||
|
// Writes an error produced by the server.
|
||||||
|
WriteError(w http.ResponseWriter, status int, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Server
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// NewServer returns a new RPC server.
|
||||||
|
func NewServer() *Server {
|
||||||
|
return &Server{
|
||||||
|
codecs: make(map[string]Codec),
|
||||||
|
services: new(serviceMap),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server serves registered RPC services using registered codecs.
|
||||||
|
type Server struct {
|
||||||
|
codecs map[string]Codec
|
||||||
|
services *serviceMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterCodec adds a new codec to the server.
|
||||||
|
//
|
||||||
|
// Codecs are defined to process a given serialization scheme, e.g., JSON or
|
||||||
|
// XML. A codec is chosen based on the "Content-Type" header from the request,
|
||||||
|
// excluding the charset definition.
|
||||||
|
func (s *Server) RegisterCodec(codec Codec, contentType string) {
|
||||||
|
s.codecs[strings.ToLower(contentType)] = codec
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterService adds a new service to the server.
|
||||||
|
//
|
||||||
|
// The name parameter is optional: if empty it will be inferred from
|
||||||
|
// the receiver type name.
|
||||||
|
//
|
||||||
|
// Methods from the receiver will be extracted if these rules are satisfied:
|
||||||
|
//
|
||||||
|
// - The receiver is exported (begins with an upper case letter) or local
|
||||||
|
// (defined in the package registering the service).
|
||||||
|
// - The method name is exported.
|
||||||
|
// - The method has three arguments: *http.Request, *args, *reply.
|
||||||
|
// - All three arguments are pointers.
|
||||||
|
// - The second and third arguments are exported or local.
|
||||||
|
// - The method has return type error.
|
||||||
|
//
|
||||||
|
// All other methods are ignored.
|
||||||
|
func (s *Server) RegisterService(receiver interface{}, name string) error {
|
||||||
|
return s.services.register(receiver, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasMethod returns true if the given method is registered.
|
||||||
|
//
|
||||||
|
// The method uses a dotted notation as in "Service.Method".
|
||||||
|
func (s *Server) HasMethod(method string) bool {
|
||||||
|
if _, _, err := s.services.get(method); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP
|
||||||
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "POST" {
|
||||||
|
WriteError(w, 405, "rpc: POST method required, received "+r.Method)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
contentType := r.Header.Get("Content-Type")
|
||||||
|
idx := strings.Index(contentType, ";")
|
||||||
|
if idx != -1 {
|
||||||
|
contentType = contentType[:idx]
|
||||||
|
}
|
||||||
|
codec := s.codecs[strings.ToLower(contentType)]
|
||||||
|
if codec == nil {
|
||||||
|
WriteError(w, 415, "rpc: unrecognized Content-Type: "+contentType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Create a new codec request.
|
||||||
|
codecReq := codec.NewRequest(r)
|
||||||
|
// Get service method to be called.
|
||||||
|
method, errMethod := codecReq.Method()
|
||||||
|
if errMethod != nil {
|
||||||
|
codecReq.WriteError(w, 400, errMethod)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serviceSpec, methodSpec, errGet := s.services.get(method)
|
||||||
|
if errGet != nil {
|
||||||
|
codecReq.WriteError(w, 400, errGet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Decode the args.
|
||||||
|
args := reflect.New(methodSpec.argsType)
|
||||||
|
if errRead := codecReq.ReadRequest(args.Interface()); errRead != nil {
|
||||||
|
codecReq.WriteError(w, 400, errRead)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Call the service method.
|
||||||
|
reply := reflect.New(methodSpec.replyType)
|
||||||
|
errValue := methodSpec.method.Func.Call([]reflect.Value{
|
||||||
|
serviceSpec.rcvr,
|
||||||
|
reflect.ValueOf(r),
|
||||||
|
args,
|
||||||
|
reply,
|
||||||
|
})
|
||||||
|
// Cast the result to error if needed.
|
||||||
|
var errResult error
|
||||||
|
errInter := errValue[0].Interface()
|
||||||
|
if errInter != nil {
|
||||||
|
errResult = errInter.(error)
|
||||||
|
}
|
||||||
|
// Prevents Internet Explorer from MIME-sniffing a response away
|
||||||
|
// from the declared content-type
|
||||||
|
w.Header().Set("x-content-type-options", "nosniff")
|
||||||
|
// Encode the response.
|
||||||
|
if errResult == nil {
|
||||||
|
codecReq.WriteResponse(w, reply.Interface())
|
||||||
|
} else {
|
||||||
|
codecReq.WriteError(w, 400, errResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteError(w http.ResponseWriter, status int, msg string) {
|
||||||
|
w.WriteHeader(status)
|
||||||
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
|
fmt.Fprint(w, msg)
|
||||||
|
}
|
54
Godeps/_workspace/src/github.com/gorilla/rpc/v2/server_test.go
generated
vendored
Normal file
54
Godeps/_workspace/src/github.com/gorilla/rpc/v2/server_test.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service1Request struct {
|
||||||
|
A int
|
||||||
|
B int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service1Response struct {
|
||||||
|
Result int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service1 struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error {
|
||||||
|
res.Result = req.A * req.B
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service2 struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterService(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
s := NewServer()
|
||||||
|
service1 := new(Service1)
|
||||||
|
service2 := new(Service2)
|
||||||
|
|
||||||
|
// Inferred name.
|
||||||
|
err = s.RegisterService(service1, "")
|
||||||
|
if err != nil || !s.HasMethod("Service1.Multiply") {
|
||||||
|
t.Errorf("Expected to be registered: Service1.Multiply")
|
||||||
|
}
|
||||||
|
// Provided name.
|
||||||
|
err = s.RegisterService(service1, "Foo")
|
||||||
|
if err != nil || !s.HasMethod("Foo.Multiply") {
|
||||||
|
t.Errorf("Expected to be registered: Foo.Multiply")
|
||||||
|
}
|
||||||
|
// No methods.
|
||||||
|
err = s.RegisterService(service2, "")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error on service2")
|
||||||
|
}
|
||||||
|
}
|
22
Godeps/_workspace/src/github.com/stretchr/objx/.gitignore
generated
vendored
22
Godeps/_workspace/src/github.com/stretchr/objx/.gitignore
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
23
Godeps/_workspace/src/github.com/stretchr/objx/LICENSE.md
generated
vendored
23
Godeps/_workspace/src/github.com/stretchr/objx/LICENSE.md
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
objx - by Mat Ryer and Tyler Bunnell
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Stretchr, 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.
|
|
3
Godeps/_workspace/src/github.com/stretchr/objx/README.md
generated
vendored
3
Godeps/_workspace/src/github.com/stretchr/objx/README.md
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
# objx
|
|
||||||
|
|
||||||
* Jump into the [API Documentation](http://godoc.org/github.com/stretchr/objx)
|
|
179
Godeps/_workspace/src/github.com/stretchr/objx/accessors.go
generated
vendored
179
Godeps/_workspace/src/github.com/stretchr/objx/accessors.go
generated
vendored
@ -1,179 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// arrayAccesRegexString is the regex used to extract the array number
|
|
||||||
// from the access path
|
|
||||||
const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`
|
|
||||||
|
|
||||||
// arrayAccesRegex is the compiled arrayAccesRegexString
|
|
||||||
var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)
|
|
||||||
|
|
||||||
// Get gets the value using the specified selector and
|
|
||||||
// returns it inside a new Obj object.
|
|
||||||
//
|
|
||||||
// If it cannot find the value, Get will return a nil
|
|
||||||
// value inside an instance of Obj.
|
|
||||||
//
|
|
||||||
// Get can only operate directly on map[string]interface{} and []interface.
|
|
||||||
//
|
|
||||||
// Example
|
|
||||||
//
|
|
||||||
// To access the title of the third chapter of the second book, do:
|
|
||||||
//
|
|
||||||
// o.Get("books[1].chapters[2].title")
|
|
||||||
func (m Map) Get(selector string) *Value {
|
|
||||||
rawObj := access(m, selector, nil, false, false)
|
|
||||||
return &Value{data: rawObj}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the value using the specified selector and
|
|
||||||
// returns the object on which Set was called.
|
|
||||||
//
|
|
||||||
// Set can only operate directly on map[string]interface{} and []interface
|
|
||||||
//
|
|
||||||
// Example
|
|
||||||
//
|
|
||||||
// To set the title of the third chapter of the second book, do:
|
|
||||||
//
|
|
||||||
// o.Set("books[1].chapters[2].title","Time to Go")
|
|
||||||
func (m Map) Set(selector string, value interface{}) Map {
|
|
||||||
access(m, selector, value, true, false)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// access accesses the object using the selector and performs the
|
|
||||||
// appropriate action.
|
|
||||||
func access(current, selector, value interface{}, isSet, panics bool) interface{} {
|
|
||||||
|
|
||||||
switch selector.(type) {
|
|
||||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
|
||||||
|
|
||||||
if array, ok := current.([]interface{}); ok {
|
|
||||||
index := intFromInterface(selector)
|
|
||||||
|
|
||||||
if index >= len(array) {
|
|
||||||
if panics {
|
|
||||||
panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return array[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
case string:
|
|
||||||
|
|
||||||
selStr := selector.(string)
|
|
||||||
selSegs := strings.SplitN(selStr, PathSeparator, 2)
|
|
||||||
thisSel := selSegs[0]
|
|
||||||
index := -1
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// https://github.com/stretchr/objx/issues/12
|
|
||||||
if strings.Contains(thisSel, "[") {
|
|
||||||
|
|
||||||
arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel)
|
|
||||||
|
|
||||||
if len(arrayMatches) > 0 {
|
|
||||||
|
|
||||||
// Get the key into the map
|
|
||||||
thisSel = arrayMatches[1]
|
|
||||||
|
|
||||||
// Get the index into the array at the key
|
|
||||||
index, err = strconv.Atoi(arrayMatches[2])
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// This should never happen. If it does, something has gone
|
|
||||||
// seriously wrong. Panic.
|
|
||||||
panic("objx: Array index is not an integer. Must use array[int].")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if curMap, ok := current.(Map); ok {
|
|
||||||
current = map[string]interface{}(curMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the object in question
|
|
||||||
switch current.(type) {
|
|
||||||
case map[string]interface{}:
|
|
||||||
curMSI := current.(map[string]interface{})
|
|
||||||
if len(selSegs) <= 1 && isSet {
|
|
||||||
curMSI[thisSel] = value
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
current = curMSI[thisSel]
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
current = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if current == nil && panics {
|
|
||||||
panic(fmt.Sprintf("objx: '%v' invalid on object.", selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// do we need to access the item of an array?
|
|
||||||
if index > -1 {
|
|
||||||
if array, ok := current.([]interface{}); ok {
|
|
||||||
if index < len(array) {
|
|
||||||
current = array[index]
|
|
||||||
} else {
|
|
||||||
if panics {
|
|
||||||
panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
|
|
||||||
}
|
|
||||||
current = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(selSegs) > 1 {
|
|
||||||
current = access(current, selSegs[1], value, isSet, panics)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return current
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// intFromInterface converts an interface object to the largest
|
|
||||||
// representation of an unsigned integer using a type switch and
|
|
||||||
// assertions
|
|
||||||
func intFromInterface(selector interface{}) int {
|
|
||||||
var value int
|
|
||||||
switch selector.(type) {
|
|
||||||
case int:
|
|
||||||
value = selector.(int)
|
|
||||||
case int8:
|
|
||||||
value = int(selector.(int8))
|
|
||||||
case int16:
|
|
||||||
value = int(selector.(int16))
|
|
||||||
case int32:
|
|
||||||
value = int(selector.(int32))
|
|
||||||
case int64:
|
|
||||||
value = int(selector.(int64))
|
|
||||||
case uint:
|
|
||||||
value = int(selector.(uint))
|
|
||||||
case uint8:
|
|
||||||
value = int(selector.(uint8))
|
|
||||||
case uint16:
|
|
||||||
value = int(selector.(uint16))
|
|
||||||
case uint32:
|
|
||||||
value = int(selector.(uint32))
|
|
||||||
case uint64:
|
|
||||||
value = int(selector.(uint64))
|
|
||||||
default:
|
|
||||||
panic("objx: array access argument is not an integer type (this should never happen)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
145
Godeps/_workspace/src/github.com/stretchr/objx/accessors_test.go
generated
vendored
145
Godeps/_workspace/src/github.com/stretchr/objx/accessors_test.go
generated
vendored
@ -1,145 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAccessorsAccessGetSingleField(t *testing.T) {
|
|
||||||
|
|
||||||
current := map[string]interface{}{"name": "Tyler"}
|
|
||||||
assert.Equal(t, "Tyler", access(current, "name", nil, false, true))
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestAccessorsAccessGetDeep(t *testing.T) {
|
|
||||||
|
|
||||||
current := map[string]interface{}{"name": map[string]interface{}{"first": "Tyler", "last": "Bunnell"}}
|
|
||||||
assert.Equal(t, "Tyler", access(current, "name.first", nil, false, true))
|
|
||||||
assert.Equal(t, "Bunnell", access(current, "name.last", nil, false, true))
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestAccessorsAccessGetDeepDeep(t *testing.T) {
|
|
||||||
|
|
||||||
current := map[string]interface{}{"one": map[string]interface{}{"two": map[string]interface{}{"three": map[string]interface{}{"four": 4}}}}
|
|
||||||
assert.Equal(t, 4, access(current, "one.two.three.four", nil, false, true))
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestAccessorsAccessGetInsideArray(t *testing.T) {
|
|
||||||
|
|
||||||
current := map[string]interface{}{"names": []interface{}{map[string]interface{}{"first": "Tyler", "last": "Bunnell"}, map[string]interface{}{"first": "Capitol", "last": "Bollocks"}}}
|
|
||||||
assert.Equal(t, "Tyler", access(current, "names[0].first", nil, false, true))
|
|
||||||
assert.Equal(t, "Bunnell", access(current, "names[0].last", nil, false, true))
|
|
||||||
assert.Equal(t, "Capitol", access(current, "names[1].first", nil, false, true))
|
|
||||||
assert.Equal(t, "Bollocks", access(current, "names[1].last", nil, false, true))
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
access(current, "names[2]", nil, false, true)
|
|
||||||
})
|
|
||||||
assert.Nil(t, access(current, "names[2]", nil, false, false))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccessorsAccessGetFromArrayWithInt(t *testing.T) {
|
|
||||||
|
|
||||||
current := []interface{}{map[string]interface{}{"first": "Tyler", "last": "Bunnell"}, map[string]interface{}{"first": "Capitol", "last": "Bollocks"}}
|
|
||||||
one := access(current, 0, nil, false, false)
|
|
||||||
two := access(current, 1, nil, false, false)
|
|
||||||
three := access(current, 2, nil, false, false)
|
|
||||||
|
|
||||||
assert.Equal(t, "Tyler", one.(map[string]interface{})["first"])
|
|
||||||
assert.Equal(t, "Capitol", two.(map[string]interface{})["first"])
|
|
||||||
assert.Nil(t, three)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccessorsGet(t *testing.T) {
|
|
||||||
|
|
||||||
current := New(map[string]interface{}{"name": "Tyler"})
|
|
||||||
assert.Equal(t, "Tyler", current.Get("name").data)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccessorsAccessSetSingleField(t *testing.T) {
|
|
||||||
|
|
||||||
current := map[string]interface{}{"name": "Tyler"}
|
|
||||||
access(current, "name", "Mat", true, false)
|
|
||||||
assert.Equal(t, current["name"], "Mat")
|
|
||||||
|
|
||||||
access(current, "age", 29, true, true)
|
|
||||||
assert.Equal(t, current["age"], 29)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccessorsAccessSetSingleFieldNotExisting(t *testing.T) {
|
|
||||||
|
|
||||||
current := map[string]interface{}{}
|
|
||||||
access(current, "name", "Mat", true, false)
|
|
||||||
assert.Equal(t, current["name"], "Mat")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccessorsAccessSetDeep(t *testing.T) {
|
|
||||||
|
|
||||||
current := map[string]interface{}{"name": map[string]interface{}{"first": "Tyler", "last": "Bunnell"}}
|
|
||||||
|
|
||||||
access(current, "name.first", "Mat", true, true)
|
|
||||||
access(current, "name.last", "Ryer", true, true)
|
|
||||||
|
|
||||||
assert.Equal(t, "Mat", access(current, "name.first", nil, false, true))
|
|
||||||
assert.Equal(t, "Ryer", access(current, "name.last", nil, false, true))
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestAccessorsAccessSetDeepDeep(t *testing.T) {
|
|
||||||
|
|
||||||
current := map[string]interface{}{"one": map[string]interface{}{"two": map[string]interface{}{"three": map[string]interface{}{"four": 4}}}}
|
|
||||||
|
|
||||||
access(current, "one.two.three.four", 5, true, true)
|
|
||||||
|
|
||||||
assert.Equal(t, 5, access(current, "one.two.three.four", nil, false, true))
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestAccessorsAccessSetArray(t *testing.T) {
|
|
||||||
|
|
||||||
current := map[string]interface{}{"names": []interface{}{"Tyler"}}
|
|
||||||
|
|
||||||
access(current, "names[0]", "Mat", true, true)
|
|
||||||
|
|
||||||
assert.Equal(t, "Mat", access(current, "names[0]", nil, false, true))
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestAccessorsAccessSetInsideArray(t *testing.T) {
|
|
||||||
|
|
||||||
current := map[string]interface{}{"names": []interface{}{map[string]interface{}{"first": "Tyler", "last": "Bunnell"}, map[string]interface{}{"first": "Capitol", "last": "Bollocks"}}}
|
|
||||||
|
|
||||||
access(current, "names[0].first", "Mat", true, true)
|
|
||||||
access(current, "names[0].last", "Ryer", true, true)
|
|
||||||
access(current, "names[1].first", "Captain", true, true)
|
|
||||||
access(current, "names[1].last", "Underpants", true, true)
|
|
||||||
|
|
||||||
assert.Equal(t, "Mat", access(current, "names[0].first", nil, false, true))
|
|
||||||
assert.Equal(t, "Ryer", access(current, "names[0].last", nil, false, true))
|
|
||||||
assert.Equal(t, "Captain", access(current, "names[1].first", nil, false, true))
|
|
||||||
assert.Equal(t, "Underpants", access(current, "names[1].last", nil, false, true))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccessorsAccessSetFromArrayWithInt(t *testing.T) {
|
|
||||||
|
|
||||||
current := []interface{}{map[string]interface{}{"first": "Tyler", "last": "Bunnell"}, map[string]interface{}{"first": "Capitol", "last": "Bollocks"}}
|
|
||||||
one := access(current, 0, nil, false, false)
|
|
||||||
two := access(current, 1, nil, false, false)
|
|
||||||
three := access(current, 2, nil, false, false)
|
|
||||||
|
|
||||||
assert.Equal(t, "Tyler", one.(map[string]interface{})["first"])
|
|
||||||
assert.Equal(t, "Capitol", two.(map[string]interface{})["first"])
|
|
||||||
assert.Nil(t, three)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccessorsSet(t *testing.T) {
|
|
||||||
|
|
||||||
current := New(map[string]interface{}{"name": "Tyler"})
|
|
||||||
current.Set("name", "Mat")
|
|
||||||
assert.Equal(t, "Mat", current.Get("name").data)
|
|
||||||
|
|
||||||
}
|
|
14
Godeps/_workspace/src/github.com/stretchr/objx/codegen/array-access.txt
generated
vendored
14
Godeps/_workspace/src/github.com/stretchr/objx/codegen/array-access.txt
generated
vendored
@ -1,14 +0,0 @@
|
|||||||
case []{1}:
|
|
||||||
a := object.([]{1})
|
|
||||||
if isSet {
|
|
||||||
a[index] = value.({1})
|
|
||||||
} else {
|
|
||||||
if index >= len(a) {
|
|
||||||
if panics {
|
|
||||||
panic(fmt.Sprintf("objx: Index %d is out of range because the []{1} only contains %d items.", index, len(a)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return a[index]
|
|
||||||
}
|
|
||||||
}
|
|
86
Godeps/_workspace/src/github.com/stretchr/objx/codegen/index.html
generated
vendored
86
Godeps/_workspace/src/github.com/stretchr/objx/codegen/index.html
generated
vendored
@ -1,86 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>
|
|
||||||
Codegen
|
|
||||||
</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
width: 800px;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
textarea {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100px;
|
|
||||||
font-family: Courier;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<h2>
|
|
||||||
Template
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Use <code>{x}</code> as a placeholder for each argument.
|
|
||||||
</p>
|
|
||||||
<textarea id="template"></textarea>
|
|
||||||
|
|
||||||
<h2>
|
|
||||||
Arguments (comma separated)
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
One block per line
|
|
||||||
</p>
|
|
||||||
<textarea id="args"></textarea>
|
|
||||||
|
|
||||||
<h2>
|
|
||||||
Output
|
|
||||||
</h2>
|
|
||||||
<input id="go" type="button" value="Generate code" />
|
|
||||||
|
|
||||||
<textarea id="output"></textarea>
|
|
||||||
|
|
||||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
$(function(){
|
|
||||||
|
|
||||||
$("#go").click(function(){
|
|
||||||
|
|
||||||
var output = ""
|
|
||||||
var template = $("#template").val()
|
|
||||||
var args = $("#args").val()
|
|
||||||
|
|
||||||
// collect the args
|
|
||||||
var argLines = args.split("\n")
|
|
||||||
for (var line in argLines) {
|
|
||||||
|
|
||||||
var argLine = argLines[line];
|
|
||||||
var thisTemp = template
|
|
||||||
|
|
||||||
// get individual args
|
|
||||||
var args = argLine.split(",")
|
|
||||||
|
|
||||||
for (var argI in args) {
|
|
||||||
var argText = args[argI];
|
|
||||||
var argPlaceholder = "{" + argI + "}";
|
|
||||||
|
|
||||||
while (thisTemp.indexOf(argPlaceholder) > -1) {
|
|
||||||
thisTemp = thisTemp.replace(argPlaceholder, argText);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
output += thisTemp
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#output").val(output);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
286
Godeps/_workspace/src/github.com/stretchr/objx/codegen/template.txt
generated
vendored
286
Godeps/_workspace/src/github.com/stretchr/objx/codegen/template.txt
generated
vendored
@ -1,286 +0,0 @@
|
|||||||
/*
|
|
||||||
{4} ({1} and []{1})
|
|
||||||
--------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
// {4} gets the value as a {1}, returns the optionalDefault
|
|
||||||
// value or a system default object if the value is the wrong type.
|
|
||||||
func (v *Value) {4}(optionalDefault ...{1}) {1} {
|
|
||||||
if s, ok := v.data.({1}); ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
if len(optionalDefault) == 1 {
|
|
||||||
return optionalDefault[0]
|
|
||||||
}
|
|
||||||
return {3}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must{4} gets the value as a {1}.
|
|
||||||
//
|
|
||||||
// Panics if the object is not a {1}.
|
|
||||||
func (v *Value) Must{4}() {1} {
|
|
||||||
return v.data.({1})
|
|
||||||
}
|
|
||||||
|
|
||||||
// {4}Slice gets the value as a []{1}, returns the optionalDefault
|
|
||||||
// value or nil if the value is not a []{1}.
|
|
||||||
func (v *Value) {4}Slice(optionalDefault ...[]{1}) []{1} {
|
|
||||||
if s, ok := v.data.([]{1}); ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
if len(optionalDefault) == 1 {
|
|
||||||
return optionalDefault[0]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must{4}Slice gets the value as a []{1}.
|
|
||||||
//
|
|
||||||
// Panics if the object is not a []{1}.
|
|
||||||
func (v *Value) Must{4}Slice() []{1} {
|
|
||||||
return v.data.([]{1})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is{4} gets whether the object contained is a {1} or not.
|
|
||||||
func (v *Value) Is{4}() bool {
|
|
||||||
_, ok := v.data.({1})
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is{4}Slice gets whether the object contained is a []{1} or not.
|
|
||||||
func (v *Value) Is{4}Slice() bool {
|
|
||||||
_, ok := v.data.([]{1})
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Each{4} calls the specified callback for each object
|
|
||||||
// in the []{1}.
|
|
||||||
//
|
|
||||||
// Panics if the object is the wrong type.
|
|
||||||
func (v *Value) Each{4}(callback func(int, {1}) bool) *Value {
|
|
||||||
|
|
||||||
for index, val := range v.Must{4}Slice() {
|
|
||||||
carryon := callback(index, val)
|
|
||||||
if carryon == false {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Where{4} uses the specified decider function to select items
|
|
||||||
// from the []{1}. The object contained in the result will contain
|
|
||||||
// only the selected items.
|
|
||||||
func (v *Value) Where{4}(decider func(int, {1}) bool) *Value {
|
|
||||||
|
|
||||||
var selected []{1}
|
|
||||||
|
|
||||||
v.Each{4}(func(index int, val {1}) bool {
|
|
||||||
shouldSelect := decider(index, val)
|
|
||||||
if shouldSelect == false {
|
|
||||||
selected = append(selected, val)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return &Value{data:selected}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group{4} uses the specified grouper function to group the items
|
|
||||||
// keyed by the return of the grouper. The object contained in the
|
|
||||||
// result will contain a map[string][]{1}.
|
|
||||||
func (v *Value) Group{4}(grouper func(int, {1}) string) *Value {
|
|
||||||
|
|
||||||
groups := make(map[string][]{1})
|
|
||||||
|
|
||||||
v.Each{4}(func(index int, val {1}) bool {
|
|
||||||
group := grouper(index, val)
|
|
||||||
if _, ok := groups[group]; !ok {
|
|
||||||
groups[group] = make([]{1}, 0)
|
|
||||||
}
|
|
||||||
groups[group] = append(groups[group], val)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return &Value{data:groups}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace{4} uses the specified function to replace each {1}s
|
|
||||||
// by iterating each item. The data in the returned result will be a
|
|
||||||
// []{1} containing the replaced items.
|
|
||||||
func (v *Value) Replace{4}(replacer func(int, {1}) {1}) *Value {
|
|
||||||
|
|
||||||
arr := v.Must{4}Slice()
|
|
||||||
replaced := make([]{1}, len(arr))
|
|
||||||
|
|
||||||
v.Each{4}(func(index int, val {1}) bool {
|
|
||||||
replaced[index] = replacer(index, val)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return &Value{data:replaced}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect{4} uses the specified collector function to collect a value
|
|
||||||
// for each of the {1}s in the slice. The data returned will be a
|
|
||||||
// []interface{}.
|
|
||||||
func (v *Value) Collect{4}(collector func(int, {1}) interface{}) *Value {
|
|
||||||
|
|
||||||
arr := v.Must{4}Slice()
|
|
||||||
collected := make([]interface{}, len(arr))
|
|
||||||
|
|
||||||
v.Each{4}(func(index int, val {1}) bool {
|
|
||||||
collected[index] = collector(index, val)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return &Value{data:collected}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ************************************************************
|
|
||||||
// TESTS
|
|
||||||
// ************************************************************
|
|
||||||
|
|
||||||
func Test{4}(t *testing.T) {
|
|
||||||
|
|
||||||
val := {1}( {2} )
|
|
||||||
m := map[string]interface{}{"value": val, "nothing": nil}
|
|
||||||
assert.Equal(t, val, New(m).Get("value").{4}())
|
|
||||||
assert.Equal(t, val, New(m).Get("value").Must{4}())
|
|
||||||
assert.Equal(t, {1}({3}), New(m).Get("nothing").{4}())
|
|
||||||
assert.Equal(t, val, New(m).Get("nothing").{4}({2}))
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
New(m).Get("age").Must{4}()
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test{4}Slice(t *testing.T) {
|
|
||||||
|
|
||||||
val := {1}( {2} )
|
|
||||||
m := map[string]interface{}{"value": []{1}{ val }, "nothing": nil}
|
|
||||||
assert.Equal(t, val, New(m).Get("value").{4}Slice()[0])
|
|
||||||
assert.Equal(t, val, New(m).Get("value").Must{4}Slice()[0])
|
|
||||||
assert.Equal(t, []{1}(nil), New(m).Get("nothing").{4}Slice())
|
|
||||||
assert.Equal(t, val, New(m).Get("nothing").{4}Slice( []{1}{ {1}({2}) } )[0])
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
New(m).Get("nothing").Must{4}Slice()
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIs{4}(t *testing.T) {
|
|
||||||
|
|
||||||
var v *Value
|
|
||||||
|
|
||||||
v = &Value{data: {1}({2})}
|
|
||||||
assert.True(t, v.Is{4}())
|
|
||||||
|
|
||||||
v = &Value{data: []{1}{ {1}({2}) }}
|
|
||||||
assert.True(t, v.Is{4}Slice())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEach{4}(t *testing.T) {
|
|
||||||
|
|
||||||
v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
|
|
||||||
count := 0
|
|
||||||
replacedVals := make([]{1}, 0)
|
|
||||||
assert.Equal(t, v, v.Each{4}(func(i int, val {1}) bool {
|
|
||||||
|
|
||||||
count++
|
|
||||||
replacedVals = append(replacedVals, val)
|
|
||||||
|
|
||||||
// abort early
|
|
||||||
if i == 2 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
}))
|
|
||||||
|
|
||||||
assert.Equal(t, count, 3)
|
|
||||||
assert.Equal(t, replacedVals[0], v.Must{4}Slice()[0])
|
|
||||||
assert.Equal(t, replacedVals[1], v.Must{4}Slice()[1])
|
|
||||||
assert.Equal(t, replacedVals[2], v.Must{4}Slice()[2])
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWhere{4}(t *testing.T) {
|
|
||||||
|
|
||||||
v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
|
|
||||||
|
|
||||||
selected := v.Where{4}(func(i int, val {1}) bool {
|
|
||||||
return i%2==0
|
|
||||||
}).Must{4}Slice()
|
|
||||||
|
|
||||||
assert.Equal(t, 3, len(selected))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGroup{4}(t *testing.T) {
|
|
||||||
|
|
||||||
v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
|
|
||||||
|
|
||||||
grouped := v.Group{4}(func(i int, val {1}) string {
|
|
||||||
return fmt.Sprintf("%v", i%2==0)
|
|
||||||
}).data.(map[string][]{1})
|
|
||||||
|
|
||||||
assert.Equal(t, 2, len(grouped))
|
|
||||||
assert.Equal(t, 3, len(grouped["true"]))
|
|
||||||
assert.Equal(t, 3, len(grouped["false"]))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace{4}(t *testing.T) {
|
|
||||||
|
|
||||||
v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
|
|
||||||
|
|
||||||
rawArr := v.Must{4}Slice()
|
|
||||||
|
|
||||||
replaced := v.Replace{4}(func(index int, val {1}) {1} {
|
|
||||||
if index < len(rawArr)-1 {
|
|
||||||
return rawArr[index+1]
|
|
||||||
}
|
|
||||||
return rawArr[0]
|
|
||||||
})
|
|
||||||
|
|
||||||
replacedArr := replaced.Must{4}Slice()
|
|
||||||
if assert.Equal(t, 6, len(replacedArr)) {
|
|
||||||
assert.Equal(t, replacedArr[0], rawArr[1])
|
|
||||||
assert.Equal(t, replacedArr[1], rawArr[2])
|
|
||||||
assert.Equal(t, replacedArr[2], rawArr[3])
|
|
||||||
assert.Equal(t, replacedArr[3], rawArr[4])
|
|
||||||
assert.Equal(t, replacedArr[4], rawArr[5])
|
|
||||||
assert.Equal(t, replacedArr[5], rawArr[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCollect{4}(t *testing.T) {
|
|
||||||
|
|
||||||
v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
|
|
||||||
|
|
||||||
collected := v.Collect{4}(func(index int, val {1}) interface{} {
|
|
||||||
return index
|
|
||||||
})
|
|
||||||
|
|
||||||
collectedArr := collected.MustInterSlice()
|
|
||||||
if assert.Equal(t, 6, len(collectedArr)) {
|
|
||||||
assert.Equal(t, collectedArr[0], 0)
|
|
||||||
assert.Equal(t, collectedArr[1], 1)
|
|
||||||
assert.Equal(t, collectedArr[2], 2)
|
|
||||||
assert.Equal(t, collectedArr[3], 3)
|
|
||||||
assert.Equal(t, collectedArr[4], 4)
|
|
||||||
assert.Equal(t, collectedArr[5], 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
20
Godeps/_workspace/src/github.com/stretchr/objx/codegen/types_list.txt
generated
vendored
20
Godeps/_workspace/src/github.com/stretchr/objx/codegen/types_list.txt
generated
vendored
@ -1,20 +0,0 @@
|
|||||||
Interface,interface{},"something",nil,Inter
|
|
||||||
Map,map[string]interface{},map[string]interface{}{"name":"Tyler"},nil,MSI
|
|
||||||
ObjxMap,(Map),New(1),New(nil),ObjxMap
|
|
||||||
Bool,bool,true,false,Bool
|
|
||||||
String,string,"hello","",Str
|
|
||||||
Int,int,1,0,Int
|
|
||||||
Int8,int8,1,0,Int8
|
|
||||||
Int16,int16,1,0,Int16
|
|
||||||
Int32,int32,1,0,Int32
|
|
||||||
Int64,int64,1,0,Int64
|
|
||||||
Uint,uint,1,0,Uint
|
|
||||||
Uint8,uint8,1,0,Uint8
|
|
||||||
Uint16,uint16,1,0,Uint16
|
|
||||||
Uint32,uint32,1,0,Uint32
|
|
||||||
Uint64,uint64,1,0,Uint64
|
|
||||||
Uintptr,uintptr,1,0,Uintptr
|
|
||||||
Float32,float32,1,0,Float32
|
|
||||||
Float64,float64,1,0,Float64
|
|
||||||
Complex64,complex64,1,0,Complex64
|
|
||||||
Complex128,complex128,1,0,Complex128
|
|
13
Godeps/_workspace/src/github.com/stretchr/objx/constants.go
generated
vendored
13
Godeps/_workspace/src/github.com/stretchr/objx/constants.go
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
const (
|
|
||||||
// PathSeparator is the character used to separate the elements
|
|
||||||
// of the keypath.
|
|
||||||
//
|
|
||||||
// For example, `location.address.city`
|
|
||||||
PathSeparator string = "."
|
|
||||||
|
|
||||||
// SignatureSeparator is the character that is used to
|
|
||||||
// separate the Base64 string from the security signature.
|
|
||||||
SignatureSeparator = "_"
|
|
||||||
)
|
|
117
Godeps/_workspace/src/github.com/stretchr/objx/conversions.go
generated
vendored
117
Godeps/_workspace/src/github.com/stretchr/objx/conversions.go
generated
vendored
@ -1,117 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
// JSON converts the contained object to a JSON string
|
|
||||||
// representation
|
|
||||||
func (m Map) JSON() (string, error) {
|
|
||||||
|
|
||||||
result, err := json.Marshal(m)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err = errors.New("objx: JSON encode failed with: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(result), err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustJSON converts the contained object to a JSON string
|
|
||||||
// representation and panics if there is an error
|
|
||||||
func (m Map) MustJSON() string {
|
|
||||||
result, err := m.JSON()
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base64 converts the contained object to a Base64 string
|
|
||||||
// representation of the JSON string representation
|
|
||||||
func (m Map) Base64() (string, error) {
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
jsonData, err := m.JSON()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder := base64.NewEncoder(base64.StdEncoding, &buf)
|
|
||||||
encoder.Write([]byte(jsonData))
|
|
||||||
encoder.Close()
|
|
||||||
|
|
||||||
return buf.String(), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustBase64 converts the contained object to a Base64 string
|
|
||||||
// representation of the JSON string representation and panics
|
|
||||||
// if there is an error
|
|
||||||
func (m Map) MustBase64() string {
|
|
||||||
result, err := m.Base64()
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignedBase64 converts the contained object to a Base64 string
|
|
||||||
// representation of the JSON string representation and signs it
|
|
||||||
// using the provided key.
|
|
||||||
func (m Map) SignedBase64(key string) (string, error) {
|
|
||||||
|
|
||||||
base64, err := m.Base64()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
sig := HashWithKey(base64, key)
|
|
||||||
|
|
||||||
return base64 + SignatureSeparator + sig, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustSignedBase64 converts the contained object to a Base64 string
|
|
||||||
// representation of the JSON string representation and signs it
|
|
||||||
// using the provided key and panics if there is an error
|
|
||||||
func (m Map) MustSignedBase64(key string) string {
|
|
||||||
result, err := m.SignedBase64(key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
URL Query
|
|
||||||
------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
// URLValues creates a url.Values object from an Obj. This
|
|
||||||
// function requires that the wrapped object be a map[string]interface{}
|
|
||||||
func (m Map) URLValues() url.Values {
|
|
||||||
|
|
||||||
vals := make(url.Values)
|
|
||||||
|
|
||||||
for k, v := range m {
|
|
||||||
//TODO: can this be done without sprintf?
|
|
||||||
vals.Set(k, fmt.Sprintf("%v", v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return vals
|
|
||||||
}
|
|
||||||
|
|
||||||
// URLQuery gets an encoded URL query representing the given
|
|
||||||
// Obj. This function requires that the wrapped object be a
|
|
||||||
// map[string]interface{}
|
|
||||||
func (m Map) URLQuery() (string, error) {
|
|
||||||
return m.URLValues().Encode(), nil
|
|
||||||
}
|
|
94
Godeps/_workspace/src/github.com/stretchr/objx/conversions_test.go
generated
vendored
94
Godeps/_workspace/src/github.com/stretchr/objx/conversions_test.go
generated
vendored
@ -1,94 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestConversionJSON(t *testing.T) {
|
|
||||||
|
|
||||||
jsonString := `{"name":"Mat"}`
|
|
||||||
o := MustFromJSON(jsonString)
|
|
||||||
|
|
||||||
result, err := o.JSON()
|
|
||||||
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
assert.Equal(t, jsonString, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, jsonString, o.MustJSON())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConversionJSONWithError(t *testing.T) {
|
|
||||||
|
|
||||||
o := MSI()
|
|
||||||
o["test"] = func() {}
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
o.MustJSON()
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := o.JSON()
|
|
||||||
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConversionBase64(t *testing.T) {
|
|
||||||
|
|
||||||
o := New(map[string]interface{}{"name": "Mat"})
|
|
||||||
|
|
||||||
result, err := o.Base64()
|
|
||||||
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
assert.Equal(t, "eyJuYW1lIjoiTWF0In0=", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, "eyJuYW1lIjoiTWF0In0=", o.MustBase64())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConversionBase64WithError(t *testing.T) {
|
|
||||||
|
|
||||||
o := MSI()
|
|
||||||
o["test"] = func() {}
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
o.MustBase64()
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := o.Base64()
|
|
||||||
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConversionSignedBase64(t *testing.T) {
|
|
||||||
|
|
||||||
o := New(map[string]interface{}{"name": "Mat"})
|
|
||||||
|
|
||||||
result, err := o.SignedBase64("key")
|
|
||||||
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
assert.Equal(t, "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6", o.MustSignedBase64("key"))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConversionSignedBase64WithError(t *testing.T) {
|
|
||||||
|
|
||||||
o := MSI()
|
|
||||||
o["test"] = func() {}
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
o.MustSignedBase64("key")
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := o.SignedBase64("key")
|
|
||||||
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
}
|
|
72
Godeps/_workspace/src/github.com/stretchr/objx/doc.go
generated
vendored
72
Godeps/_workspace/src/github.com/stretchr/objx/doc.go
generated
vendored
@ -1,72 +0,0 @@
|
|||||||
// objx - Go package for dealing with maps, slices, JSON and other data.
|
|
||||||
//
|
|
||||||
// Overview
|
|
||||||
//
|
|
||||||
// Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes
|
|
||||||
// a powerful `Get` method (among others) that allows you to easily and quickly get
|
|
||||||
// access to data within the map, without having to worry too much about type assertions,
|
|
||||||
// missing data, default values etc.
|
|
||||||
//
|
|
||||||
// Pattern
|
|
||||||
//
|
|
||||||
// Objx uses a preditable pattern to make access data from within `map[string]interface{}'s
|
|
||||||
// easy.
|
|
||||||
//
|
|
||||||
// Call one of the `objx.` functions to create your `objx.Map` to get going:
|
|
||||||
//
|
|
||||||
// m, err := objx.FromJSON(json)
|
|
||||||
//
|
|
||||||
// NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong,
|
|
||||||
// the rest will be optimistic and try to figure things out without panicking.
|
|
||||||
//
|
|
||||||
// Use `Get` to access the value you're interested in. You can use dot and array
|
|
||||||
// notation too:
|
|
||||||
//
|
|
||||||
// m.Get("places[0].latlng")
|
|
||||||
//
|
|
||||||
// Once you have saught the `Value` you're interested in, you can use the `Is*` methods
|
|
||||||
// to determine its type.
|
|
||||||
//
|
|
||||||
// if m.Get("code").IsStr() { /* ... */ }
|
|
||||||
//
|
|
||||||
// Or you can just assume the type, and use one of the strong type methods to
|
|
||||||
// extract the real value:
|
|
||||||
//
|
|
||||||
// m.Get("code").Int()
|
|
||||||
//
|
|
||||||
// If there's no value there (or if it's the wrong type) then a default value
|
|
||||||
// will be returned, or you can be explicit about the default value.
|
|
||||||
//
|
|
||||||
// Get("code").Int(-1)
|
|
||||||
//
|
|
||||||
// If you're dealing with a slice of data as a value, Objx provides many useful
|
|
||||||
// methods for iterating, manipulating and selecting that data. You can find out more
|
|
||||||
// by exploring the index below.
|
|
||||||
//
|
|
||||||
// Reading data
|
|
||||||
//
|
|
||||||
// A simple example of how to use Objx:
|
|
||||||
//
|
|
||||||
// // use MustFromJSON to make an objx.Map from some JSON
|
|
||||||
// m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
|
|
||||||
//
|
|
||||||
// // get the details
|
|
||||||
// name := m.Get("name").Str()
|
|
||||||
// age := m.Get("age").Int()
|
|
||||||
//
|
|
||||||
// // get their nickname (or use their name if they
|
|
||||||
// // don't have one)
|
|
||||||
// nickname := m.Get("nickname").Str(name)
|
|
||||||
//
|
|
||||||
// Ranging
|
|
||||||
//
|
|
||||||
// Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For
|
|
||||||
// example, to `range` the data, do what you would expect:
|
|
||||||
//
|
|
||||||
// m := objx.MustFromJSON(json)
|
|
||||||
// for key, value := range m {
|
|
||||||
//
|
|
||||||
// /* ... do your magic ... */
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
package objx
|
|
98
Godeps/_workspace/src/github.com/stretchr/objx/fixture_test.go
generated
vendored
98
Godeps/_workspace/src/github.com/stretchr/objx/fixture_test.go
generated
vendored
@ -1,98 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var fixtures = []struct {
|
|
||||||
// name is the name of the fixture (used for reporting
|
|
||||||
// failures)
|
|
||||||
name string
|
|
||||||
// data is the JSON data to be worked on
|
|
||||||
data string
|
|
||||||
// get is the argument(s) to pass to Get
|
|
||||||
get interface{}
|
|
||||||
// output is the expected output
|
|
||||||
output interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Simple get",
|
|
||||||
data: `{"name": "Mat"}`,
|
|
||||||
get: "name",
|
|
||||||
output: "Mat",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Get with dot notation",
|
|
||||||
data: `{"address": {"city": "Boulder"}}`,
|
|
||||||
get: "address.city",
|
|
||||||
output: "Boulder",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Deep get with dot notation",
|
|
||||||
data: `{"one": {"two": {"three": {"four": "hello"}}}}`,
|
|
||||||
get: "one.two.three.four",
|
|
||||||
output: "hello",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Get missing with dot notation",
|
|
||||||
data: `{"one": {"two": {"three": {"four": "hello"}}}}`,
|
|
||||||
get: "one.ten",
|
|
||||||
output: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Get with array notation",
|
|
||||||
data: `{"tags": ["one", "two", "three"]}`,
|
|
||||||
get: "tags[1]",
|
|
||||||
output: "two",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Get with array and dot notation",
|
|
||||||
data: `{"types": { "tags": ["one", "two", "three"]}}`,
|
|
||||||
get: "types.tags[1]",
|
|
||||||
output: "two",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Get with array and dot notation - field after array",
|
|
||||||
data: `{"tags": [{"name":"one"}, {"name":"two"}, {"name":"three"}]}`,
|
|
||||||
get: "tags[1].name",
|
|
||||||
output: "two",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Complex get with array and dot notation",
|
|
||||||
data: `{"tags": [{"list": [{"one":"pizza"}]}]}`,
|
|
||||||
get: "tags[0].list[0].one",
|
|
||||||
output: "pizza",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Get field from within string should be nil",
|
|
||||||
data: `{"name":"Tyler"}`,
|
|
||||||
get: "name.something",
|
|
||||||
output: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Get field from within string (using array accessor) should be nil",
|
|
||||||
data: `{"numbers":["one", "two", "three"]}`,
|
|
||||||
get: "numbers[0].nope",
|
|
||||||
output: nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFixtures(t *testing.T) {
|
|
||||||
|
|
||||||
for _, fixture := range fixtures {
|
|
||||||
|
|
||||||
m := MustFromJSON(fixture.data)
|
|
||||||
|
|
||||||
// get the value
|
|
||||||
t.Logf("Running get fixture: \"%s\" (%v)", fixture.name, fixture)
|
|
||||||
value := m.Get(fixture.get.(string))
|
|
||||||
|
|
||||||
// make sure it matches
|
|
||||||
assert.Equal(t, fixture.output, value.data,
|
|
||||||
"Get fixture \"%s\" failed: %v", fixture.name, fixture,
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
222
Godeps/_workspace/src/github.com/stretchr/objx/map.go
generated
vendored
222
Godeps/_workspace/src/github.com/stretchr/objx/map.go
generated
vendored
@ -1,222 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MSIConvertable is an interface that defines methods for converting your
|
|
||||||
// custom types to a map[string]interface{} representation.
|
|
||||||
type MSIConvertable interface {
|
|
||||||
// MSI gets a map[string]interface{} (msi) representing the
|
|
||||||
// object.
|
|
||||||
MSI() map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map provides extended functionality for working with
|
|
||||||
// untyped data, in particular map[string]interface (msi).
|
|
||||||
type Map map[string]interface{}
|
|
||||||
|
|
||||||
// Value returns the internal value instance
|
|
||||||
func (m Map) Value() *Value {
|
|
||||||
return &Value{data: m}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nil represents a nil Map.
|
|
||||||
var Nil Map = New(nil)
|
|
||||||
|
|
||||||
// New creates a new Map containing the map[string]interface{} in the data argument.
|
|
||||||
// If the data argument is not a map[string]interface, New attempts to call the
|
|
||||||
// MSI() method on the MSIConvertable interface to create one.
|
|
||||||
func New(data interface{}) Map {
|
|
||||||
if _, ok := data.(map[string]interface{}); !ok {
|
|
||||||
if converter, ok := data.(MSIConvertable); ok {
|
|
||||||
data = converter.MSI()
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Map(data.(map[string]interface{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MSI creates a map[string]interface{} and puts it inside a new Map.
|
|
||||||
//
|
|
||||||
// The arguments follow a key, value pattern.
|
|
||||||
//
|
|
||||||
// Panics
|
|
||||||
//
|
|
||||||
// Panics if any key arugment is non-string or if there are an odd number of arguments.
|
|
||||||
//
|
|
||||||
// Example
|
|
||||||
//
|
|
||||||
// To easily create Maps:
|
|
||||||
//
|
|
||||||
// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
|
|
||||||
//
|
|
||||||
// // creates an Map equivalent to
|
|
||||||
// m := objx.New(map[string]interface{}{"name": "Mat", "age": 29, "subobj": map[string]interface{}{"active": true}})
|
|
||||||
func MSI(keyAndValuePairs ...interface{}) Map {
|
|
||||||
|
|
||||||
newMap := make(map[string]interface{})
|
|
||||||
keyAndValuePairsLen := len(keyAndValuePairs)
|
|
||||||
|
|
||||||
if keyAndValuePairsLen%2 != 0 {
|
|
||||||
panic("objx: MSI must have an even number of arguments following the 'key, value' pattern.")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < keyAndValuePairsLen; i = i + 2 {
|
|
||||||
|
|
||||||
key := keyAndValuePairs[i]
|
|
||||||
value := keyAndValuePairs[i+1]
|
|
||||||
|
|
||||||
// make sure the key is a string
|
|
||||||
keyString, keyStringOK := key.(string)
|
|
||||||
if !keyStringOK {
|
|
||||||
panic("objx: MSI must follow 'string, interface{}' pattern. " + keyString + " is not a valid key.")
|
|
||||||
}
|
|
||||||
|
|
||||||
newMap[keyString] = value
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return New(newMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****** Conversion Constructors
|
|
||||||
|
|
||||||
// MustFromJSON creates a new Map containing the data specified in the
|
|
||||||
// jsonString.
|
|
||||||
//
|
|
||||||
// Panics if the JSON is invalid.
|
|
||||||
func MustFromJSON(jsonString string) Map {
|
|
||||||
o, err := FromJSON(jsonString)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic("objx: MustFromJSON failed with error: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromJSON creates a new Map containing the data specified in the
|
|
||||||
// jsonString.
|
|
||||||
//
|
|
||||||
// Returns an error if the JSON is invalid.
|
|
||||||
func FromJSON(jsonString string) (Map, error) {
|
|
||||||
|
|
||||||
var data interface{}
|
|
||||||
err := json.Unmarshal([]byte(jsonString), &data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return Nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return New(data), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromBase64 creates a new Obj containing the data specified
|
|
||||||
// in the Base64 string.
|
|
||||||
//
|
|
||||||
// The string is an encoded JSON string returned by Base64
|
|
||||||
func FromBase64(base64String string) (Map, error) {
|
|
||||||
|
|
||||||
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String))
|
|
||||||
|
|
||||||
decoded, err := ioutil.ReadAll(decoder)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return FromJSON(string(decoded))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustFromBase64 creates a new Obj containing the data specified
|
|
||||||
// in the Base64 string and panics if there is an error.
|
|
||||||
//
|
|
||||||
// The string is an encoded JSON string returned by Base64
|
|
||||||
func MustFromBase64(base64String string) Map {
|
|
||||||
|
|
||||||
result, err := FromBase64(base64String)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic("objx: MustFromBase64 failed with error: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromSignedBase64 creates a new Obj containing the data specified
|
|
||||||
// in the Base64 string.
|
|
||||||
//
|
|
||||||
// The string is an encoded JSON string returned by SignedBase64
|
|
||||||
func FromSignedBase64(base64String, key string) (Map, error) {
|
|
||||||
parts := strings.Split(base64String, SignatureSeparator)
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return nil, errors.New("objx: Signed base64 string is malformed.")
|
|
||||||
}
|
|
||||||
|
|
||||||
sig := HashWithKey(parts[0], key)
|
|
||||||
if parts[1] != sig {
|
|
||||||
return nil, errors.New("objx: Signature for base64 data does not match.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return FromBase64(parts[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustFromSignedBase64 creates a new Obj containing the data specified
|
|
||||||
// in the Base64 string and panics if there is an error.
|
|
||||||
//
|
|
||||||
// The string is an encoded JSON string returned by Base64
|
|
||||||
func MustFromSignedBase64(base64String, key string) Map {
|
|
||||||
|
|
||||||
result, err := FromSignedBase64(base64String, key)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic("objx: MustFromSignedBase64 failed with error: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromURLQuery generates a new Obj by parsing the specified
|
|
||||||
// query.
|
|
||||||
//
|
|
||||||
// For queries with multiple values, the first value is selected.
|
|
||||||
func FromURLQuery(query string) (Map, error) {
|
|
||||||
|
|
||||||
vals, err := url.ParseQuery(query)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
m := make(map[string]interface{})
|
|
||||||
for k, vals := range vals {
|
|
||||||
m[k] = vals[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return New(m), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustFromURLQuery generates a new Obj by parsing the specified
|
|
||||||
// query.
|
|
||||||
//
|
|
||||||
// For queries with multiple values, the first value is selected.
|
|
||||||
//
|
|
||||||
// Panics if it encounters an error
|
|
||||||
func MustFromURLQuery(query string) Map {
|
|
||||||
|
|
||||||
o, err := FromURLQuery(query)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic("objx: MustFromURLQuery failed with error: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return o
|
|
||||||
|
|
||||||
}
|
|
10
Godeps/_workspace/src/github.com/stretchr/objx/map_for_test.go
generated
vendored
10
Godeps/_workspace/src/github.com/stretchr/objx/map_for_test.go
generated
vendored
@ -1,10 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
var TestMap map[string]interface{} = map[string]interface{}{
|
|
||||||
"name": "Tyler",
|
|
||||||
"address": map[string]interface{}{
|
|
||||||
"city": "Salt Lake City",
|
|
||||||
"state": "UT",
|
|
||||||
},
|
|
||||||
"numbers": []interface{}{"one", "two", "three", "four", "five"},
|
|
||||||
}
|
|
147
Godeps/_workspace/src/github.com/stretchr/objx/map_test.go
generated
vendored
147
Godeps/_workspace/src/github.com/stretchr/objx/map_test.go
generated
vendored
@ -1,147 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Convertable struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Convertable) MSI() map[string]interface{} {
|
|
||||||
return map[string]interface{}{"name": c.name}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Unconvertable struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapCreation(t *testing.T) {
|
|
||||||
|
|
||||||
o := New(nil)
|
|
||||||
assert.Nil(t, o)
|
|
||||||
|
|
||||||
o = New("Tyler")
|
|
||||||
assert.Nil(t, o)
|
|
||||||
|
|
||||||
unconvertable := &Unconvertable{name: "Tyler"}
|
|
||||||
o = New(unconvertable)
|
|
||||||
assert.Nil(t, o)
|
|
||||||
|
|
||||||
convertable := &Convertable{name: "Tyler"}
|
|
||||||
o = New(convertable)
|
|
||||||
if assert.NotNil(t, convertable) {
|
|
||||||
assert.Equal(t, "Tyler", o["name"], "Tyler")
|
|
||||||
}
|
|
||||||
|
|
||||||
o = MSI()
|
|
||||||
if assert.NotNil(t, o) {
|
|
||||||
assert.NotNil(t, o)
|
|
||||||
}
|
|
||||||
|
|
||||||
o = MSI("name", "Tyler")
|
|
||||||
if assert.NotNil(t, o) {
|
|
||||||
if assert.NotNil(t, o) {
|
|
||||||
assert.Equal(t, o["name"], "Tyler")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapMustFromJSONWithError(t *testing.T) {
|
|
||||||
|
|
||||||
_, err := FromJSON(`"name":"Mat"}`)
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapFromJSON(t *testing.T) {
|
|
||||||
|
|
||||||
o := MustFromJSON(`{"name":"Mat"}`)
|
|
||||||
|
|
||||||
if assert.NotNil(t, o) {
|
|
||||||
if assert.NotNil(t, o) {
|
|
||||||
assert.Equal(t, "Mat", o["name"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapFromJSONWithError(t *testing.T) {
|
|
||||||
|
|
||||||
var m Map
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
m = MustFromJSON(`"name":"Mat"}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
assert.Nil(t, m)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapFromBase64String(t *testing.T) {
|
|
||||||
|
|
||||||
base64String := "eyJuYW1lIjoiTWF0In0="
|
|
||||||
|
|
||||||
o, err := FromBase64(base64String)
|
|
||||||
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
assert.Equal(t, o.Get("name").Str(), "Mat")
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, MustFromBase64(base64String).Get("name").Str(), "Mat")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapFromBase64StringWithError(t *testing.T) {
|
|
||||||
|
|
||||||
base64String := "eyJuYW1lIjoiTWFasd0In0="
|
|
||||||
|
|
||||||
_, err := FromBase64(base64String)
|
|
||||||
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
MustFromBase64(base64String)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapFromSignedBase64String(t *testing.T) {
|
|
||||||
|
|
||||||
base64String := "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6"
|
|
||||||
|
|
||||||
o, err := FromSignedBase64(base64String, "key")
|
|
||||||
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
assert.Equal(t, o.Get("name").Str(), "Mat")
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, MustFromSignedBase64(base64String, "key").Get("name").Str(), "Mat")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapFromSignedBase64StringWithError(t *testing.T) {
|
|
||||||
|
|
||||||
base64String := "eyJuYW1lasdIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6"
|
|
||||||
|
|
||||||
_, err := FromSignedBase64(base64String, "key")
|
|
||||||
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
MustFromSignedBase64(base64String, "key")
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapFromURLQuery(t *testing.T) {
|
|
||||||
|
|
||||||
m, err := FromURLQuery("name=tyler&state=UT")
|
|
||||||
if assert.NoError(t, err) && assert.NotNil(t, m) {
|
|
||||||
assert.Equal(t, "tyler", m.Get("name").Str())
|
|
||||||
assert.Equal(t, "UT", m.Get("state").Str())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
81
Godeps/_workspace/src/github.com/stretchr/objx/mutations.go
generated
vendored
81
Godeps/_workspace/src/github.com/stretchr/objx/mutations.go
generated
vendored
@ -1,81 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
// Exclude returns a new Map with the keys in the specified []string
|
|
||||||
// excluded.
|
|
||||||
func (d Map) Exclude(exclude []string) Map {
|
|
||||||
|
|
||||||
excluded := make(Map)
|
|
||||||
for k, v := range d {
|
|
||||||
var shouldInclude bool = true
|
|
||||||
for _, toExclude := range exclude {
|
|
||||||
if k == toExclude {
|
|
||||||
shouldInclude = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if shouldInclude {
|
|
||||||
excluded[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return excluded
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy creates a shallow copy of the Obj.
|
|
||||||
func (m Map) Copy() Map {
|
|
||||||
copied := make(map[string]interface{})
|
|
||||||
for k, v := range m {
|
|
||||||
copied[k] = v
|
|
||||||
}
|
|
||||||
return New(copied)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge blends the specified map with a copy of this map and returns the result.
|
|
||||||
//
|
|
||||||
// Keys that appear in both will be selected from the specified map.
|
|
||||||
// This method requires that the wrapped object be a map[string]interface{}
|
|
||||||
func (m Map) Merge(merge Map) Map {
|
|
||||||
return m.Copy().MergeHere(merge)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge blends the specified map with this map and returns the current map.
|
|
||||||
//
|
|
||||||
// Keys that appear in both will be selected from the specified map. The original map
|
|
||||||
// will be modified. This method requires that
|
|
||||||
// the wrapped object be a map[string]interface{}
|
|
||||||
func (m Map) MergeHere(merge Map) Map {
|
|
||||||
|
|
||||||
for k, v := range merge {
|
|
||||||
m[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform builds a new Obj giving the transformer a chance
|
|
||||||
// to change the keys and values as it goes. This method requires that
|
|
||||||
// the wrapped object be a map[string]interface{}
|
|
||||||
func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map {
|
|
||||||
newMap := make(map[string]interface{})
|
|
||||||
for k, v := range m {
|
|
||||||
modifiedKey, modifiedVal := transformer(k, v)
|
|
||||||
newMap[modifiedKey] = modifiedVal
|
|
||||||
}
|
|
||||||
return New(newMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransformKeys builds a new map using the specified key mapping.
|
|
||||||
//
|
|
||||||
// Unspecified keys will be unaltered.
|
|
||||||
// This method requires that the wrapped object be a map[string]interface{}
|
|
||||||
func (m Map) TransformKeys(mapping map[string]string) Map {
|
|
||||||
return m.Transform(func(key string, value interface{}) (string, interface{}) {
|
|
||||||
|
|
||||||
if newKey, ok := mapping[key]; ok {
|
|
||||||
return newKey, value
|
|
||||||
}
|
|
||||||
|
|
||||||
return key, value
|
|
||||||
})
|
|
||||||
}
|
|
77
Godeps/_workspace/src/github.com/stretchr/objx/mutations_test.go
generated
vendored
77
Godeps/_workspace/src/github.com/stretchr/objx/mutations_test.go
generated
vendored
@ -1,77 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestExclude(t *testing.T) {
|
|
||||||
|
|
||||||
d := make(Map)
|
|
||||||
d["name"] = "Mat"
|
|
||||||
d["age"] = 29
|
|
||||||
d["secret"] = "ABC"
|
|
||||||
|
|
||||||
excluded := d.Exclude([]string{"secret"})
|
|
||||||
|
|
||||||
assert.Equal(t, d["name"], excluded["name"])
|
|
||||||
assert.Equal(t, d["age"], excluded["age"])
|
|
||||||
assert.False(t, excluded.Has("secret"), "secret should be excluded")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopy(t *testing.T) {
|
|
||||||
|
|
||||||
d1 := make(map[string]interface{})
|
|
||||||
d1["name"] = "Tyler"
|
|
||||||
d1["location"] = "UT"
|
|
||||||
|
|
||||||
d1Obj := New(d1)
|
|
||||||
d2Obj := d1Obj.Copy()
|
|
||||||
|
|
||||||
d2Obj["name"] = "Mat"
|
|
||||||
|
|
||||||
assert.Equal(t, d1Obj.Get("name").Str(), "Tyler")
|
|
||||||
assert.Equal(t, d2Obj.Get("name").Str(), "Mat")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMerge(t *testing.T) {
|
|
||||||
|
|
||||||
d := make(map[string]interface{})
|
|
||||||
d["name"] = "Mat"
|
|
||||||
|
|
||||||
d1 := make(map[string]interface{})
|
|
||||||
d1["name"] = "Tyler"
|
|
||||||
d1["location"] = "UT"
|
|
||||||
|
|
||||||
dObj := New(d)
|
|
||||||
d1Obj := New(d1)
|
|
||||||
|
|
||||||
merged := dObj.Merge(d1Obj)
|
|
||||||
|
|
||||||
assert.Equal(t, merged.Get("name").Str(), d1Obj.Get("name").Str())
|
|
||||||
assert.Equal(t, merged.Get("location").Str(), d1Obj.Get("location").Str())
|
|
||||||
assert.Empty(t, dObj.Get("location").Str())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMergeHere(t *testing.T) {
|
|
||||||
|
|
||||||
d := make(map[string]interface{})
|
|
||||||
d["name"] = "Mat"
|
|
||||||
|
|
||||||
d1 := make(map[string]interface{})
|
|
||||||
d1["name"] = "Tyler"
|
|
||||||
d1["location"] = "UT"
|
|
||||||
|
|
||||||
dObj := New(d)
|
|
||||||
d1Obj := New(d1)
|
|
||||||
|
|
||||||
merged := dObj.MergeHere(d1Obj)
|
|
||||||
|
|
||||||
assert.Equal(t, dObj, merged, "With MergeHere, it should return the first modified map")
|
|
||||||
assert.Equal(t, merged.Get("name").Str(), d1Obj.Get("name").Str())
|
|
||||||
assert.Equal(t, merged.Get("location").Str(), d1Obj.Get("location").Str())
|
|
||||||
assert.Equal(t, merged.Get("location").Str(), dObj.Get("location").Str())
|
|
||||||
}
|
|
14
Godeps/_workspace/src/github.com/stretchr/objx/security.go
generated
vendored
14
Godeps/_workspace/src/github.com/stretchr/objx/security.go
generated
vendored
@ -1,14 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/hex"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HashWithKey hashes the specified string using the security
|
|
||||||
// key.
|
|
||||||
func HashWithKey(data, key string) string {
|
|
||||||
hash := sha1.New()
|
|
||||||
hash.Write([]byte(data + ":" + key))
|
|
||||||
return hex.EncodeToString(hash.Sum(nil))
|
|
||||||
}
|
|
12
Godeps/_workspace/src/github.com/stretchr/objx/security_test.go
generated
vendored
12
Godeps/_workspace/src/github.com/stretchr/objx/security_test.go
generated
vendored
@ -1,12 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHashWithKey(t *testing.T) {
|
|
||||||
|
|
||||||
assert.Equal(t, "0ce84d8d01f2c7b6e0882b784429c54d280ea2d9", HashWithKey("abc", "def"))
|
|
||||||
|
|
||||||
}
|
|
41
Godeps/_workspace/src/github.com/stretchr/objx/simple_example_test.go
generated
vendored
41
Godeps/_workspace/src/github.com/stretchr/objx/simple_example_test.go
generated
vendored
@ -1,41 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSimpleExample(t *testing.T) {
|
|
||||||
|
|
||||||
// build a map from a JSON object
|
|
||||||
o := MustFromJSON(`{"name":"Mat","foods":["indian","chinese"], "location":{"county":"hobbiton","city":"the shire"}}`)
|
|
||||||
|
|
||||||
// Map can be used as a straight map[string]interface{}
|
|
||||||
assert.Equal(t, o["name"], "Mat")
|
|
||||||
|
|
||||||
// Get an Value object
|
|
||||||
v := o.Get("name")
|
|
||||||
assert.Equal(t, v, &Value{data: "Mat"})
|
|
||||||
|
|
||||||
// Test the contained value
|
|
||||||
assert.False(t, v.IsInt())
|
|
||||||
assert.False(t, v.IsBool())
|
|
||||||
assert.True(t, v.IsStr())
|
|
||||||
|
|
||||||
// Get the contained value
|
|
||||||
assert.Equal(t, v.Str(), "Mat")
|
|
||||||
|
|
||||||
// Get a default value if the contained value is not of the expected type or does not exist
|
|
||||||
assert.Equal(t, 1, v.Int(1))
|
|
||||||
|
|
||||||
// Get a value by using array notation
|
|
||||||
assert.Equal(t, "indian", o.Get("foods[0]").Data())
|
|
||||||
|
|
||||||
// Set a value by using array notation
|
|
||||||
o.Set("foods[0]", "italian")
|
|
||||||
assert.Equal(t, "italian", o.Get("foods[0]").Str())
|
|
||||||
|
|
||||||
// Get a value by using dot notation
|
|
||||||
assert.Equal(t, "hobbiton", o.Get("location.county").Str())
|
|
||||||
|
|
||||||
}
|
|
17
Godeps/_workspace/src/github.com/stretchr/objx/tests.go
generated
vendored
17
Godeps/_workspace/src/github.com/stretchr/objx/tests.go
generated
vendored
@ -1,17 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
// Has gets whether there is something at the specified selector
|
|
||||||
// or not.
|
|
||||||
//
|
|
||||||
// If m is nil, Has will always return false.
|
|
||||||
func (m Map) Has(selector string) bool {
|
|
||||||
if m == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return !m.Get(selector).IsNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNil gets whether the data is nil or not.
|
|
||||||
func (v *Value) IsNil() bool {
|
|
||||||
return v == nil || v.data == nil
|
|
||||||
}
|
|
24
Godeps/_workspace/src/github.com/stretchr/objx/tests_test.go
generated
vendored
24
Godeps/_workspace/src/github.com/stretchr/objx/tests_test.go
generated
vendored
@ -1,24 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHas(t *testing.T) {
|
|
||||||
|
|
||||||
m := New(TestMap)
|
|
||||||
|
|
||||||
assert.True(t, m.Has("name"))
|
|
||||||
assert.True(t, m.Has("address.state"))
|
|
||||||
assert.True(t, m.Has("numbers[4]"))
|
|
||||||
|
|
||||||
assert.False(t, m.Has("address.state.nope"))
|
|
||||||
assert.False(t, m.Has("address.nope"))
|
|
||||||
assert.False(t, m.Has("nope"))
|
|
||||||
assert.False(t, m.Has("numbers[5]"))
|
|
||||||
|
|
||||||
m = nil
|
|
||||||
assert.False(t, m.Has("nothing"))
|
|
||||||
|
|
||||||
}
|
|
2881
Godeps/_workspace/src/github.com/stretchr/objx/type_specific_codegen.go
generated
vendored
2881
Godeps/_workspace/src/github.com/stretchr/objx/type_specific_codegen.go
generated
vendored
File diff suppressed because it is too large
Load Diff
2867
Godeps/_workspace/src/github.com/stretchr/objx/type_specific_codegen_test.go
generated
vendored
2867
Godeps/_workspace/src/github.com/stretchr/objx/type_specific_codegen_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
13
Godeps/_workspace/src/github.com/stretchr/objx/value.go
generated
vendored
13
Godeps/_workspace/src/github.com/stretchr/objx/value.go
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
package objx
|
|
||||||
|
|
||||||
// Value provides methods for extracting interface{} data in various
|
|
||||||
// types.
|
|
||||||
type Value struct {
|
|
||||||
// data contains the raw data being managed by this Value
|
|
||||||
data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data returns the raw data contained by this Value
|
|
||||||
func (v *Value) Data() interface{} {
|
|
||||||
return v.data
|
|
||||||
}
|
|
1
Godeps/_workspace/src/github.com/stretchr/objx/value_test.go
generated
vendored
1
Godeps/_workspace/src/github.com/stretchr/objx/value_test.go
generated
vendored
@ -1 +0,0 @@
|
|||||||
package objx
|
|
805
Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions.go
generated
vendored
805
Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions.go
generated
vendored
@ -1,805 +0,0 @@
|
|||||||
package assert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestingT is an interface wrapper around *testing.T
|
|
||||||
type TestingT interface {
|
|
||||||
Errorf(format string, args ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comparison a custom function that returns true on success and false on failure
|
|
||||||
type Comparison func() (success bool)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Helper functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ObjectsAreEqual determines if two objects are considered equal.
|
|
||||||
//
|
|
||||||
// This function does no assertion of any kind.
|
|
||||||
func ObjectsAreEqual(expected, actual interface{}) bool {
|
|
||||||
|
|
||||||
if expected == nil || actual == nil {
|
|
||||||
return expected == actual
|
|
||||||
}
|
|
||||||
|
|
||||||
if reflect.DeepEqual(expected, actual) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Last ditch effort
|
|
||||||
if fmt.Sprintf("%#v", expected) == fmt.Sprintf("%#v", actual) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func ObjectsAreEqualValues(expected, actual interface{}) bool {
|
|
||||||
if ObjectsAreEqual(expected, actual) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
actualType := reflect.TypeOf(actual)
|
|
||||||
expectedValue := reflect.ValueOf(expected)
|
|
||||||
if expectedValue.Type().ConvertibleTo(actualType) {
|
|
||||||
// Attempt comparison after type conversion
|
|
||||||
if reflect.DeepEqual(actual, expectedValue.Convert(actualType).Interface()) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CallerInfo is necessary because the assert functions use the testing object
|
|
||||||
internally, causing it to print the file:line of the assert method, rather than where
|
|
||||||
the problem actually occured in calling code.*/
|
|
||||||
|
|
||||||
// CallerInfo returns a string containing the file and line number of the assert call
|
|
||||||
// that failed.
|
|
||||||
func CallerInfo() string {
|
|
||||||
|
|
||||||
file := ""
|
|
||||||
line := 0
|
|
||||||
ok := false
|
|
||||||
|
|
||||||
for i := 0; ; i++ {
|
|
||||||
_, file, line, ok = runtime.Caller(i)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
parts := strings.Split(file, "/")
|
|
||||||
dir := parts[len(parts)-2]
|
|
||||||
file = parts[len(parts)-1]
|
|
||||||
if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s:%d", file, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getWhitespaceString returns a string that is long enough to overwrite the default
|
|
||||||
// output from the go testing framework.
|
|
||||||
func getWhitespaceString() string {
|
|
||||||
|
|
||||||
_, file, line, ok := runtime.Caller(1)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
parts := strings.Split(file, "/")
|
|
||||||
file = parts[len(parts)-1]
|
|
||||||
|
|
||||||
return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line)))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
|
||||||
if len(msgAndArgs) == 0 || msgAndArgs == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if len(msgAndArgs) == 1 {
|
|
||||||
return msgAndArgs[0].(string)
|
|
||||||
}
|
|
||||||
if len(msgAndArgs) > 1 {
|
|
||||||
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indents all lines of the message by appending a number of tabs to each line, in an output format compatible with Go's
|
|
||||||
// test printing (see inner comment for specifics)
|
|
||||||
func indentMessageLines(message string, tabs int) string {
|
|
||||||
outBuf := new(bytes.Buffer)
|
|
||||||
|
|
||||||
for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ {
|
|
||||||
if i != 0 {
|
|
||||||
outBuf.WriteRune('\n')
|
|
||||||
}
|
|
||||||
for ii := 0; ii < tabs; ii++ {
|
|
||||||
outBuf.WriteRune('\t')
|
|
||||||
// Bizarrely, all lines except the first need one fewer tabs prepended, so deliberately advance the counter
|
|
||||||
// by 1 prematurely.
|
|
||||||
if ii == 0 && i > 0 {
|
|
||||||
ii++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
outBuf.WriteString(scanner.Text())
|
|
||||||
}
|
|
||||||
|
|
||||||
return outBuf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fail reports a failure through
|
|
||||||
func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
message := messageFromMsgAndArgs(msgAndArgs...)
|
|
||||||
|
|
||||||
if len(message) > 0 {
|
|
||||||
t.Errorf("\r%s\r\tLocation:\t%s\n"+
|
|
||||||
"\r\tError:%s\n"+
|
|
||||||
"\r\tMessages:\t%s\n\r",
|
|
||||||
getWhitespaceString(),
|
|
||||||
CallerInfo(),
|
|
||||||
indentMessageLines(failureMessage, 2),
|
|
||||||
message)
|
|
||||||
} else {
|
|
||||||
t.Errorf("\r%s\r\tLocation:\t%s\n"+
|
|
||||||
"\r\tError:%s\n\r",
|
|
||||||
getWhitespaceString(),
|
|
||||||
CallerInfo(),
|
|
||||||
indentMessageLines(failureMessage, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements asserts that an object is implemented by the specified interface.
|
|
||||||
//
|
|
||||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject")
|
|
||||||
func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
interfaceType := reflect.TypeOf(interfaceObject).Elem()
|
|
||||||
|
|
||||||
if !reflect.TypeOf(object).Implements(interfaceType) {
|
|
||||||
return Fail(t, fmt.Sprintf("Object must implement %v", interfaceType), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsType asserts that the specified objects are of the same type.
|
|
||||||
func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) {
|
|
||||||
return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal asserts that two objects are equal.
|
|
||||||
//
|
|
||||||
// assert.Equal(t, 123, 123, "123 and 123 should be equal")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
if !ObjectsAreEqual(expected, actual) {
|
|
||||||
return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+
|
|
||||||
" != %#v (actual)", expected, actual), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
|
||||||
// and equal.
|
|
||||||
//
|
|
||||||
// assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
if !ObjectsAreEqualValues(expected, actual) {
|
|
||||||
return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+
|
|
||||||
" != %#v (actual)", expected, actual), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exactly asserts that two objects are equal is value and type.
|
|
||||||
//
|
|
||||||
// assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
aType := reflect.TypeOf(expected)
|
|
||||||
bType := reflect.TypeOf(actual)
|
|
||||||
|
|
||||||
if aType != bType {
|
|
||||||
return Fail(t, "Types expected to match exactly", "%v != %v", aType, bType)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Equal(t, expected, actual, msgAndArgs...)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotNil asserts that the specified object is not nil.
|
|
||||||
//
|
|
||||||
// assert.NotNil(t, err, "err should be something")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
success := true
|
|
||||||
|
|
||||||
if object == nil {
|
|
||||||
success = false
|
|
||||||
} else {
|
|
||||||
value := reflect.ValueOf(object)
|
|
||||||
kind := value.Kind()
|
|
||||||
if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
|
|
||||||
success = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !success {
|
|
||||||
Fail(t, "Expected not to be nil.", msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// isNil checks if a specified object is nil or not, without Failing.
|
|
||||||
func isNil(object interface{}) bool {
|
|
||||||
if object == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
value := reflect.ValueOf(object)
|
|
||||||
kind := value.Kind()
|
|
||||||
if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nil asserts that the specified object is nil.
|
|
||||||
//
|
|
||||||
// assert.Nil(t, err, "err should be nothing")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
if isNil(object) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var zeros = []interface{}{
|
|
||||||
int(0),
|
|
||||||
int8(0),
|
|
||||||
int16(0),
|
|
||||||
int32(0),
|
|
||||||
int64(0),
|
|
||||||
uint(0),
|
|
||||||
uint8(0),
|
|
||||||
uint16(0),
|
|
||||||
uint32(0),
|
|
||||||
uint64(0),
|
|
||||||
float32(0),
|
|
||||||
float64(0),
|
|
||||||
}
|
|
||||||
|
|
||||||
// isEmpty gets whether the specified object is considered empty or not.
|
|
||||||
func isEmpty(object interface{}) bool {
|
|
||||||
|
|
||||||
if object == nil {
|
|
||||||
return true
|
|
||||||
} else if object == "" {
|
|
||||||
return true
|
|
||||||
} else if object == false {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range zeros {
|
|
||||||
if object == v {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
objValue := reflect.ValueOf(object)
|
|
||||||
|
|
||||||
switch objValue.Kind() {
|
|
||||||
case reflect.Map:
|
|
||||||
fallthrough
|
|
||||||
case reflect.Slice, reflect.Chan:
|
|
||||||
{
|
|
||||||
return (objValue.Len() == 0)
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
{
|
|
||||||
switch object.(type) {
|
|
||||||
case *time.Time:
|
|
||||||
return object.(*time.Time).IsZero()
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
|
||||||
// a slice or a channel with len == 0.
|
|
||||||
//
|
|
||||||
// assert.Empty(t, obj)
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
pass := isEmpty(object)
|
|
||||||
if !pass {
|
|
||||||
Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pass
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
|
||||||
// a slice or a channel with len == 0.
|
|
||||||
//
|
|
||||||
// if assert.NotEmpty(t, obj) {
|
|
||||||
// assert.Equal(t, "two", obj[1])
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
pass := !isEmpty(object)
|
|
||||||
if !pass {
|
|
||||||
Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pass
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// getLen try to get length of object.
|
|
||||||
// return (false, 0) if impossible.
|
|
||||||
func getLen(x interface{}) (ok bool, length int) {
|
|
||||||
v := reflect.ValueOf(x)
|
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
ok = false
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return true, v.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len asserts that the specified object has specific length.
|
|
||||||
// Len also fails if the object has a type that len() not accept.
|
|
||||||
//
|
|
||||||
// assert.Len(t, mySlice, 3, "The size of slice is not 3")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
|
|
||||||
ok, l := getLen(object)
|
|
||||||
if !ok {
|
|
||||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if l != length {
|
|
||||||
return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// True asserts that the specified value is true.
|
|
||||||
//
|
|
||||||
// assert.True(t, myBool, "myBool should be true")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
if value != true {
|
|
||||||
return Fail(t, "Should be true", msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// False asserts that the specified value is true.
|
|
||||||
//
|
|
||||||
// assert.False(t, myBool, "myBool should be false")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
if value != false {
|
|
||||||
return Fail(t, "Should be false", msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotEqual asserts that the specified values are NOT equal.
|
|
||||||
//
|
|
||||||
// assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
if ObjectsAreEqual(expected, actual) {
|
|
||||||
return Fail(t, "Should not be equal", msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// containsElement try loop over the list check if the list includes the element.
|
|
||||||
// return (false, false) if impossible.
|
|
||||||
// return (true, false) if element was not found.
|
|
||||||
// return (true, true) if element was found.
|
|
||||||
func includeElement(list interface{}, element interface{}) (ok, found bool) {
|
|
||||||
|
|
||||||
listValue := reflect.ValueOf(list)
|
|
||||||
elementValue := reflect.ValueOf(element)
|
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
ok = false
|
|
||||||
found = false
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if reflect.TypeOf(list).Kind() == reflect.String {
|
|
||||||
return true, strings.Contains(listValue.String(), elementValue.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < listValue.Len(); i++ {
|
|
||||||
if ObjectsAreEqual(listValue.Index(i).Interface(), element) {
|
|
||||||
return true, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, false
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains asserts that the specified string or list(array, slice...) contains the
|
|
||||||
// specified substring or element.
|
|
||||||
//
|
|
||||||
// assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'")
|
|
||||||
// assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
ok, found := includeElement(s, contains)
|
|
||||||
if !ok {
|
|
||||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotContains asserts that the specified string or list(array, slice...) does NOT contain the
|
|
||||||
// specified substring or element.
|
|
||||||
//
|
|
||||||
// assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
|
|
||||||
// assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
ok, found := includeElement(s, contains)
|
|
||||||
if !ok {
|
|
||||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
|
|
||||||
}
|
|
||||||
if found {
|
|
||||||
return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Condition uses a Comparison to assert a complex condition.
|
|
||||||
func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
|
|
||||||
result := comp()
|
|
||||||
if !result {
|
|
||||||
Fail(t, "Condition failed!", msgAndArgs...)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics
|
|
||||||
// methods, and represents a simple func that takes no arguments, and returns nothing.
|
|
||||||
type PanicTestFunc func()
|
|
||||||
|
|
||||||
// didPanic returns true if the function passed to it panics. Otherwise, it returns false.
|
|
||||||
func didPanic(f PanicTestFunc) (bool, interface{}) {
|
|
||||||
|
|
||||||
didPanic := false
|
|
||||||
var message interface{}
|
|
||||||
func() {
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if message = recover(); message != nil {
|
|
||||||
didPanic = true
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// call the target function
|
|
||||||
f()
|
|
||||||
|
|
||||||
}()
|
|
||||||
|
|
||||||
return didPanic, message
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
|
||||||
//
|
|
||||||
// assert.Panics(t, func(){
|
|
||||||
// GoCrazy()
|
|
||||||
// }, "Calling GoCrazy() should panic")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {
|
|
||||||
return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
|
||||||
//
|
|
||||||
// assert.NotPanics(t, func(){
|
|
||||||
// RemainCalm()
|
|
||||||
// }, "Calling RemainCalm() should NOT panic")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
if funcDidPanic, panicValue := didPanic(f); funcDidPanic {
|
|
||||||
return Fail(t, fmt.Sprintf("func %#v should not panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithinDuration asserts that the two times are within duration delta of each other.
|
|
||||||
//
|
|
||||||
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
dt := expected.Sub(actual)
|
|
||||||
if dt < -delta || dt > delta {
|
|
||||||
return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func toFloat(x interface{}) (float64, bool) {
|
|
||||||
var xf float64
|
|
||||||
xok := true
|
|
||||||
|
|
||||||
switch xn := x.(type) {
|
|
||||||
case uint8:
|
|
||||||
xf = float64(xn)
|
|
||||||
case uint16:
|
|
||||||
xf = float64(xn)
|
|
||||||
case uint32:
|
|
||||||
xf = float64(xn)
|
|
||||||
case uint64:
|
|
||||||
xf = float64(xn)
|
|
||||||
case int:
|
|
||||||
xf = float64(xn)
|
|
||||||
case int8:
|
|
||||||
xf = float64(xn)
|
|
||||||
case int16:
|
|
||||||
xf = float64(xn)
|
|
||||||
case int32:
|
|
||||||
xf = float64(xn)
|
|
||||||
case int64:
|
|
||||||
xf = float64(xn)
|
|
||||||
case float32:
|
|
||||||
xf = float64(xn)
|
|
||||||
case float64:
|
|
||||||
xf = float64(xn)
|
|
||||||
default:
|
|
||||||
xok = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return xf, xok
|
|
||||||
}
|
|
||||||
|
|
||||||
// InDelta asserts that the two numerals are within delta of each other.
|
|
||||||
//
|
|
||||||
// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
af, aok := toFloat(expected)
|
|
||||||
bf, bok := toFloat(actual)
|
|
||||||
|
|
||||||
if !aok || !bok {
|
|
||||||
return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
dt := af - bf
|
|
||||||
if dt < -delta || dt > delta {
|
|
||||||
return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// min(|expected|, |actual|) * epsilon
|
|
||||||
func calcEpsilonDelta(expected, actual interface{}, epsilon float64) float64 {
|
|
||||||
af, aok := toFloat(expected)
|
|
||||||
bf, bok := toFloat(actual)
|
|
||||||
|
|
||||||
if !aok || !bok {
|
|
||||||
// invalid input
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if af < 0 {
|
|
||||||
af = -af
|
|
||||||
}
|
|
||||||
if bf < 0 {
|
|
||||||
bf = -bf
|
|
||||||
}
|
|
||||||
var delta float64
|
|
||||||
if af < bf {
|
|
||||||
delta = af * epsilon
|
|
||||||
} else {
|
|
||||||
delta = bf * epsilon
|
|
||||||
}
|
|
||||||
return delta
|
|
||||||
}
|
|
||||||
|
|
||||||
// InEpsilon asserts that expected and actual have a relative error less than epsilon
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
|
||||||
delta := calcEpsilonDelta(expected, actual, epsilon)
|
|
||||||
|
|
||||||
return InDelta(t, expected, actual, delta, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Errors
|
|
||||||
*/
|
|
||||||
|
|
||||||
// NoError asserts that a function returned no error (i.e. `nil`).
|
|
||||||
//
|
|
||||||
// actualObj, err := SomeFunction()
|
|
||||||
// if assert.NoError(t, err) {
|
|
||||||
// assert.Equal(t, actualObj, expectedObj)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
|
||||||
if isNil(err) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return Fail(t, fmt.Sprintf("No error is expected but got %v", err), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error asserts that a function returned an error (i.e. not `nil`).
|
|
||||||
//
|
|
||||||
// actualObj, err := SomeFunction()
|
|
||||||
// if assert.Error(t, err, "An error was expected") {
|
|
||||||
// assert.Equal(t, err, expectedError)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
message := messageFromMsgAndArgs(msgAndArgs...)
|
|
||||||
return NotNil(t, err, "An error is expected but got nil. %s", message)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// EqualError asserts that a function returned an error (i.e. not `nil`)
|
|
||||||
// and that it is equal to the provided error.
|
|
||||||
//
|
|
||||||
// actualObj, err := SomeFunction()
|
|
||||||
// if assert.Error(t, err, "An error was expected") {
|
|
||||||
// assert.Equal(t, err, expectedError)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
message := messageFromMsgAndArgs(msgAndArgs...)
|
|
||||||
if !NotNil(t, theError, "An error is expected but got nil. %s", message) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
s := "An error with value \"%s\" is expected but got \"%s\". %s"
|
|
||||||
return Equal(t, theError.Error(), errString,
|
|
||||||
s, errString, theError.Error(), message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// matchRegexp return true if a specified regexp matches a string.
|
|
||||||
func matchRegexp(rx interface{}, str interface{}) bool {
|
|
||||||
|
|
||||||
var r *regexp.Regexp
|
|
||||||
if rr, ok := rx.(*regexp.Regexp); ok {
|
|
||||||
r = rr
|
|
||||||
} else {
|
|
||||||
r = regexp.MustCompile(fmt.Sprint(rx))
|
|
||||||
}
|
|
||||||
|
|
||||||
return (r.FindStringIndex(fmt.Sprint(str)) != nil)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regexp asserts that a specified regexp matches a string.
|
|
||||||
//
|
|
||||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
|
||||||
// assert.Regexp(t, "start...$", "it's not starting")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
|
|
||||||
match := matchRegexp(rx, str)
|
|
||||||
|
|
||||||
if !match {
|
|
||||||
Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return match
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotRegexp asserts that a specified regexp does not match a string.
|
|
||||||
//
|
|
||||||
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
|
||||||
// assert.NotRegexp(t, "^start", "it's not starting")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
match := matchRegexp(rx, str)
|
|
||||||
|
|
||||||
if match {
|
|
||||||
Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return !match
|
|
||||||
|
|
||||||
}
|
|
768
Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions_test.go
generated
vendored
768
Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions_test.go
generated
vendored
@ -1,768 +0,0 @@
|
|||||||
package assert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AssertionTesterInterface defines an interface to be used for testing assertion methods
|
|
||||||
type AssertionTesterInterface interface {
|
|
||||||
TestMethod()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface
|
|
||||||
type AssertionTesterConformingObject struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AssertionTesterConformingObject) TestMethod() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface
|
|
||||||
type AssertionTesterNonConformingObject struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestObjectsAreEqual(t *testing.T) {
|
|
||||||
|
|
||||||
if !ObjectsAreEqual("Hello World", "Hello World") {
|
|
||||||
t.Error("objectsAreEqual should return true")
|
|
||||||
}
|
|
||||||
if !ObjectsAreEqual(123, 123) {
|
|
||||||
t.Error("objectsAreEqual should return true")
|
|
||||||
}
|
|
||||||
if !ObjectsAreEqual(123.5, 123.5) {
|
|
||||||
t.Error("objectsAreEqual should return true")
|
|
||||||
}
|
|
||||||
if !ObjectsAreEqual([]byte("Hello World"), []byte("Hello World")) {
|
|
||||||
t.Error("objectsAreEqual should return true")
|
|
||||||
}
|
|
||||||
if !ObjectsAreEqual(nil, nil) {
|
|
||||||
t.Error("objectsAreEqual should return true")
|
|
||||||
}
|
|
||||||
if ObjectsAreEqual(map[int]int{5: 10}, map[int]int{10: 20}) {
|
|
||||||
t.Error("objectsAreEqual should return false")
|
|
||||||
}
|
|
||||||
if ObjectsAreEqual('x', "x") {
|
|
||||||
t.Error("objectsAreEqual should return false")
|
|
||||||
}
|
|
||||||
if ObjectsAreEqual("x", 'x') {
|
|
||||||
t.Error("objectsAreEqual should return false")
|
|
||||||
}
|
|
||||||
if ObjectsAreEqual(0, 0.1) {
|
|
||||||
t.Error("objectsAreEqual should return false")
|
|
||||||
}
|
|
||||||
if ObjectsAreEqual(0.1, 0) {
|
|
||||||
t.Error("objectsAreEqual should return false")
|
|
||||||
}
|
|
||||||
if ObjectsAreEqual(uint32(10), int32(10)) {
|
|
||||||
t.Error("objectsAreEqual should return false")
|
|
||||||
}
|
|
||||||
if !ObjectsAreEqualValues(uint32(10), int32(10)) {
|
|
||||||
t.Error("ObjectsAreEqualValues should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestImplements(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface")
|
|
||||||
}
|
|
||||||
if Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) {
|
|
||||||
t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsType(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject")
|
|
||||||
}
|
|
||||||
if IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) {
|
|
||||||
t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqual(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !Equal(mockT, "Hello World", "Hello World") {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
if !Equal(mockT, 123, 123) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
if !Equal(mockT, 123.5, 123.5) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
if !Equal(mockT, []byte("Hello World"), []byte("Hello World")) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
if !Equal(mockT, nil, nil) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
if !Equal(mockT, int32(123), int32(123)) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
if !Equal(mockT, uint64(123), uint64(123)) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
funcA := func() int { return 42 }
|
|
||||||
if !Equal(mockT, funcA, funcA) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotNil(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !NotNil(mockT, new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("NotNil should return true: object is not nil")
|
|
||||||
}
|
|
||||||
if NotNil(mockT, nil) {
|
|
||||||
t.Error("NotNil should return false: object is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNil(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !Nil(mockT, nil) {
|
|
||||||
t.Error("Nil should return true: object is nil")
|
|
||||||
}
|
|
||||||
if Nil(mockT, new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("Nil should return false: object is not nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTrue(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !True(mockT, true) {
|
|
||||||
t.Error("True should return true")
|
|
||||||
}
|
|
||||||
if True(mockT, false) {
|
|
||||||
t.Error("True should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFalse(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !False(mockT, false) {
|
|
||||||
t.Error("False should return true")
|
|
||||||
}
|
|
||||||
if False(mockT, true) {
|
|
||||||
t.Error("False should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExactly(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
a := float32(1)
|
|
||||||
b := float64(1)
|
|
||||||
c := float32(1)
|
|
||||||
d := float32(2)
|
|
||||||
|
|
||||||
if Exactly(mockT, a, b) {
|
|
||||||
t.Error("Exactly should return false")
|
|
||||||
}
|
|
||||||
if Exactly(mockT, a, d) {
|
|
||||||
t.Error("Exactly should return false")
|
|
||||||
}
|
|
||||||
if !Exactly(mockT, a, c) {
|
|
||||||
t.Error("Exactly should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if Exactly(mockT, nil, a) {
|
|
||||||
t.Error("Exactly should return false")
|
|
||||||
}
|
|
||||||
if Exactly(mockT, a, nil) {
|
|
||||||
t.Error("Exactly should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotEqual(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !NotEqual(mockT, "Hello World", "Hello World!") {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
if !NotEqual(mockT, 123, 1234) {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
if !NotEqual(mockT, 123.5, 123.55) {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
if !NotEqual(mockT, []byte("Hello World"), []byte("Hello World!")) {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
if !NotEqual(mockT, nil, new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
funcA := func() int { return 23 }
|
|
||||||
funcB := func() int { return 42 }
|
|
||||||
if !NotEqual(mockT, funcA, funcB) {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if NotEqual(mockT, "Hello World", "Hello World") {
|
|
||||||
t.Error("NotEqual should return false")
|
|
||||||
}
|
|
||||||
if NotEqual(mockT, 123, 123) {
|
|
||||||
t.Error("NotEqual should return false")
|
|
||||||
}
|
|
||||||
if NotEqual(mockT, 123.5, 123.5) {
|
|
||||||
t.Error("NotEqual should return false")
|
|
||||||
}
|
|
||||||
if NotEqual(mockT, []byte("Hello World"), []byte("Hello World")) {
|
|
||||||
t.Error("NotEqual should return false")
|
|
||||||
}
|
|
||||||
if NotEqual(mockT, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("NotEqual should return false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type A struct {
|
|
||||||
Name, Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContains(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
list := []string{"Foo", "Bar"}
|
|
||||||
complexList := []*A{
|
|
||||||
{"b", "c"},
|
|
||||||
{"d", "e"},
|
|
||||||
{"g", "h"},
|
|
||||||
{"j", "k"},
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Contains(mockT, "Hello World", "Hello") {
|
|
||||||
t.Error("Contains should return true: \"Hello World\" contains \"Hello\"")
|
|
||||||
}
|
|
||||||
if Contains(mockT, "Hello World", "Salut") {
|
|
||||||
t.Error("Contains should return false: \"Hello World\" does not contain \"Salut\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Contains(mockT, list, "Bar") {
|
|
||||||
t.Error("Contains should return true: \"[\"Foo\", \"Bar\"]\" contains \"Bar\"")
|
|
||||||
}
|
|
||||||
if Contains(mockT, list, "Salut") {
|
|
||||||
t.Error("Contains should return false: \"[\"Foo\", \"Bar\"]\" does not contain \"Salut\"")
|
|
||||||
}
|
|
||||||
if !Contains(mockT, complexList, &A{"g", "h"}) {
|
|
||||||
t.Error("Contains should return true: complexList contains {\"g\", \"h\"}")
|
|
||||||
}
|
|
||||||
if Contains(mockT, complexList, &A{"g", "e"}) {
|
|
||||||
t.Error("Contains should return false: complexList contains {\"g\", \"e\"}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotContains(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
list := []string{"Foo", "Bar"}
|
|
||||||
|
|
||||||
if !NotContains(mockT, "Hello World", "Hello!") {
|
|
||||||
t.Error("NotContains should return true: \"Hello World\" does not contain \"Hello!\"")
|
|
||||||
}
|
|
||||||
if NotContains(mockT, "Hello World", "Hello") {
|
|
||||||
t.Error("NotContains should return false: \"Hello World\" contains \"Hello\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !NotContains(mockT, list, "Foo!") {
|
|
||||||
t.Error("NotContains should return true: \"[\"Foo\", \"Bar\"]\" does not contain \"Foo!\"")
|
|
||||||
}
|
|
||||||
if NotContains(mockT, list, "Foo") {
|
|
||||||
t.Error("NotContains should return false: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_includeElement(t *testing.T) {
|
|
||||||
|
|
||||||
list1 := []string{"Foo", "Bar"}
|
|
||||||
list2 := []int{1, 2}
|
|
||||||
|
|
||||||
ok, found := includeElement("Hello World", "World")
|
|
||||||
True(t, ok)
|
|
||||||
True(t, found)
|
|
||||||
|
|
||||||
ok, found = includeElement(list1, "Foo")
|
|
||||||
True(t, ok)
|
|
||||||
True(t, found)
|
|
||||||
|
|
||||||
ok, found = includeElement(list1, "Bar")
|
|
||||||
True(t, ok)
|
|
||||||
True(t, found)
|
|
||||||
|
|
||||||
ok, found = includeElement(list2, 1)
|
|
||||||
True(t, ok)
|
|
||||||
True(t, found)
|
|
||||||
|
|
||||||
ok, found = includeElement(list2, 2)
|
|
||||||
True(t, ok)
|
|
||||||
True(t, found)
|
|
||||||
|
|
||||||
ok, found = includeElement(list1, "Foo!")
|
|
||||||
True(t, ok)
|
|
||||||
False(t, found)
|
|
||||||
|
|
||||||
ok, found = includeElement(list2, 3)
|
|
||||||
True(t, ok)
|
|
||||||
False(t, found)
|
|
||||||
|
|
||||||
ok, found = includeElement(list2, "1")
|
|
||||||
True(t, ok)
|
|
||||||
False(t, found)
|
|
||||||
|
|
||||||
ok, found = includeElement(1433, "1")
|
|
||||||
False(t, ok)
|
|
||||||
False(t, found)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCondition(t *testing.T) {
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !Condition(mockT, func() bool { return true }, "Truth") {
|
|
||||||
t.Error("Condition should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if Condition(mockT, func() bool { return false }, "Lie") {
|
|
||||||
t.Error("Condition should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDidPanic(t *testing.T) {
|
|
||||||
|
|
||||||
if funcDidPanic, _ := didPanic(func() {
|
|
||||||
panic("Panic!")
|
|
||||||
}); !funcDidPanic {
|
|
||||||
t.Error("didPanic should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if funcDidPanic, _ := didPanic(func() {
|
|
||||||
}); funcDidPanic {
|
|
||||||
t.Error("didPanic should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPanics(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !Panics(mockT, func() {
|
|
||||||
panic("Panic!")
|
|
||||||
}) {
|
|
||||||
t.Error("Panics should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if Panics(mockT, func() {
|
|
||||||
}) {
|
|
||||||
t.Error("Panics should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotPanics(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
if !NotPanics(mockT, func() {
|
|
||||||
}) {
|
|
||||||
t.Error("NotPanics should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if NotPanics(mockT, func() {
|
|
||||||
panic("Panic!")
|
|
||||||
}) {
|
|
||||||
t.Error("NotPanics should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqual_Funcs(t *testing.T) {
|
|
||||||
|
|
||||||
type f func() int
|
|
||||||
f1 := func() int { return 1 }
|
|
||||||
f2 := func() int { return 2 }
|
|
||||||
|
|
||||||
f1Copy := f1
|
|
||||||
|
|
||||||
Equal(t, f1Copy, f1, "Funcs are the same and should be considered equal")
|
|
||||||
NotEqual(t, f1, f2, "f1 and f2 are different")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoError(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
// start with a nil error
|
|
||||||
var err error
|
|
||||||
|
|
||||||
True(t, NoError(mockT, err), "NoError should return True for nil arg")
|
|
||||||
|
|
||||||
// now set an error
|
|
||||||
err = errors.New("some error")
|
|
||||||
|
|
||||||
False(t, NoError(mockT, err), "NoError with error should return False")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestError(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
// start with a nil error
|
|
||||||
var err error
|
|
||||||
|
|
||||||
False(t, Error(mockT, err), "Error should return False for nil arg")
|
|
||||||
|
|
||||||
// now set an error
|
|
||||||
err = errors.New("some error")
|
|
||||||
|
|
||||||
True(t, Error(mockT, err), "Error with error should return True")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqualError(t *testing.T) {
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
// start with a nil error
|
|
||||||
var err error
|
|
||||||
False(t, EqualError(mockT, err, ""),
|
|
||||||
"EqualError should return false for nil arg")
|
|
||||||
|
|
||||||
// now set an error
|
|
||||||
err = errors.New("some error")
|
|
||||||
False(t, EqualError(mockT, err, "Not some error"),
|
|
||||||
"EqualError should return false for different error string")
|
|
||||||
True(t, EqualError(mockT, err, "some error"),
|
|
||||||
"EqualError should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_isEmpty(t *testing.T) {
|
|
||||||
|
|
||||||
chWithValue := make(chan struct{}, 1)
|
|
||||||
chWithValue <- struct{}{}
|
|
||||||
|
|
||||||
True(t, isEmpty(""))
|
|
||||||
True(t, isEmpty(nil))
|
|
||||||
True(t, isEmpty([]string{}))
|
|
||||||
True(t, isEmpty(0))
|
|
||||||
True(t, isEmpty(int32(0)))
|
|
||||||
True(t, isEmpty(int64(0)))
|
|
||||||
True(t, isEmpty(false))
|
|
||||||
True(t, isEmpty(map[string]string{}))
|
|
||||||
True(t, isEmpty(new(time.Time)))
|
|
||||||
True(t, isEmpty(make(chan struct{})))
|
|
||||||
False(t, isEmpty("something"))
|
|
||||||
False(t, isEmpty(errors.New("something")))
|
|
||||||
False(t, isEmpty([]string{"something"}))
|
|
||||||
False(t, isEmpty(1))
|
|
||||||
False(t, isEmpty(true))
|
|
||||||
False(t, isEmpty(map[string]string{"Hello": "World"}))
|
|
||||||
False(t, isEmpty(chWithValue))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmpty(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
chWithValue := make(chan struct{}, 1)
|
|
||||||
chWithValue <- struct{}{}
|
|
||||||
|
|
||||||
True(t, Empty(mockT, ""), "Empty string is empty")
|
|
||||||
True(t, Empty(mockT, nil), "Nil is empty")
|
|
||||||
True(t, Empty(mockT, []string{}), "Empty string array is empty")
|
|
||||||
True(t, Empty(mockT, 0), "Zero int value is empty")
|
|
||||||
True(t, Empty(mockT, false), "False value is empty")
|
|
||||||
True(t, Empty(mockT, make(chan struct{})), "Channel without values is empty")
|
|
||||||
|
|
||||||
False(t, Empty(mockT, "something"), "Non Empty string is not empty")
|
|
||||||
False(t, Empty(mockT, errors.New("something")), "Non nil object is not empty")
|
|
||||||
False(t, Empty(mockT, []string{"something"}), "Non empty string array is not empty")
|
|
||||||
False(t, Empty(mockT, 1), "Non-zero int value is not empty")
|
|
||||||
False(t, Empty(mockT, true), "True value is not empty")
|
|
||||||
False(t, Empty(mockT, chWithValue), "Channel with values is not empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotEmpty(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
chWithValue := make(chan struct{}, 1)
|
|
||||||
chWithValue <- struct{}{}
|
|
||||||
|
|
||||||
False(t, NotEmpty(mockT, ""), "Empty string is empty")
|
|
||||||
False(t, NotEmpty(mockT, nil), "Nil is empty")
|
|
||||||
False(t, NotEmpty(mockT, []string{}), "Empty string array is empty")
|
|
||||||
False(t, NotEmpty(mockT, 0), "Zero int value is empty")
|
|
||||||
False(t, NotEmpty(mockT, false), "False value is empty")
|
|
||||||
False(t, NotEmpty(mockT, make(chan struct{})), "Channel without values is empty")
|
|
||||||
|
|
||||||
True(t, NotEmpty(mockT, "something"), "Non Empty string is not empty")
|
|
||||||
True(t, NotEmpty(mockT, errors.New("something")), "Non nil object is not empty")
|
|
||||||
True(t, NotEmpty(mockT, []string{"something"}), "Non empty string array is not empty")
|
|
||||||
True(t, NotEmpty(mockT, 1), "Non-zero int value is not empty")
|
|
||||||
True(t, NotEmpty(mockT, true), "True value is not empty")
|
|
||||||
True(t, NotEmpty(mockT, chWithValue), "Channel with values is not empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_getLen(t *testing.T) {
|
|
||||||
falseCases := []interface{}{
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
'A',
|
|
||||||
struct{}{},
|
|
||||||
}
|
|
||||||
for _, v := range falseCases {
|
|
||||||
ok, l := getLen(v)
|
|
||||||
False(t, ok, "Expected getLen fail to get length of %#v", v)
|
|
||||||
Equal(t, 0, l, "getLen should return 0 for %#v", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan int, 5)
|
|
||||||
ch <- 1
|
|
||||||
ch <- 2
|
|
||||||
ch <- 3
|
|
||||||
trueCases := []struct {
|
|
||||||
v interface{}
|
|
||||||
l int
|
|
||||||
}{
|
|
||||||
{[]int{1, 2, 3}, 3},
|
|
||||||
{[...]int{1, 2, 3}, 3},
|
|
||||||
{"ABC", 3},
|
|
||||||
{map[int]int{1: 2, 2: 4, 3: 6}, 3},
|
|
||||||
{ch, 3},
|
|
||||||
|
|
||||||
{[]int{}, 0},
|
|
||||||
{map[int]int{}, 0},
|
|
||||||
{make(chan int), 0},
|
|
||||||
|
|
||||||
{[]int(nil), 0},
|
|
||||||
{map[int]int(nil), 0},
|
|
||||||
{(chan int)(nil), 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range trueCases {
|
|
||||||
ok, l := getLen(c.v)
|
|
||||||
True(t, ok, "Expected getLen success to get length of %#v", c.v)
|
|
||||||
Equal(t, c.l, l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLen(t *testing.T) {
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
False(t, Len(mockT, nil, 0), "nil does not have length")
|
|
||||||
False(t, Len(mockT, 0, 0), "int does not have length")
|
|
||||||
False(t, Len(mockT, true, 0), "true does not have length")
|
|
||||||
False(t, Len(mockT, false, 0), "false does not have length")
|
|
||||||
False(t, Len(mockT, 'A', 0), "Rune does not have length")
|
|
||||||
False(t, Len(mockT, struct{}{}, 0), "Struct does not have length")
|
|
||||||
|
|
||||||
ch := make(chan int, 5)
|
|
||||||
ch <- 1
|
|
||||||
ch <- 2
|
|
||||||
ch <- 3
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
v interface{}
|
|
||||||
l int
|
|
||||||
}{
|
|
||||||
{[]int{1, 2, 3}, 3},
|
|
||||||
{[...]int{1, 2, 3}, 3},
|
|
||||||
{"ABC", 3},
|
|
||||||
{map[int]int{1: 2, 2: 4, 3: 6}, 3},
|
|
||||||
{ch, 3},
|
|
||||||
|
|
||||||
{[]int{}, 0},
|
|
||||||
{map[int]int{}, 0},
|
|
||||||
{make(chan int), 0},
|
|
||||||
|
|
||||||
{[]int(nil), 0},
|
|
||||||
{map[int]int(nil), 0},
|
|
||||||
{(chan int)(nil), 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
True(t, Len(mockT, c.v, c.l), "%#v have %d items", c.v, c.l)
|
|
||||||
}
|
|
||||||
|
|
||||||
cases = []struct {
|
|
||||||
v interface{}
|
|
||||||
l int
|
|
||||||
}{
|
|
||||||
{[]int{1, 2, 3}, 4},
|
|
||||||
{[...]int{1, 2, 3}, 2},
|
|
||||||
{"ABC", 2},
|
|
||||||
{map[int]int{1: 2, 2: 4, 3: 6}, 4},
|
|
||||||
{ch, 2},
|
|
||||||
|
|
||||||
{[]int{}, 1},
|
|
||||||
{map[int]int{}, 1},
|
|
||||||
{make(chan int), 1},
|
|
||||||
|
|
||||||
{[]int(nil), 1},
|
|
||||||
{map[int]int(nil), 1},
|
|
||||||
{(chan int)(nil), 1},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
False(t, Len(mockT, c.v, c.l), "%#v have %d items", c.v, c.l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithinDuration(t *testing.T) {
|
|
||||||
|
|
||||||
mockT := new(testing.T)
|
|
||||||
a := time.Now()
|
|
||||||
b := a.Add(10 * time.Second)
|
|
||||||
|
|
||||||
True(t, WithinDuration(mockT, a, b, 10*time.Second), "A 10s difference is within a 10s time difference")
|
|
||||||
True(t, WithinDuration(mockT, b, a, 10*time.Second), "A 10s difference is within a 10s time difference")
|
|
||||||
|
|
||||||
False(t, WithinDuration(mockT, a, b, 9*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
False(t, WithinDuration(mockT, b, a, 9*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
|
|
||||||
False(t, WithinDuration(mockT, a, b, -9*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
False(t, WithinDuration(mockT, b, a, -9*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
|
|
||||||
False(t, WithinDuration(mockT, a, b, -11*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
False(t, WithinDuration(mockT, b, a, -11*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInDelta(t *testing.T) {
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
True(t, InDelta(mockT, 1.001, 1, 0.01), "|1.001 - 1| <= 0.01")
|
|
||||||
True(t, InDelta(mockT, 1, 1.001, 0.01), "|1 - 1.001| <= 0.01")
|
|
||||||
True(t, InDelta(mockT, 1, 2, 1), "|1 - 2| <= 1")
|
|
||||||
False(t, InDelta(mockT, 1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail")
|
|
||||||
False(t, InDelta(mockT, 2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail")
|
|
||||||
False(t, InDelta(mockT, "", nil, 1), "Expected non numerals to fail")
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
a, b interface{}
|
|
||||||
delta float64
|
|
||||||
}{
|
|
||||||
{uint8(2), uint8(1), 1},
|
|
||||||
{uint16(2), uint16(1), 1},
|
|
||||||
{uint32(2), uint32(1), 1},
|
|
||||||
{uint64(2), uint64(1), 1},
|
|
||||||
|
|
||||||
{int(2), int(1), 1},
|
|
||||||
{int8(2), int8(1), 1},
|
|
||||||
{int16(2), int16(1), 1},
|
|
||||||
{int32(2), int32(1), 1},
|
|
||||||
{int64(2), int64(1), 1},
|
|
||||||
|
|
||||||
{float32(2), float32(1), 1},
|
|
||||||
{float64(2), float64(1), 1},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
True(t, InDelta(mockT, tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInEpsilon(t *testing.T) {
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
a, b interface{}
|
|
||||||
epsilon float64
|
|
||||||
}{
|
|
||||||
{uint8(2), uint16(2), .001},
|
|
||||||
{2.1, 2.2, 0.1},
|
|
||||||
{2.2, 2.1, 0.1},
|
|
||||||
{-2.1, -2.2, 0.1},
|
|
||||||
{-2.2, -2.1, 0.1},
|
|
||||||
{uint64(100), uint8(101), 0.01},
|
|
||||||
{0.1, -0.1, 2},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
True(t, InEpsilon(mockT, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon))
|
|
||||||
}
|
|
||||||
|
|
||||||
cases = []struct {
|
|
||||||
a, b interface{}
|
|
||||||
epsilon float64
|
|
||||||
}{
|
|
||||||
{uint8(2), int16(-2), .001},
|
|
||||||
{uint64(100), uint8(102), 0.01},
|
|
||||||
{2.1, 2.2, 0.001},
|
|
||||||
{2.2, 2.1, 0.001},
|
|
||||||
{2.1, -2.2, 1},
|
|
||||||
{2.1, "bla-bla", 0},
|
|
||||||
{0.1, -0.1, 1.99},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
False(t, InEpsilon(mockT, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRegexp(t *testing.T) {
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
rx, str string
|
|
||||||
}{
|
|
||||||
{"^start", "start of the line"},
|
|
||||||
{"end$", "in the end"},
|
|
||||||
{"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
True(t, Regexp(mockT, tc.rx, tc.str))
|
|
||||||
True(t, Regexp(mockT, regexp.MustCompile(tc.rx), tc.str))
|
|
||||||
False(t, NotRegexp(mockT, tc.rx, tc.str))
|
|
||||||
False(t, NotRegexp(mockT, regexp.MustCompile(tc.rx), tc.str))
|
|
||||||
}
|
|
||||||
|
|
||||||
cases = []struct {
|
|
||||||
rx, str string
|
|
||||||
}{
|
|
||||||
{"^asdfastart", "Not the start of the line"},
|
|
||||||
{"end$", "in the end."},
|
|
||||||
{"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
False(t, Regexp(mockT, tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str)
|
|
||||||
False(t, Regexp(mockT, regexp.MustCompile(tc.rx), tc.str))
|
|
||||||
True(t, NotRegexp(mockT, tc.rx, tc.str))
|
|
||||||
True(t, NotRegexp(mockT, regexp.MustCompile(tc.rx), tc.str))
|
|
||||||
}
|
|
||||||
}
|
|
150
Godeps/_workspace/src/github.com/stretchr/testify/assert/doc.go
generated
vendored
150
Godeps/_workspace/src/github.com/stretchr/testify/assert/doc.go
generated
vendored
@ -1,150 +0,0 @@
|
|||||||
// A set of comprehensive testing tools for use with the normal Go testing system.
|
|
||||||
//
|
|
||||||
// Example Usage
|
|
||||||
//
|
|
||||||
// The following is a complete example using assert in a standard test function:
|
|
||||||
// import (
|
|
||||||
// "testing"
|
|
||||||
// "github.com/stretchr/testify/assert"
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// func TestSomething(t *testing.T) {
|
|
||||||
//
|
|
||||||
// var a string = "Hello"
|
|
||||||
// var b string = "Hello"
|
|
||||||
//
|
|
||||||
// assert.Equal(t, a, b, "The two words should be the same.")
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if you assert many times, use the below:
|
|
||||||
//
|
|
||||||
// import (
|
|
||||||
// "testing"
|
|
||||||
// "github.com/stretchr/testify/assert"
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// func TestSomething(t *testing.T) {
|
|
||||||
// assert := assert.New(t)
|
|
||||||
//
|
|
||||||
// var a string = "Hello"
|
|
||||||
// var b string = "Hello"
|
|
||||||
//
|
|
||||||
// assert.Equal(a, b, "The two words should be the same.")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Assertions
|
|
||||||
//
|
|
||||||
// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
|
|
||||||
// All assertion functions take, as the first argument, the `*testing.T` object provided by the
|
|
||||||
// testing framework. This allows the assertion funcs to write the failings and other details to
|
|
||||||
// the correct place.
|
|
||||||
//
|
|
||||||
// Every assertion function also takes an optional string message as the final argument,
|
|
||||||
// allowing custom error messages to be appended to the message the assertion method outputs.
|
|
||||||
//
|
|
||||||
// Here is an overview of the assert functions:
|
|
||||||
//
|
|
||||||
// assert.Equal(t, expected, actual [, message [, format-args])
|
|
||||||
//
|
|
||||||
// assert.NotEqual(t, notExpected, actual [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.True(t, actualBool [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.False(t, actualBool [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Nil(t, actualObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.NotNil(t, actualObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Empty(t, actualObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.NotEmpty(t, actualObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Len(t, actualObject, expectedLength, [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Error(t, errorObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.NoError(t, errorObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.EqualError(t, theError, errString [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject) [,message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.IsType(t, expectedObject, actualObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Contains(t, stringOrSlice, substringOrElement [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.NotContains(t, stringOrSlice, substringOrElement [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Panics(t, func(){
|
|
||||||
//
|
|
||||||
// // call code that should panic
|
|
||||||
//
|
|
||||||
// } [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.NotPanics(t, func(){
|
|
||||||
//
|
|
||||||
// // call code that should not panic
|
|
||||||
//
|
|
||||||
// } [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.WithinDuration(t, timeA, timeB, deltaTime, [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.InDelta(t, numA, numB, delta, [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.InEpsilon(t, numA, numB, epsilon, [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert package contains Assertions object. it has assertion methods.
|
|
||||||
//
|
|
||||||
// Here is an overview of the assert functions:
|
|
||||||
// assert.Equal(expected, actual [, message [, format-args])
|
|
||||||
//
|
|
||||||
// assert.NotEqual(notExpected, actual [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.True(actualBool [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.False(actualBool [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Nil(actualObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.NotNil(actualObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Empty(actualObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.NotEmpty(actualObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Len(actualObject, expectedLength, [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Error(errorObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.NoError(errorObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.EqualError(theError, errString [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Implements((*MyInterface)(nil), new(MyObject) [,message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.IsType(expectedObject, actualObject [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Contains(stringOrSlice, substringOrElement [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.NotContains(stringOrSlice, substringOrElement [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.Panics(func(){
|
|
||||||
//
|
|
||||||
// // call code that should panic
|
|
||||||
//
|
|
||||||
// } [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.NotPanics(func(){
|
|
||||||
//
|
|
||||||
// // call code that should not panic
|
|
||||||
//
|
|
||||||
// } [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.WithinDuration(timeA, timeB, deltaTime, [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.InDelta(numA, numB, delta, [, message [, format-args]])
|
|
||||||
//
|
|
||||||
// assert.InEpsilon(numA, numB, epsilon, [, message [, format-args]])
|
|
||||||
package assert
|
|
10
Godeps/_workspace/src/github.com/stretchr/testify/assert/errors.go
generated
vendored
10
Godeps/_workspace/src/github.com/stretchr/testify/assert/errors.go
generated
vendored
@ -1,10 +0,0 @@
|
|||||||
package assert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AnError is an error instance useful for testing. If the code does not care
|
|
||||||
// about error specifics, and only needs to return the error for example, this
|
|
||||||
// error should be used to make the test code more readable.
|
|
||||||
var AnError = errors.New("assert.AnError general error for testing")
|
|
262
Godeps/_workspace/src/github.com/stretchr/testify/assert/forward_assertions.go
generated
vendored
262
Godeps/_workspace/src/github.com/stretchr/testify/assert/forward_assertions.go
generated
vendored
@ -1,262 +0,0 @@
|
|||||||
package assert
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type Assertions struct {
|
|
||||||
t TestingT
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(t TestingT) *Assertions {
|
|
||||||
return &Assertions{
|
|
||||||
t: t,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fail reports a failure through
|
|
||||||
func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool {
|
|
||||||
return Fail(a.t, failureMessage, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements asserts that an object is implemented by the specified interface.
|
|
||||||
//
|
|
||||||
// assert.Implements((*MyInterface)(nil), new(MyObject), "MyObject")
|
|
||||||
func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return Implements(a.t, interfaceObject, object, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsType asserts that the specified objects are of the same type.
|
|
||||||
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return IsType(a.t, expectedType, object, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal asserts that two objects are equal.
|
|
||||||
//
|
|
||||||
// assert.Equal(123, 123, "123 and 123 should be equal")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) Equal(expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return Equal(a.t, expected, actual, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
|
||||||
// and equal.
|
|
||||||
//
|
|
||||||
// assert.EqualValues(uint32(123), int32(123), "123 and 123 should be equal")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) EqualValues(expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return EqualValues(a.t, expected, actual, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exactly asserts that two objects are equal is value and type.
|
|
||||||
//
|
|
||||||
// assert.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) Exactly(expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return Exactly(a.t, expected, actual, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotNil asserts that the specified object is not nil.
|
|
||||||
//
|
|
||||||
// assert.NotNil(err, "err should be something")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return NotNil(a.t, object, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nil asserts that the specified object is nil.
|
|
||||||
//
|
|
||||||
// assert.Nil(err, "err should be nothing")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return Nil(a.t, object, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or a
|
|
||||||
// slice with len == 0.
|
|
||||||
//
|
|
||||||
// assert.Empty(obj)
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return Empty(a.t, object, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or a
|
|
||||||
// slice with len == 0.
|
|
||||||
//
|
|
||||||
// if assert.NotEmpty(obj) {
|
|
||||||
// assert.Equal("two", obj[1])
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return NotEmpty(a.t, object, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len asserts that the specified object has specific length.
|
|
||||||
// Len also fails if the object has a type that len() not accept.
|
|
||||||
//
|
|
||||||
// assert.Len(mySlice, 3, "The size of slice is not 3")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool {
|
|
||||||
return Len(a.t, object, length, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// True asserts that the specified value is true.
|
|
||||||
//
|
|
||||||
// assert.True(myBool, "myBool should be true")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
|
|
||||||
return True(a.t, value, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// False asserts that the specified value is true.
|
|
||||||
//
|
|
||||||
// assert.False(myBool, "myBool should be false")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
|
|
||||||
return False(a.t, value, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotEqual asserts that the specified values are NOT equal.
|
|
||||||
//
|
|
||||||
// assert.NotEqual(obj1, obj2, "two objects shouldn't be equal")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) NotEqual(expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return NotEqual(a.t, expected, actual, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains asserts that the specified string contains the specified substring.
|
|
||||||
//
|
|
||||||
// assert.Contains("Hello World", "World", "But 'Hello World' does contain 'World'")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) Contains(s, contains interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return Contains(a.t, s, contains, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotContains asserts that the specified string does NOT contain the specified substring.
|
|
||||||
//
|
|
||||||
// assert.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) NotContains(s, contains interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return NotContains(a.t, s, contains, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uses a Comparison to assert a complex condition.
|
|
||||||
func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool {
|
|
||||||
return Condition(a.t, comp, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
|
||||||
//
|
|
||||||
// assert.Panics(func(){
|
|
||||||
// GoCrazy()
|
|
||||||
// }, "Calling GoCrazy() should panic")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
||||||
return Panics(a.t, f, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
|
||||||
//
|
|
||||||
// assert.NotPanics(func(){
|
|
||||||
// RemainCalm()
|
|
||||||
// }, "Calling RemainCalm() should NOT panic")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
||||||
return NotPanics(a.t, f, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithinDuration asserts that the two times are within duration delta of each other.
|
|
||||||
//
|
|
||||||
// assert.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) WithinDuration(expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
|
||||||
return WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InDelta asserts that the two numerals are within delta of each other.
|
|
||||||
//
|
|
||||||
// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) InDelta(expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
|
||||||
return InDelta(a.t, expected, actual, delta, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InEpsilon asserts that expected and actual have a relative error less than epsilon
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) InEpsilon(expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
|
||||||
return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoError asserts that a function returned no error (i.e. `nil`).
|
|
||||||
//
|
|
||||||
// actualObj, err := SomeFunction()
|
|
||||||
// if assert.NoError(err) {
|
|
||||||
// assert.Equal(actualObj, expectedObj)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) NoError(theError error, msgAndArgs ...interface{}) bool {
|
|
||||||
return NoError(a.t, theError, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error asserts that a function returned an error (i.e. not `nil`).
|
|
||||||
//
|
|
||||||
// actualObj, err := SomeFunction()
|
|
||||||
// if assert.Error(err, "An error was expected") {
|
|
||||||
// assert.Equal(err, expectedError)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) Error(theError error, msgAndArgs ...interface{}) bool {
|
|
||||||
return Error(a.t, theError, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EqualError asserts that a function returned an error (i.e. not `nil`)
|
|
||||||
// and that it is equal to the provided error.
|
|
||||||
//
|
|
||||||
// actualObj, err := SomeFunction()
|
|
||||||
// if assert.Error(err, "An error was expected") {
|
|
||||||
// assert.Equal(err, expectedError)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool {
|
|
||||||
return EqualError(a.t, theError, errString, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regexp asserts that a specified regexp matches a string.
|
|
||||||
//
|
|
||||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
|
||||||
// assert.Regexp(t, "start...$", "it's not starting")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return Regexp(a.t, rx, str, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotRegexp asserts that a specified regexp does not match a string.
|
|
||||||
//
|
|
||||||
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
|
||||||
// assert.NotRegexp(t, "^start", "it's not starting")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
|
||||||
return NotRegexp(a.t, rx, str, msgAndArgs...)
|
|
||||||
}
|
|
526
Godeps/_workspace/src/github.com/stretchr/testify/assert/forward_assertions_test.go
generated
vendored
526
Godeps/_workspace/src/github.com/stretchr/testify/assert/forward_assertions_test.go
generated
vendored
@ -1,526 +0,0 @@
|
|||||||
package assert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestImplementsWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface")
|
|
||||||
}
|
|
||||||
if assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) {
|
|
||||||
t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsTypeWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject")
|
|
||||||
}
|
|
||||||
if assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) {
|
|
||||||
t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqualWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.Equal("Hello World", "Hello World") {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
if !assert.Equal(123, 123) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
if !assert.Equal(123.5, 123.5) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
if !assert.Equal([]byte("Hello World"), []byte("Hello World")) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
if !assert.Equal(nil, nil) {
|
|
||||||
t.Error("Equal should return true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqualValuesWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.EqualValues(uint32(10), int32(10)) {
|
|
||||||
t.Error("EqualValues should return true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotNilWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.NotNil(new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("NotNil should return true: object is not nil")
|
|
||||||
}
|
|
||||||
if assert.NotNil(nil) {
|
|
||||||
t.Error("NotNil should return false: object is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNilWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.Nil(nil) {
|
|
||||||
t.Error("Nil should return true: object is nil")
|
|
||||||
}
|
|
||||||
if assert.Nil(new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("Nil should return false: object is not nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTrueWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.True(true) {
|
|
||||||
t.Error("True should return true")
|
|
||||||
}
|
|
||||||
if assert.True(false) {
|
|
||||||
t.Error("True should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFalseWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.False(false) {
|
|
||||||
t.Error("False should return true")
|
|
||||||
}
|
|
||||||
if assert.False(true) {
|
|
||||||
t.Error("False should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExactlyWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
a := float32(1)
|
|
||||||
b := float64(1)
|
|
||||||
c := float32(1)
|
|
||||||
d := float32(2)
|
|
||||||
|
|
||||||
if assert.Exactly(a, b) {
|
|
||||||
t.Error("Exactly should return false")
|
|
||||||
}
|
|
||||||
if assert.Exactly(a, d) {
|
|
||||||
t.Error("Exactly should return false")
|
|
||||||
}
|
|
||||||
if !assert.Exactly(a, c) {
|
|
||||||
t.Error("Exactly should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if assert.Exactly(nil, a) {
|
|
||||||
t.Error("Exactly should return false")
|
|
||||||
}
|
|
||||||
if assert.Exactly(a, nil) {
|
|
||||||
t.Error("Exactly should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotEqualWrapper(t *testing.T) {
|
|
||||||
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.NotEqual("Hello World", "Hello World!") {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
if !assert.NotEqual(123, 1234) {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
if !assert.NotEqual(123.5, 123.55) {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
if !assert.NotEqual([]byte("Hello World"), []byte("Hello World!")) {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
if !assert.NotEqual(nil, new(AssertionTesterConformingObject)) {
|
|
||||||
t.Error("NotEqual should return true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContainsWrapper(t *testing.T) {
|
|
||||||
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
list := []string{"Foo", "Bar"}
|
|
||||||
|
|
||||||
if !assert.Contains("Hello World", "Hello") {
|
|
||||||
t.Error("Contains should return true: \"Hello World\" contains \"Hello\"")
|
|
||||||
}
|
|
||||||
if assert.Contains("Hello World", "Salut") {
|
|
||||||
t.Error("Contains should return false: \"Hello World\" does not contain \"Salut\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Contains(list, "Foo") {
|
|
||||||
t.Error("Contains should return true: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"")
|
|
||||||
}
|
|
||||||
if assert.Contains(list, "Salut") {
|
|
||||||
t.Error("Contains should return false: \"[\"Foo\", \"Bar\"]\" does not contain \"Salut\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotContainsWrapper(t *testing.T) {
|
|
||||||
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
list := []string{"Foo", "Bar"}
|
|
||||||
|
|
||||||
if !assert.NotContains("Hello World", "Hello!") {
|
|
||||||
t.Error("NotContains should return true: \"Hello World\" does not contain \"Hello!\"")
|
|
||||||
}
|
|
||||||
if assert.NotContains("Hello World", "Hello") {
|
|
||||||
t.Error("NotContains should return false: \"Hello World\" contains \"Hello\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.NotContains(list, "Foo!") {
|
|
||||||
t.Error("NotContains should return true: \"[\"Foo\", \"Bar\"]\" does not contain \"Foo!\"")
|
|
||||||
}
|
|
||||||
if assert.NotContains(list, "Foo") {
|
|
||||||
t.Error("NotContains should return false: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConditionWrapper(t *testing.T) {
|
|
||||||
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.Condition(func() bool { return true }, "Truth") {
|
|
||||||
t.Error("Condition should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if assert.Condition(func() bool { return false }, "Lie") {
|
|
||||||
t.Error("Condition should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDidPanicWrapper(t *testing.T) {
|
|
||||||
|
|
||||||
if funcDidPanic, _ := didPanic(func() {
|
|
||||||
panic("Panic!")
|
|
||||||
}); !funcDidPanic {
|
|
||||||
t.Error("didPanic should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if funcDidPanic, _ := didPanic(func() {
|
|
||||||
}); funcDidPanic {
|
|
||||||
t.Error("didPanic should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPanicsWrapper(t *testing.T) {
|
|
||||||
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.Panics(func() {
|
|
||||||
panic("Panic!")
|
|
||||||
}) {
|
|
||||||
t.Error("Panics should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if assert.Panics(func() {
|
|
||||||
}) {
|
|
||||||
t.Error("Panics should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotPanicsWrapper(t *testing.T) {
|
|
||||||
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
if !assert.NotPanics(func() {
|
|
||||||
}) {
|
|
||||||
t.Error("NotPanics should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if assert.NotPanics(func() {
|
|
||||||
panic("Panic!")
|
|
||||||
}) {
|
|
||||||
t.Error("NotPanics should return false")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqualWrapper_Funcs(t *testing.T) {
|
|
||||||
|
|
||||||
assert := New(t)
|
|
||||||
|
|
||||||
type f func() int
|
|
||||||
var f1 f = func() int { return 1 }
|
|
||||||
var f2 f = func() int { return 2 }
|
|
||||||
|
|
||||||
var f1_copy f = f1
|
|
||||||
|
|
||||||
assert.Equal(f1_copy, f1, "Funcs are the same and should be considered equal")
|
|
||||||
assert.NotEqual(f1, f2, "f1 and f2 are different")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoErrorWrapper(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockAssert := New(new(testing.T))
|
|
||||||
|
|
||||||
// start with a nil error
|
|
||||||
var err error = nil
|
|
||||||
|
|
||||||
assert.True(mockAssert.NoError(err), "NoError should return True for nil arg")
|
|
||||||
|
|
||||||
// now set an error
|
|
||||||
err = errors.New("Some error")
|
|
||||||
|
|
||||||
assert.False(mockAssert.NoError(err), "NoError with error should return False")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorWrapper(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockAssert := New(new(testing.T))
|
|
||||||
|
|
||||||
// start with a nil error
|
|
||||||
var err error = nil
|
|
||||||
|
|
||||||
assert.False(mockAssert.Error(err), "Error should return False for nil arg")
|
|
||||||
|
|
||||||
// now set an error
|
|
||||||
err = errors.New("Some error")
|
|
||||||
|
|
||||||
assert.True(mockAssert.Error(err), "Error with error should return True")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqualErrorWrapper(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockAssert := New(new(testing.T))
|
|
||||||
|
|
||||||
// start with a nil error
|
|
||||||
var err error
|
|
||||||
assert.False(mockAssert.EqualError(err, ""),
|
|
||||||
"EqualError should return false for nil arg")
|
|
||||||
|
|
||||||
// now set an error
|
|
||||||
err = errors.New("some error")
|
|
||||||
assert.False(mockAssert.EqualError(err, "Not some error"),
|
|
||||||
"EqualError should return false for different error string")
|
|
||||||
assert.True(mockAssert.EqualError(err, "some error"),
|
|
||||||
"EqualError should return true")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptyWrapper(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockAssert := New(new(testing.T))
|
|
||||||
|
|
||||||
assert.True(mockAssert.Empty(""), "Empty string is empty")
|
|
||||||
assert.True(mockAssert.Empty(nil), "Nil is empty")
|
|
||||||
assert.True(mockAssert.Empty([]string{}), "Empty string array is empty")
|
|
||||||
assert.True(mockAssert.Empty(0), "Zero int value is empty")
|
|
||||||
assert.True(mockAssert.Empty(false), "False value is empty")
|
|
||||||
|
|
||||||
assert.False(mockAssert.Empty("something"), "Non Empty string is not empty")
|
|
||||||
assert.False(mockAssert.Empty(errors.New("something")), "Non nil object is not empty")
|
|
||||||
assert.False(mockAssert.Empty([]string{"something"}), "Non empty string array is not empty")
|
|
||||||
assert.False(mockAssert.Empty(1), "Non-zero int value is not empty")
|
|
||||||
assert.False(mockAssert.Empty(true), "True value is not empty")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotEmptyWrapper(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockAssert := New(new(testing.T))
|
|
||||||
|
|
||||||
assert.False(mockAssert.NotEmpty(""), "Empty string is empty")
|
|
||||||
assert.False(mockAssert.NotEmpty(nil), "Nil is empty")
|
|
||||||
assert.False(mockAssert.NotEmpty([]string{}), "Empty string array is empty")
|
|
||||||
assert.False(mockAssert.NotEmpty(0), "Zero int value is empty")
|
|
||||||
assert.False(mockAssert.NotEmpty(false), "False value is empty")
|
|
||||||
|
|
||||||
assert.True(mockAssert.NotEmpty("something"), "Non Empty string is not empty")
|
|
||||||
assert.True(mockAssert.NotEmpty(errors.New("something")), "Non nil object is not empty")
|
|
||||||
assert.True(mockAssert.NotEmpty([]string{"something"}), "Non empty string array is not empty")
|
|
||||||
assert.True(mockAssert.NotEmpty(1), "Non-zero int value is not empty")
|
|
||||||
assert.True(mockAssert.NotEmpty(true), "True value is not empty")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLenWrapper(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockAssert := New(new(testing.T))
|
|
||||||
|
|
||||||
assert.False(mockAssert.Len(nil, 0), "nil does not have length")
|
|
||||||
assert.False(mockAssert.Len(0, 0), "int does not have length")
|
|
||||||
assert.False(mockAssert.Len(true, 0), "true does not have length")
|
|
||||||
assert.False(mockAssert.Len(false, 0), "false does not have length")
|
|
||||||
assert.False(mockAssert.Len('A', 0), "Rune does not have length")
|
|
||||||
assert.False(mockAssert.Len(struct{}{}, 0), "Struct does not have length")
|
|
||||||
|
|
||||||
ch := make(chan int, 5)
|
|
||||||
ch <- 1
|
|
||||||
ch <- 2
|
|
||||||
ch <- 3
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
v interface{}
|
|
||||||
l int
|
|
||||||
}{
|
|
||||||
{[]int{1, 2, 3}, 3},
|
|
||||||
{[...]int{1, 2, 3}, 3},
|
|
||||||
{"ABC", 3},
|
|
||||||
{map[int]int{1: 2, 2: 4, 3: 6}, 3},
|
|
||||||
{ch, 3},
|
|
||||||
|
|
||||||
{[]int{}, 0},
|
|
||||||
{map[int]int{}, 0},
|
|
||||||
{make(chan int), 0},
|
|
||||||
|
|
||||||
{[]int(nil), 0},
|
|
||||||
{map[int]int(nil), 0},
|
|
||||||
{(chan int)(nil), 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
assert.True(mockAssert.Len(c.v, c.l), "%#v have %d items", c.v, c.l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithinDurationWrapper(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockAssert := New(new(testing.T))
|
|
||||||
a := time.Now()
|
|
||||||
b := a.Add(10 * time.Second)
|
|
||||||
|
|
||||||
assert.True(mockAssert.WithinDuration(a, b, 10*time.Second), "A 10s difference is within a 10s time difference")
|
|
||||||
assert.True(mockAssert.WithinDuration(b, a, 10*time.Second), "A 10s difference is within a 10s time difference")
|
|
||||||
|
|
||||||
assert.False(mockAssert.WithinDuration(a, b, 9*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
assert.False(mockAssert.WithinDuration(b, a, 9*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
|
|
||||||
assert.False(mockAssert.WithinDuration(a, b, -9*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
assert.False(mockAssert.WithinDuration(b, a, -9*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
|
|
||||||
assert.False(mockAssert.WithinDuration(a, b, -11*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
assert.False(mockAssert.WithinDuration(b, a, -11*time.Second), "A 10s difference is not within a 9s time difference")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInDeltaWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
True(t, assert.InDelta(1.001, 1, 0.01), "|1.001 - 1| <= 0.01")
|
|
||||||
True(t, assert.InDelta(1, 1.001, 0.01), "|1 - 1.001| <= 0.01")
|
|
||||||
True(t, assert.InDelta(1, 2, 1), "|1 - 2| <= 1")
|
|
||||||
False(t, assert.InDelta(1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail")
|
|
||||||
False(t, assert.InDelta(2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail")
|
|
||||||
False(t, assert.InDelta("", nil, 1), "Expected non numerals to fail")
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
a, b interface{}
|
|
||||||
delta float64
|
|
||||||
}{
|
|
||||||
{uint8(2), uint8(1), 1},
|
|
||||||
{uint16(2), uint16(1), 1},
|
|
||||||
{uint32(2), uint32(1), 1},
|
|
||||||
{uint64(2), uint64(1), 1},
|
|
||||||
|
|
||||||
{int(2), int(1), 1},
|
|
||||||
{int8(2), int8(1), 1},
|
|
||||||
{int16(2), int16(1), 1},
|
|
||||||
{int32(2), int32(1), 1},
|
|
||||||
{int64(2), int64(1), 1},
|
|
||||||
|
|
||||||
{float32(2), float32(1), 1},
|
|
||||||
{float64(2), float64(1), 1},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
True(t, assert.InDelta(tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInEpsilonWrapper(t *testing.T) {
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
a, b interface{}
|
|
||||||
epsilon float64
|
|
||||||
}{
|
|
||||||
{uint8(2), uint16(2), .001},
|
|
||||||
{2.1, 2.2, 0.1},
|
|
||||||
{2.2, 2.1, 0.1},
|
|
||||||
{-2.1, -2.2, 0.1},
|
|
||||||
{-2.2, -2.1, 0.1},
|
|
||||||
{uint64(100), uint8(101), 0.01},
|
|
||||||
{0.1, -0.1, 2},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
True(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon))
|
|
||||||
}
|
|
||||||
|
|
||||||
cases = []struct {
|
|
||||||
a, b interface{}
|
|
||||||
epsilon float64
|
|
||||||
}{
|
|
||||||
{uint8(2), int16(-2), .001},
|
|
||||||
{uint64(100), uint8(102), 0.01},
|
|
||||||
{2.1, 2.2, 0.001},
|
|
||||||
{2.2, 2.1, 0.001},
|
|
||||||
{2.1, -2.2, 1},
|
|
||||||
{2.1, "bla-bla", 0},
|
|
||||||
{0.1, -0.1, 1.99},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
False(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRegexpWrapper(t *testing.T) {
|
|
||||||
|
|
||||||
assert := New(new(testing.T))
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
rx, str string
|
|
||||||
}{
|
|
||||||
{"^start", "start of the line"},
|
|
||||||
{"end$", "in the end"},
|
|
||||||
{"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
True(t, assert.Regexp(tc.rx, tc.str))
|
|
||||||
True(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str))
|
|
||||||
False(t, assert.NotRegexp(tc.rx, tc.str))
|
|
||||||
False(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str))
|
|
||||||
}
|
|
||||||
|
|
||||||
cases = []struct {
|
|
||||||
rx, str string
|
|
||||||
}{
|
|
||||||
{"^asdfastart", "Not the start of the line"},
|
|
||||||
{"end$", "in the end."},
|
|
||||||
{"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
False(t, assert.Regexp(tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str)
|
|
||||||
False(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str))
|
|
||||||
True(t, assert.NotRegexp(tc.rx, tc.str))
|
|
||||||
True(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str))
|
|
||||||
}
|
|
||||||
}
|
|
157
Godeps/_workspace/src/github.com/stretchr/testify/assert/http_assertions.go
generated
vendored
157
Godeps/_workspace/src/github.com/stretchr/testify/assert/http_assertions.go
generated
vendored
@ -1,157 +0,0 @@
|
|||||||
package assert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// httpCode is a helper that returns HTTP code of the response. It returns -1
|
|
||||||
// if building a new request fails.
|
|
||||||
func httpCode(handler http.HandlerFunc, mode, url string, values url.Values) int {
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest(mode, url+"?"+values.Encode(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
handler(w, req)
|
|
||||||
return w.Code
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPSuccess asserts that a specified handler returns a success status code.
|
|
||||||
//
|
|
||||||
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func HTTPSuccess(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values) bool {
|
|
||||||
code := httpCode(handler, mode, url, values)
|
|
||||||
if code == -1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return code >= http.StatusOK && code <= http.StatusPartialContent
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPRedirect asserts that a specified handler returns a redirect status code.
|
|
||||||
//
|
|
||||||
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func HTTPRedirect(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values) bool {
|
|
||||||
code := httpCode(handler, mode, url, values)
|
|
||||||
if code == -1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPError asserts that a specified handler returns an error status code.
|
|
||||||
//
|
|
||||||
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func HTTPError(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values) bool {
|
|
||||||
code := httpCode(handler, mode, url, values)
|
|
||||||
if code == -1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return code >= http.StatusBadRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
// HttpBody is a helper that returns HTTP body of the response. It returns
|
|
||||||
// empty string if building a new request fails.
|
|
||||||
func HttpBody(handler http.HandlerFunc, mode, url string, values url.Values) string {
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest(mode, url+"?"+values.Encode(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
handler(w, req)
|
|
||||||
return w.Body.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPBodyContains asserts that a specified handler returns a
|
|
||||||
// body that contains a string.
|
|
||||||
//
|
|
||||||
// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values, str interface{}) bool {
|
|
||||||
body := HttpBody(handler, mode, url, values)
|
|
||||||
|
|
||||||
contains := strings.Contains(body, fmt.Sprint(str))
|
|
||||||
if !contains {
|
|
||||||
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
|
|
||||||
}
|
|
||||||
|
|
||||||
return contains
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPBodyNotContains asserts that a specified handler returns a
|
|
||||||
// body that does not contain a string.
|
|
||||||
//
|
|
||||||
// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values, str interface{}) bool {
|
|
||||||
body := HttpBody(handler, mode, url, values)
|
|
||||||
|
|
||||||
contains := strings.Contains(body, fmt.Sprint(str))
|
|
||||||
if contains {
|
|
||||||
Fail(t, "Expected response body for %s to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
return !contains
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Assertions Wrappers
|
|
||||||
//
|
|
||||||
|
|
||||||
// HTTPSuccess asserts that a specified handler returns a success status code.
|
|
||||||
//
|
|
||||||
// assert.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, mode, url string, values url.Values) bool {
|
|
||||||
return HTTPSuccess(a.t, handler, mode, url, values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPRedirect asserts that a specified handler returns a redirect status code.
|
|
||||||
//
|
|
||||||
// assert.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, mode, url string, values url.Values) bool {
|
|
||||||
return HTTPRedirect(a.t, handler, mode, url, values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPError asserts that a specified handler returns an error status code.
|
|
||||||
//
|
|
||||||
// assert.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) HTTPError(handler http.HandlerFunc, mode, url string, values url.Values) bool {
|
|
||||||
return HTTPError(a.t, handler, mode, url, values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPBodyContains asserts that a specified handler returns a
|
|
||||||
// body that contains a string.
|
|
||||||
//
|
|
||||||
// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, mode, url string, values url.Values, str interface{}) bool {
|
|
||||||
return HTTPBodyContains(a.t, handler, mode, url, values, str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPBodyNotContains asserts that a specified handler returns a
|
|
||||||
// body that does not contain a string.
|
|
||||||
//
|
|
||||||
// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, mode, url string, values url.Values, str interface{}) bool {
|
|
||||||
return HTTPBodyNotContains(a.t, handler, mode, url, values, str)
|
|
||||||
}
|
|
86
Godeps/_workspace/src/github.com/stretchr/testify/assert/http_assertions_test.go
generated
vendored
86
Godeps/_workspace/src/github.com/stretchr/testify/assert/http_assertions_test.go
generated
vendored
@ -1,86 +0,0 @@
|
|||||||
package assert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func httpOK(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
func httpRedirect(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusTemporaryRedirect)
|
|
||||||
}
|
|
||||||
|
|
||||||
func httpError(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHTTPStatuses(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
assert.Equal(HTTPSuccess(mockT, httpOK, "GET", "/", nil), true)
|
|
||||||
assert.Equal(HTTPSuccess(mockT, httpRedirect, "GET", "/", nil), false)
|
|
||||||
assert.Equal(HTTPSuccess(mockT, httpError, "GET", "/", nil), false)
|
|
||||||
|
|
||||||
assert.Equal(HTTPRedirect(mockT, httpOK, "GET", "/", nil), false)
|
|
||||||
assert.Equal(HTTPRedirect(mockT, httpRedirect, "GET", "/", nil), true)
|
|
||||||
assert.Equal(HTTPRedirect(mockT, httpError, "GET", "/", nil), false)
|
|
||||||
|
|
||||||
assert.Equal(HTTPError(mockT, httpOK, "GET", "/", nil), false)
|
|
||||||
assert.Equal(HTTPError(mockT, httpRedirect, "GET", "/", nil), false)
|
|
||||||
assert.Equal(HTTPError(mockT, httpError, "GET", "/", nil), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHTTPStatusesWrapper(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockAssert := New(new(testing.T))
|
|
||||||
|
|
||||||
assert.Equal(mockAssert.HTTPSuccess(httpOK, "GET", "/", nil), true)
|
|
||||||
assert.Equal(mockAssert.HTTPSuccess(httpRedirect, "GET", "/", nil), false)
|
|
||||||
assert.Equal(mockAssert.HTTPSuccess(httpError, "GET", "/", nil), false)
|
|
||||||
|
|
||||||
assert.Equal(mockAssert.HTTPRedirect(httpOK, "GET", "/", nil), false)
|
|
||||||
assert.Equal(mockAssert.HTTPRedirect(httpRedirect, "GET", "/", nil), true)
|
|
||||||
assert.Equal(mockAssert.HTTPRedirect(httpError, "GET", "/", nil), false)
|
|
||||||
|
|
||||||
assert.Equal(mockAssert.HTTPError(httpOK, "GET", "/", nil), false)
|
|
||||||
assert.Equal(mockAssert.HTTPError(httpRedirect, "GET", "/", nil), false)
|
|
||||||
assert.Equal(mockAssert.HTTPError(httpError, "GET", "/", nil), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func httpHelloName(w http.ResponseWriter, r *http.Request) {
|
|
||||||
name := r.FormValue("name")
|
|
||||||
w.Write([]byte(fmt.Sprintf("Hello, %s!", name)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHttpBody(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockT := new(testing.T)
|
|
||||||
|
|
||||||
assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
|
|
||||||
assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
|
|
||||||
assert.False(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
|
|
||||||
|
|
||||||
assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
|
|
||||||
assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
|
|
||||||
assert.True(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHttpBodyWrappers(t *testing.T) {
|
|
||||||
assert := New(t)
|
|
||||||
mockAssert := New(new(testing.T))
|
|
||||||
|
|
||||||
assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
|
|
||||||
assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
|
|
||||||
assert.False(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
|
|
||||||
|
|
||||||
assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
|
|
||||||
assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
|
|
||||||
assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
|
|
||||||
|
|
||||||
}
|
|
43
Godeps/_workspace/src/github.com/stretchr/testify/mock/doc.go
generated
vendored
43
Godeps/_workspace/src/github.com/stretchr/testify/mock/doc.go
generated
vendored
@ -1,43 +0,0 @@
|
|||||||
// Provides a system by which it is possible to mock your objects and verify calls are happening as expected.
|
|
||||||
//
|
|
||||||
// Example Usage
|
|
||||||
//
|
|
||||||
// The mock package provides an object, Mock, that tracks activity on another object. It is usually
|
|
||||||
// embedded into a test object as shown below:
|
|
||||||
//
|
|
||||||
// type MyTestObject struct {
|
|
||||||
// // add a Mock object instance
|
|
||||||
// mock.Mock
|
|
||||||
//
|
|
||||||
// // other fields go here as normal
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// When implementing the methods of an interface, you wire your functions up
|
|
||||||
// to call the Mock.Called(args...) method, and return the appropriate values.
|
|
||||||
//
|
|
||||||
// For example, to mock a method that saves the name and age of a person and returns
|
|
||||||
// the year of their birth or an error, you might write this:
|
|
||||||
//
|
|
||||||
// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) {
|
|
||||||
// args := o.Called(firstname, lastname, age)
|
|
||||||
// return args.Int(0), args.Error(1)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// The Int, Error and Bool methods are examples of strongly typed getters that take the argument
|
|
||||||
// index position. Given this argument list:
|
|
||||||
//
|
|
||||||
// (12, true, "Something")
|
|
||||||
//
|
|
||||||
// You could read them out strongly typed like this:
|
|
||||||
//
|
|
||||||
// args.Int(0)
|
|
||||||
// args.Bool(1)
|
|
||||||
// args.String(2)
|
|
||||||
//
|
|
||||||
// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion:
|
|
||||||
//
|
|
||||||
// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine)
|
|
||||||
//
|
|
||||||
// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those
|
|
||||||
// cases you should check for nil first.
|
|
||||||
package mock
|
|
510
Godeps/_workspace/src/github.com/stretchr/testify/mock/mock.go
generated
vendored
510
Godeps/_workspace/src/github.com/stretchr/testify/mock/mock.go
generated
vendored
@ -1,510 +0,0 @@
|
|||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/stretchr/objx"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestingT is an interface wrapper around *testing.T
|
|
||||||
type TestingT interface {
|
|
||||||
Logf(format string, args ...interface{})
|
|
||||||
Errorf(format string, args ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Call
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Call represents a method call and is used for setting expectations,
|
|
||||||
// as well as recording activity.
|
|
||||||
type Call struct {
|
|
||||||
|
|
||||||
// The name of the method that was or will be called.
|
|
||||||
Method string
|
|
||||||
|
|
||||||
// Holds the arguments of the method.
|
|
||||||
Arguments Arguments
|
|
||||||
|
|
||||||
// Holds the arguments that should be returned when
|
|
||||||
// this method is called.
|
|
||||||
ReturnArguments Arguments
|
|
||||||
|
|
||||||
// The number of times to return the return arguments when setting
|
|
||||||
// expectations. 0 means to always return the value.
|
|
||||||
Repeatability int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock is the workhorse used to track activity on another object.
|
|
||||||
// For an example of its usage, refer to the "Example Usage" section at the top of this document.
|
|
||||||
type Mock struct {
|
|
||||||
|
|
||||||
// The method name that is currently
|
|
||||||
// being referred to by the On method.
|
|
||||||
onMethodName string
|
|
||||||
|
|
||||||
// An array of the arguments that are
|
|
||||||
// currently being referred to by the On method.
|
|
||||||
onMethodArguments Arguments
|
|
||||||
|
|
||||||
// Represents the calls that are expected of
|
|
||||||
// an object.
|
|
||||||
ExpectedCalls []Call
|
|
||||||
|
|
||||||
// Holds the calls that were made to this mocked object.
|
|
||||||
Calls []Call
|
|
||||||
|
|
||||||
// TestData holds any data that might be useful for testing. Testify ignores
|
|
||||||
// this data completely allowing you to do whatever you like with it.
|
|
||||||
testData objx.Map
|
|
||||||
|
|
||||||
mutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestData holds any data that might be useful for testing. Testify ignores
|
|
||||||
// this data completely allowing you to do whatever you like with it.
|
|
||||||
func (m *Mock) TestData() objx.Map {
|
|
||||||
|
|
||||||
if m.testData == nil {
|
|
||||||
m.testData = make(objx.Map)
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.testData
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Setting expectations
|
|
||||||
*/
|
|
||||||
|
|
||||||
// On starts a description of an expectation of the specified method
|
|
||||||
// being called.
|
|
||||||
//
|
|
||||||
// Mock.On("MyMethod", arg1, arg2)
|
|
||||||
func (m *Mock) On(methodName string, arguments ...interface{}) *Mock {
|
|
||||||
m.onMethodName = methodName
|
|
||||||
m.onMethodArguments = arguments
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return finishes a description of an expectation of the method (and arguments)
|
|
||||||
// specified in the most recent On method call.
|
|
||||||
//
|
|
||||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2)
|
|
||||||
func (m *Mock) Return(returnArguments ...interface{}) *Mock {
|
|
||||||
m.ExpectedCalls = append(m.ExpectedCalls, Call{m.onMethodName, m.onMethodArguments, returnArguments, 0})
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once indicates that that the mock should only return the value once.
|
|
||||||
//
|
|
||||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
|
|
||||||
func (m *Mock) Once() {
|
|
||||||
m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Twice indicates that that the mock should only return the value twice.
|
|
||||||
//
|
|
||||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
|
|
||||||
func (m *Mock) Twice() {
|
|
||||||
m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// Times indicates that that the mock should only return the indicated number
|
|
||||||
// of times.
|
|
||||||
//
|
|
||||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
|
|
||||||
func (m *Mock) Times(i int) {
|
|
||||||
m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = i
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Recording and responding to activity
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) {
|
|
||||||
for i, call := range m.ExpectedCalls {
|
|
||||||
if call.Method == method && call.Repeatability > -1 {
|
|
||||||
|
|
||||||
_, diffCount := call.Arguments.Diff(arguments)
|
|
||||||
if diffCount == 0 {
|
|
||||||
return i, &call
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Mock) findClosestCall(method string, arguments ...interface{}) (bool, *Call) {
|
|
||||||
|
|
||||||
diffCount := 0
|
|
||||||
var closestCall *Call = nil
|
|
||||||
|
|
||||||
for _, call := range m.ExpectedCalls {
|
|
||||||
if call.Method == method {
|
|
||||||
|
|
||||||
_, tempDiffCount := call.Arguments.Diff(arguments)
|
|
||||||
if tempDiffCount < diffCount || diffCount == 0 {
|
|
||||||
diffCount = tempDiffCount
|
|
||||||
closestCall = &call
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if closestCall == nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, closestCall
|
|
||||||
}
|
|
||||||
|
|
||||||
func callString(method string, arguments Arguments, includeArgumentValues bool) string {
|
|
||||||
|
|
||||||
var argValsString string = ""
|
|
||||||
if includeArgumentValues {
|
|
||||||
var argVals []string
|
|
||||||
for argIndex, arg := range arguments {
|
|
||||||
argVals = append(argVals, fmt.Sprintf("%d: %v", argIndex, arg))
|
|
||||||
}
|
|
||||||
argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called tells the mock object that a method has been called, and gets an array
|
|
||||||
// of arguments to return. Panics if the call is unexpected (i.e. not preceeded by
|
|
||||||
// appropriate .On .Return() calls)
|
|
||||||
func (m *Mock) Called(arguments ...interface{}) Arguments {
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
m.mutex.Lock()
|
|
||||||
|
|
||||||
// get the calling function's name
|
|
||||||
pc, _, _, ok := runtime.Caller(1)
|
|
||||||
if !ok {
|
|
||||||
panic("Couldn't get the caller information")
|
|
||||||
}
|
|
||||||
functionPath := runtime.FuncForPC(pc).Name()
|
|
||||||
parts := strings.Split(functionPath, ".")
|
|
||||||
functionName := parts[len(parts)-1]
|
|
||||||
|
|
||||||
found, call := m.findExpectedCall(functionName, arguments...)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case found < 0:
|
|
||||||
// we have to fail here - because we don't know what to do
|
|
||||||
// as the return arguments. This is because:
|
|
||||||
//
|
|
||||||
// a) this is a totally unexpected call to this method,
|
|
||||||
// b) the arguments are not what was expected, or
|
|
||||||
// c) the developer has forgotten to add an accompanying On...Return pair.
|
|
||||||
|
|
||||||
closestFound, closestCall := m.findClosestCall(functionName, arguments...)
|
|
||||||
|
|
||||||
if closestFound {
|
|
||||||
panic(fmt.Sprintf("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n", callString(functionName, arguments, true), callString(functionName, closestCall.Arguments, true)))
|
|
||||||
} else {
|
|
||||||
panic(fmt.Sprintf("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", functionName, functionName, callString(functionName, arguments, true), assert.CallerInfo()))
|
|
||||||
}
|
|
||||||
case call.Repeatability == 1:
|
|
||||||
call.Repeatability = -1
|
|
||||||
m.ExpectedCalls[found] = *call
|
|
||||||
case call.Repeatability > 1:
|
|
||||||
call.Repeatability -= 1
|
|
||||||
m.ExpectedCalls[found] = *call
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the call
|
|
||||||
m.Calls = append(m.Calls, Call{functionName, arguments, make([]interface{}, 0), 0})
|
|
||||||
|
|
||||||
return call.ReturnArguments
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Assertions
|
|
||||||
*/
|
|
||||||
|
|
||||||
// AssertExpectationsForObjects asserts that everything specified with On and Return
|
|
||||||
// of the specified objects was in fact called as expected.
|
|
||||||
//
|
|
||||||
// Calls may have occurred in any order.
|
|
||||||
func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
|
|
||||||
var success bool = true
|
|
||||||
for _, obj := range testObjects {
|
|
||||||
mockObj := obj.(Mock)
|
|
||||||
success = success && mockObj.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertExpectations asserts that everything specified with On and Return was
|
|
||||||
// in fact called as expected. Calls may have occurred in any order.
|
|
||||||
func (m *Mock) AssertExpectations(t TestingT) bool {
|
|
||||||
|
|
||||||
var somethingMissing bool = false
|
|
||||||
var failedExpectations int = 0
|
|
||||||
|
|
||||||
// iterate through each expectation
|
|
||||||
for _, expectedCall := range m.ExpectedCalls {
|
|
||||||
switch {
|
|
||||||
case !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments):
|
|
||||||
somethingMissing = true
|
|
||||||
failedExpectations++
|
|
||||||
t.Logf("\u274C\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String())
|
|
||||||
case expectedCall.Repeatability > 0:
|
|
||||||
somethingMissing = true
|
|
||||||
failedExpectations++
|
|
||||||
default:
|
|
||||||
t.Logf("\u2705\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if somethingMissing {
|
|
||||||
t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(m.ExpectedCalls)-failedExpectations, len(m.ExpectedCalls), failedExpectations, assert.CallerInfo())
|
|
||||||
}
|
|
||||||
|
|
||||||
return !somethingMissing
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertNumberOfCalls asserts that the method was called expectedCalls times.
|
|
||||||
func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool {
|
|
||||||
var actualCalls int = 0
|
|
||||||
for _, call := range m.Calls {
|
|
||||||
if call.Method == methodName {
|
|
||||||
actualCalls++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return assert.Equal(t, actualCalls, expectedCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertCalled asserts that the method was called.
|
|
||||||
func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool {
|
|
||||||
if !assert.True(t, m.methodWasCalled(methodName, arguments), fmt.Sprintf("The \"%s\" method should have been called with %d argument(s), but was not.", methodName, len(arguments))) {
|
|
||||||
t.Logf("%s", m.ExpectedCalls)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertNotCalled asserts that the method was not called.
|
|
||||||
func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool {
|
|
||||||
if !assert.False(t, m.methodWasCalled(methodName, arguments), fmt.Sprintf("The \"%s\" method was called with %d argument(s), but should NOT have been.", methodName, len(arguments))) {
|
|
||||||
t.Logf("%s", m.ExpectedCalls)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool {
|
|
||||||
for _, call := range m.Calls {
|
|
||||||
if call.Method == methodName {
|
|
||||||
|
|
||||||
_, differences := Arguments(expected).Diff(call.Arguments)
|
|
||||||
|
|
||||||
if differences == 0 {
|
|
||||||
// found the expected call
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// we didn't find the expected call
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Arguments
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Arguments holds an array of method arguments or return values.
|
|
||||||
type Arguments []interface{}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// The "any" argument. Used in Diff and Assert when
|
|
||||||
// the argument being tested shouldn't be taken into consideration.
|
|
||||||
Anything string = "mock.Anything"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AnythingOfTypeArgument is a string that contains the type of an argument
|
|
||||||
// for use when type checking. Used in Diff and Assert.
|
|
||||||
type AnythingOfTypeArgument string
|
|
||||||
|
|
||||||
// AnythingOfType returns an AnythingOfTypeArgument object containing the
|
|
||||||
// name of the type to check for. Used in Diff and Assert.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// Assert(t, AnythingOfType("string"), AnythingOfType("int"))
|
|
||||||
func AnythingOfType(t string) AnythingOfTypeArgument {
|
|
||||||
return AnythingOfTypeArgument(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Returns the argument at the specified index.
|
|
||||||
func (args Arguments) Get(index int) interface{} {
|
|
||||||
if index+1 > len(args) {
|
|
||||||
panic(fmt.Sprintf("assert: arguments: Cannot call Get(%d) because there are %d argument(s).", index, len(args)))
|
|
||||||
}
|
|
||||||
return args[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is gets whether the objects match the arguments specified.
|
|
||||||
func (args Arguments) Is(objects ...interface{}) bool {
|
|
||||||
for i, obj := range args {
|
|
||||||
if obj != objects[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Diff gets a string describing the differences between the arguments
|
|
||||||
// and the specified objects.
|
|
||||||
//
|
|
||||||
// Returns the diff string and number of differences found.
|
|
||||||
func (args Arguments) Diff(objects []interface{}) (string, int) {
|
|
||||||
|
|
||||||
var output string = "\n"
|
|
||||||
var differences int
|
|
||||||
|
|
||||||
var maxArgCount int = len(args)
|
|
||||||
if len(objects) > maxArgCount {
|
|
||||||
maxArgCount = len(objects)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < maxArgCount; i++ {
|
|
||||||
var actual, expected interface{}
|
|
||||||
|
|
||||||
if len(objects) <= i {
|
|
||||||
actual = "(Missing)"
|
|
||||||
} else {
|
|
||||||
actual = objects[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) <= i {
|
|
||||||
expected = "(Missing)"
|
|
||||||
} else {
|
|
||||||
expected = args[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() {
|
|
||||||
|
|
||||||
// type checking
|
|
||||||
if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) {
|
|
||||||
// not match
|
|
||||||
differences++
|
|
||||||
output = fmt.Sprintf("%s\t%d: \u274C type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// normal checking
|
|
||||||
|
|
||||||
if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) {
|
|
||||||
// match
|
|
||||||
output = fmt.Sprintf("%s\t%d: \u2705 %s == %s\n", output, i, actual, expected)
|
|
||||||
} else {
|
|
||||||
// not match
|
|
||||||
differences++
|
|
||||||
output = fmt.Sprintf("%s\t%d: \u274C %s != %s\n", output, i, actual, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if differences == 0 {
|
|
||||||
return "No differences.", differences
|
|
||||||
}
|
|
||||||
|
|
||||||
return output, differences
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert compares the arguments with the specified objects and fails if
|
|
||||||
// they do not exactly match.
|
|
||||||
func (args Arguments) Assert(t TestingT, objects ...interface{}) bool {
|
|
||||||
|
|
||||||
// get the differences
|
|
||||||
diff, diffCount := args.Diff(objects)
|
|
||||||
|
|
||||||
if diffCount == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// there are differences... report them...
|
|
||||||
t.Logf(diff)
|
|
||||||
t.Errorf("%sArguments do not match.", assert.CallerInfo())
|
|
||||||
|
|
||||||
return false
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// String gets the argument at the specified index. Panics if there is no argument, or
|
|
||||||
// if the argument is of the wrong type.
|
|
||||||
//
|
|
||||||
// If no index is provided, String() returns a complete string representation
|
|
||||||
// of the arguments.
|
|
||||||
func (args Arguments) String(indexOrNil ...int) string {
|
|
||||||
|
|
||||||
if len(indexOrNil) == 0 {
|
|
||||||
// normal String() method - return a string representation of the args
|
|
||||||
var argsStr []string
|
|
||||||
for _, arg := range args {
|
|
||||||
argsStr = append(argsStr, fmt.Sprintf("%s", reflect.TypeOf(arg)))
|
|
||||||
}
|
|
||||||
return strings.Join(argsStr, ",")
|
|
||||||
} else if len(indexOrNil) == 1 {
|
|
||||||
// Index has been specified - get the argument at that index
|
|
||||||
var index int = indexOrNil[0]
|
|
||||||
var s string
|
|
||||||
var ok bool
|
|
||||||
if s, ok = args.Get(index).(string); !ok {
|
|
||||||
panic(fmt.Sprintf("assert: arguments: String(%d) failed because object wasn't correct type: %s", index, args.Get(index)))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String. Must be 0 or 1, not %d", len(indexOrNil)))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int gets the argument at the specified index. Panics if there is no argument, or
|
|
||||||
// if the argument is of the wrong type.
|
|
||||||
func (args Arguments) Int(index int) int {
|
|
||||||
var s int
|
|
||||||
var ok bool
|
|
||||||
if s, ok = args.Get(index).(int); !ok {
|
|
||||||
panic(fmt.Sprintf("assert: arguments: Int(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error gets the argument at the specified index. Panics if there is no argument, or
|
|
||||||
// if the argument is of the wrong type.
|
|
||||||
func (args Arguments) Error(index int) error {
|
|
||||||
obj := args.Get(index)
|
|
||||||
var s error
|
|
||||||
var ok bool
|
|
||||||
if obj == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if s, ok = obj.(error); !ok {
|
|
||||||
panic(fmt.Sprintf("assert: arguments: Error(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool gets the argument at the specified index. Panics if there is no argument, or
|
|
||||||
// if the argument is of the wrong type.
|
|
||||||
func (args Arguments) Bool(index int) bool {
|
|
||||||
var s bool
|
|
||||||
var ok bool
|
|
||||||
if s, ok = args.Get(index).(bool); !ok {
|
|
||||||
panic(fmt.Sprintf("assert: arguments: Bool(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
669
Godeps/_workspace/src/github.com/stretchr/testify/mock/mock_test.go
generated
vendored
669
Godeps/_workspace/src/github.com/stretchr/testify/mock/mock_test.go
generated
vendored
@ -1,669 +0,0 @@
|
|||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Test objects
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ExampleInterface represents an example interface.
|
|
||||||
type ExampleInterface interface {
|
|
||||||
TheExampleMethod(a, b, c int) (int, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestExampleImplementation is a test implementation of ExampleInterface
|
|
||||||
type TestExampleImplementation struct {
|
|
||||||
Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *TestExampleImplementation) TheExampleMethod(a, b, c int) (int, error) {
|
|
||||||
args := i.Called(a, b, c)
|
|
||||||
return args.Int(0), errors.New("Whoops")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *TestExampleImplementation) TheExampleMethod2(yesorno bool) {
|
|
||||||
i.Called(yesorno)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExampleType struct{}
|
|
||||||
|
|
||||||
func (i *TestExampleImplementation) TheExampleMethod3(et *ExampleType) error {
|
|
||||||
args := i.Called(et)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Mock
|
|
||||||
*/
|
|
||||||
|
|
||||||
func Test_Mock_TestData(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
if assert.NotNil(t, mockedService.TestData()) {
|
|
||||||
|
|
||||||
mockedService.TestData().Set("something", 123)
|
|
||||||
assert.Equal(t, 123, mockedService.TestData().Get("something").Data())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_On(t *testing.T) {
|
|
||||||
|
|
||||||
// make a test impl object
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
assert.Equal(t, mockedService.On("TheExampleMethod"), &mockedService.Mock)
|
|
||||||
assert.Equal(t, "TheExampleMethod", mockedService.onMethodName)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_On_WithArgs(t *testing.T) {
|
|
||||||
|
|
||||||
// make a test impl object
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
assert.Equal(t, mockedService.On("TheExampleMethod", 1, 2, 3), &mockedService.Mock)
|
|
||||||
assert.Equal(t, "TheExampleMethod", mockedService.onMethodName)
|
|
||||||
assert.Equal(t, 1, mockedService.onMethodArguments[0])
|
|
||||||
assert.Equal(t, 2, mockedService.onMethodArguments[1])
|
|
||||||
assert.Equal(t, 3, mockedService.onMethodArguments[2])
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_Return(t *testing.T) {
|
|
||||||
|
|
||||||
// make a test impl object
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
assert.Equal(t, mockedService.On("TheExampleMethod", "A", "B", true).Return(1, "two", true), &mockedService.Mock)
|
|
||||||
|
|
||||||
// ensure the call was created
|
|
||||||
if assert.Equal(t, 1, len(mockedService.ExpectedCalls)) {
|
|
||||||
call := mockedService.ExpectedCalls[0]
|
|
||||||
|
|
||||||
assert.Equal(t, "TheExampleMethod", call.Method)
|
|
||||||
assert.Equal(t, "A", call.Arguments[0])
|
|
||||||
assert.Equal(t, "B", call.Arguments[1])
|
|
||||||
assert.Equal(t, true, call.Arguments[2])
|
|
||||||
assert.Equal(t, 1, call.ReturnArguments[0])
|
|
||||||
assert.Equal(t, "two", call.ReturnArguments[1])
|
|
||||||
assert.Equal(t, true, call.ReturnArguments[2])
|
|
||||||
assert.Equal(t, 0, call.Repeatability)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_Return_Once(t *testing.T) {
|
|
||||||
|
|
||||||
// make a test impl object
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("TheExampleMethod", "A", "B", true).Return(1, "two", true).Once()
|
|
||||||
|
|
||||||
// ensure the call was created
|
|
||||||
if assert.Equal(t, 1, len(mockedService.ExpectedCalls)) {
|
|
||||||
call := mockedService.ExpectedCalls[0]
|
|
||||||
|
|
||||||
assert.Equal(t, "TheExampleMethod", call.Method)
|
|
||||||
assert.Equal(t, "A", call.Arguments[0])
|
|
||||||
assert.Equal(t, "B", call.Arguments[1])
|
|
||||||
assert.Equal(t, true, call.Arguments[2])
|
|
||||||
assert.Equal(t, 1, call.ReturnArguments[0])
|
|
||||||
assert.Equal(t, "two", call.ReturnArguments[1])
|
|
||||||
assert.Equal(t, true, call.ReturnArguments[2])
|
|
||||||
assert.Equal(t, 1, call.Repeatability)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_Return_Twice(t *testing.T) {
|
|
||||||
|
|
||||||
// make a test impl object
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("TheExampleMethod", "A", "B", true).Return(1, "two", true).Twice()
|
|
||||||
|
|
||||||
// ensure the call was created
|
|
||||||
if assert.Equal(t, 1, len(mockedService.ExpectedCalls)) {
|
|
||||||
call := mockedService.ExpectedCalls[0]
|
|
||||||
|
|
||||||
assert.Equal(t, "TheExampleMethod", call.Method)
|
|
||||||
assert.Equal(t, "A", call.Arguments[0])
|
|
||||||
assert.Equal(t, "B", call.Arguments[1])
|
|
||||||
assert.Equal(t, true, call.Arguments[2])
|
|
||||||
assert.Equal(t, 1, call.ReturnArguments[0])
|
|
||||||
assert.Equal(t, "two", call.ReturnArguments[1])
|
|
||||||
assert.Equal(t, true, call.ReturnArguments[2])
|
|
||||||
assert.Equal(t, 2, call.Repeatability)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_Return_Times(t *testing.T) {
|
|
||||||
|
|
||||||
// make a test impl object
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("TheExampleMethod", "A", "B", true).Return(1, "two", true).Times(5)
|
|
||||||
|
|
||||||
// ensure the call was created
|
|
||||||
if assert.Equal(t, 1, len(mockedService.ExpectedCalls)) {
|
|
||||||
call := mockedService.ExpectedCalls[0]
|
|
||||||
|
|
||||||
assert.Equal(t, "TheExampleMethod", call.Method)
|
|
||||||
assert.Equal(t, "A", call.Arguments[0])
|
|
||||||
assert.Equal(t, "B", call.Arguments[1])
|
|
||||||
assert.Equal(t, true, call.Arguments[2])
|
|
||||||
assert.Equal(t, 1, call.ReturnArguments[0])
|
|
||||||
assert.Equal(t, "two", call.ReturnArguments[1])
|
|
||||||
assert.Equal(t, true, call.ReturnArguments[2])
|
|
||||||
assert.Equal(t, 5, call.Repeatability)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_Return_Nothing(t *testing.T) {
|
|
||||||
|
|
||||||
// make a test impl object
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
assert.Equal(t, mockedService.On("TheExampleMethod", "A", "B", true).Return(), &mockedService.Mock)
|
|
||||||
|
|
||||||
// ensure the call was created
|
|
||||||
if assert.Equal(t, 1, len(mockedService.ExpectedCalls)) {
|
|
||||||
call := mockedService.ExpectedCalls[0]
|
|
||||||
|
|
||||||
assert.Equal(t, "TheExampleMethod", call.Method)
|
|
||||||
assert.Equal(t, "A", call.Arguments[0])
|
|
||||||
assert.Equal(t, "B", call.Arguments[1])
|
|
||||||
assert.Equal(t, true, call.Arguments[2])
|
|
||||||
assert.Equal(t, 0, len(call.ReturnArguments))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_findExpectedCall(t *testing.T) {
|
|
||||||
|
|
||||||
m := new(Mock)
|
|
||||||
m.On("One", 1).Return("one")
|
|
||||||
m.On("Two", 2).Return("two")
|
|
||||||
m.On("Two", 3).Return("three")
|
|
||||||
|
|
||||||
f, c := m.findExpectedCall("Two", 3)
|
|
||||||
|
|
||||||
if assert.Equal(t, 2, f) {
|
|
||||||
if assert.NotNil(t, c) {
|
|
||||||
assert.Equal(t, "Two", c.Method)
|
|
||||||
assert.Equal(t, 3, c.Arguments[0])
|
|
||||||
assert.Equal(t, "three", c.ReturnArguments[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_findExpectedCall_For_Unknown_Method(t *testing.T) {
|
|
||||||
|
|
||||||
m := new(Mock)
|
|
||||||
m.On("One", 1).Return("one")
|
|
||||||
m.On("Two", 2).Return("two")
|
|
||||||
m.On("Two", 3).Return("three")
|
|
||||||
|
|
||||||
f, _ := m.findExpectedCall("Two")
|
|
||||||
|
|
||||||
assert.Equal(t, -1, f)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_findExpectedCall_Respects_Repeatability(t *testing.T) {
|
|
||||||
|
|
||||||
m := new(Mock)
|
|
||||||
m.On("One", 1).Return("one")
|
|
||||||
m.On("Two", 2).Return("two").Once()
|
|
||||||
m.On("Two", 3).Return("three").Twice()
|
|
||||||
m.On("Two", 3).Return("three").Times(8)
|
|
||||||
|
|
||||||
f, c := m.findExpectedCall("Two", 3)
|
|
||||||
|
|
||||||
if assert.Equal(t, 2, f) {
|
|
||||||
if assert.NotNil(t, c) {
|
|
||||||
assert.Equal(t, "Two", c.Method)
|
|
||||||
assert.Equal(t, 3, c.Arguments[0])
|
|
||||||
assert.Equal(t, "three", c.ReturnArguments[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_callString(t *testing.T) {
|
|
||||||
|
|
||||||
assert.Equal(t, `Method(int,bool,string)`, callString("Method", []interface{}{1, true, "something"}, false))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_Called(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_Called", 1, 2, 3).Return(5, "6", true)
|
|
||||||
|
|
||||||
returnArguments := mockedService.Called(1, 2, 3)
|
|
||||||
|
|
||||||
if assert.Equal(t, 1, len(mockedService.Calls)) {
|
|
||||||
assert.Equal(t, "Test_Mock_Called", mockedService.Calls[0].Method)
|
|
||||||
assert.Equal(t, 1, mockedService.Calls[0].Arguments[0])
|
|
||||||
assert.Equal(t, 2, mockedService.Calls[0].Arguments[1])
|
|
||||||
assert.Equal(t, 3, mockedService.Calls[0].Arguments[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
if assert.Equal(t, 3, len(returnArguments)) {
|
|
||||||
assert.Equal(t, 5, returnArguments[0])
|
|
||||||
assert.Equal(t, "6", returnArguments[1])
|
|
||||||
assert.Equal(t, true, returnArguments[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_Called_For_Bounded_Repeatability(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_Called_For_Bounded_Repeatability", 1, 2, 3).Return(5, "6", true).Once()
|
|
||||||
mockedService.On("Test_Mock_Called_For_Bounded_Repeatability", 1, 2, 3).Return(-1, "hi", false)
|
|
||||||
|
|
||||||
returnArguments1 := mockedService.Called(1, 2, 3)
|
|
||||||
returnArguments2 := mockedService.Called(1, 2, 3)
|
|
||||||
|
|
||||||
if assert.Equal(t, 2, len(mockedService.Calls)) {
|
|
||||||
assert.Equal(t, "Test_Mock_Called_For_Bounded_Repeatability", mockedService.Calls[0].Method)
|
|
||||||
assert.Equal(t, 1, mockedService.Calls[0].Arguments[0])
|
|
||||||
assert.Equal(t, 2, mockedService.Calls[0].Arguments[1])
|
|
||||||
assert.Equal(t, 3, mockedService.Calls[0].Arguments[2])
|
|
||||||
|
|
||||||
assert.Equal(t, "Test_Mock_Called_For_Bounded_Repeatability", mockedService.Calls[1].Method)
|
|
||||||
assert.Equal(t, 1, mockedService.Calls[1].Arguments[0])
|
|
||||||
assert.Equal(t, 2, mockedService.Calls[1].Arguments[1])
|
|
||||||
assert.Equal(t, 3, mockedService.Calls[1].Arguments[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
if assert.Equal(t, 3, len(returnArguments1)) {
|
|
||||||
assert.Equal(t, 5, returnArguments1[0])
|
|
||||||
assert.Equal(t, "6", returnArguments1[1])
|
|
||||||
assert.Equal(t, true, returnArguments1[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
if assert.Equal(t, 3, len(returnArguments2)) {
|
|
||||||
assert.Equal(t, -1, returnArguments2[0])
|
|
||||||
assert.Equal(t, "hi", returnArguments2[1])
|
|
||||||
assert.Equal(t, false, returnArguments2[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_Called_For_SetTime_Expectation(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("TheExampleMethod", 1, 2, 3).Return(5, "6", true).Times(4)
|
|
||||||
|
|
||||||
mockedService.TheExampleMethod(1, 2, 3)
|
|
||||||
mockedService.TheExampleMethod(1, 2, 3)
|
|
||||||
mockedService.TheExampleMethod(1, 2, 3)
|
|
||||||
mockedService.TheExampleMethod(1, 2, 3)
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
mockedService.TheExampleMethod(1, 2, 3)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_Called_Unexpected(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
// make sure it panics if no expectation was made
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
mockedService.Called(1, 2, 3)
|
|
||||||
}, "Calling unexpected method should panic")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_AssertExpectationsForObjects_Helper(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService1 *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
var mockedService2 *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
var mockedService3 *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService1.On("Test_AssertExpectationsForObjects_Helper", 1).Return()
|
|
||||||
mockedService2.On("Test_AssertExpectationsForObjects_Helper", 2).Return()
|
|
||||||
mockedService3.On("Test_AssertExpectationsForObjects_Helper", 3).Return()
|
|
||||||
|
|
||||||
mockedService1.Called(1)
|
|
||||||
mockedService2.Called(2)
|
|
||||||
mockedService3.Called(3)
|
|
||||||
|
|
||||||
assert.True(t, AssertExpectationsForObjects(t, mockedService1.Mock, mockedService2.Mock, mockedService3.Mock))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_AssertExpectationsForObjects_Helper_Failed(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService1 *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
var mockedService2 *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
var mockedService3 *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService1.On("Test_AssertExpectationsForObjects_Helper_Failed", 1).Return()
|
|
||||||
mockedService2.On("Test_AssertExpectationsForObjects_Helper_Failed", 2).Return()
|
|
||||||
mockedService3.On("Test_AssertExpectationsForObjects_Helper_Failed", 3).Return()
|
|
||||||
|
|
||||||
mockedService1.Called(1)
|
|
||||||
mockedService3.Called(3)
|
|
||||||
|
|
||||||
tt := new(testing.T)
|
|
||||||
assert.False(t, AssertExpectationsForObjects(tt, mockedService1.Mock, mockedService2.Mock, mockedService3.Mock))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_AssertExpectations(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_AssertExpectations", 1, 2, 3).Return(5, 6, 7)
|
|
||||||
|
|
||||||
tt := new(testing.T)
|
|
||||||
assert.False(t, mockedService.AssertExpectations(tt))
|
|
||||||
|
|
||||||
// make the call now
|
|
||||||
mockedService.Called(1, 2, 3)
|
|
||||||
|
|
||||||
// now assert expectations
|
|
||||||
assert.True(t, mockedService.AssertExpectations(tt))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_AssertExpectationsCustomType(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("TheExampleMethod3", AnythingOfType("*mock.ExampleType")).Return(nil).Once()
|
|
||||||
|
|
||||||
tt := new(testing.T)
|
|
||||||
assert.False(t, mockedService.AssertExpectations(tt))
|
|
||||||
|
|
||||||
// make the call now
|
|
||||||
mockedService.TheExampleMethod3(&ExampleType{})
|
|
||||||
|
|
||||||
// now assert expectations
|
|
||||||
assert.True(t, mockedService.AssertExpectations(tt))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_AssertExpectations_With_Repeatability(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_AssertExpectations_With_Repeatability", 1, 2, 3).Return(5, 6, 7).Twice()
|
|
||||||
|
|
||||||
tt := new(testing.T)
|
|
||||||
assert.False(t, mockedService.AssertExpectations(tt))
|
|
||||||
|
|
||||||
// make the call now
|
|
||||||
mockedService.Called(1, 2, 3)
|
|
||||||
|
|
||||||
assert.False(t, mockedService.AssertExpectations(tt))
|
|
||||||
|
|
||||||
mockedService.Called(1, 2, 3)
|
|
||||||
|
|
||||||
// now assert expectations
|
|
||||||
assert.True(t, mockedService.AssertExpectations(tt))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_TwoCallsWithDifferentArguments(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_TwoCallsWithDifferentArguments", 1, 2, 3).Return(5, 6, 7)
|
|
||||||
mockedService.On("Test_Mock_TwoCallsWithDifferentArguments", 4, 5, 6).Return(5, 6, 7)
|
|
||||||
|
|
||||||
args1 := mockedService.Called(1, 2, 3)
|
|
||||||
assert.Equal(t, 5, args1.Int(0))
|
|
||||||
assert.Equal(t, 6, args1.Int(1))
|
|
||||||
assert.Equal(t, 7, args1.Int(2))
|
|
||||||
|
|
||||||
args2 := mockedService.Called(4, 5, 6)
|
|
||||||
assert.Equal(t, 5, args2.Int(0))
|
|
||||||
assert.Equal(t, 6, args2.Int(1))
|
|
||||||
assert.Equal(t, 7, args2.Int(2))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_AssertNumberOfCalls(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_AssertNumberOfCalls", 1, 2, 3).Return(5, 6, 7)
|
|
||||||
|
|
||||||
mockedService.Called(1, 2, 3)
|
|
||||||
assert.True(t, mockedService.AssertNumberOfCalls(t, "Test_Mock_AssertNumberOfCalls", 1))
|
|
||||||
|
|
||||||
mockedService.Called(1, 2, 3)
|
|
||||||
assert.True(t, mockedService.AssertNumberOfCalls(t, "Test_Mock_AssertNumberOfCalls", 2))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_AssertCalled(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_AssertCalled", 1, 2, 3).Return(5, 6, 7)
|
|
||||||
|
|
||||||
mockedService.Called(1, 2, 3)
|
|
||||||
|
|
||||||
assert.True(t, mockedService.AssertCalled(t, "Test_Mock_AssertCalled", 1, 2, 3))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_AssertCalled_WithAnythingOfTypeArgument(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_AssertCalled_WithAnythingOfTypeArgument", Anything, Anything, Anything).Return()
|
|
||||||
|
|
||||||
mockedService.Called(1, "two", []uint8("three"))
|
|
||||||
|
|
||||||
assert.True(t, mockedService.AssertCalled(t, "Test_Mock_AssertCalled_WithAnythingOfTypeArgument", AnythingOfType("int"), AnythingOfType("string"), AnythingOfType("[]uint8")))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_AssertCalled_WithArguments(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_AssertCalled_WithArguments", 1, 2, 3).Return(5, 6, 7)
|
|
||||||
|
|
||||||
mockedService.Called(1, 2, 3)
|
|
||||||
|
|
||||||
tt := new(testing.T)
|
|
||||||
assert.True(t, mockedService.AssertCalled(tt, "Test_Mock_AssertCalled_WithArguments", 1, 2, 3))
|
|
||||||
assert.False(t, mockedService.AssertCalled(tt, "Test_Mock_AssertCalled_WithArguments", 2, 3, 4))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_AssertCalled_WithArguments_With_Repeatability(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_AssertCalled_WithArguments_With_Repeatability", 1, 2, 3).Return(5, 6, 7).Once()
|
|
||||||
mockedService.On("Test_Mock_AssertCalled_WithArguments_With_Repeatability", 2, 3, 4).Return(5, 6, 7).Once()
|
|
||||||
|
|
||||||
mockedService.Called(1, 2, 3)
|
|
||||||
mockedService.Called(2, 3, 4)
|
|
||||||
|
|
||||||
tt := new(testing.T)
|
|
||||||
assert.True(t, mockedService.AssertCalled(tt, "Test_Mock_AssertCalled_WithArguments_With_Repeatability", 1, 2, 3))
|
|
||||||
assert.True(t, mockedService.AssertCalled(tt, "Test_Mock_AssertCalled_WithArguments_With_Repeatability", 2, 3, 4))
|
|
||||||
assert.False(t, mockedService.AssertCalled(tt, "Test_Mock_AssertCalled_WithArguments_With_Repeatability", 3, 4, 5))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Mock_AssertNotCalled(t *testing.T) {
|
|
||||||
|
|
||||||
var mockedService *TestExampleImplementation = new(TestExampleImplementation)
|
|
||||||
|
|
||||||
mockedService.On("Test_Mock_AssertNotCalled", 1, 2, 3).Return(5, 6, 7)
|
|
||||||
|
|
||||||
mockedService.Called(1, 2, 3)
|
|
||||||
|
|
||||||
assert.True(t, mockedService.AssertNotCalled(t, "Test_Mock_NotCalled"))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Arguments helper methods
|
|
||||||
*/
|
|
||||||
func Test_Arguments_Get(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", 123, true}
|
|
||||||
|
|
||||||
assert.Equal(t, "string", args.Get(0).(string))
|
|
||||||
assert.Equal(t, 123, args.Get(1).(int))
|
|
||||||
assert.Equal(t, true, args.Get(2).(bool))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Is(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", 123, true}
|
|
||||||
|
|
||||||
assert.True(t, args.Is("string", 123, true))
|
|
||||||
assert.False(t, args.Is("wrong", 456, false))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Diff(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"Hello World", 123, true}
|
|
||||||
var diff string
|
|
||||||
var count int
|
|
||||||
diff, count = args.Diff([]interface{}{"Hello World", 456, "false"})
|
|
||||||
|
|
||||||
assert.Equal(t, 2, count)
|
|
||||||
assert.Contains(t, diff, `%!s(int=456) != %!s(int=123)`)
|
|
||||||
assert.Contains(t, diff, `false != %!s(bool=true)`)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Diff_DifferentNumberOfArgs(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", 123, true}
|
|
||||||
var diff string
|
|
||||||
var count int
|
|
||||||
diff, count = args.Diff([]interface{}{"string", 456, "false", "extra"})
|
|
||||||
|
|
||||||
assert.Equal(t, 3, count)
|
|
||||||
assert.Contains(t, diff, `extra != (Missing)`)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Diff_WithAnythingArgument(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", 123, true}
|
|
||||||
var count int
|
|
||||||
_, count = args.Diff([]interface{}{"string", Anything, true})
|
|
||||||
|
|
||||||
assert.Equal(t, 0, count)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Diff_WithAnythingArgument_InActualToo(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", Anything, true}
|
|
||||||
var count int
|
|
||||||
_, count = args.Diff([]interface{}{"string", 123, true})
|
|
||||||
|
|
||||||
assert.Equal(t, 0, count)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Diff_WithAnythingOfTypeArgument(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", AnythingOfType("int"), true}
|
|
||||||
var count int
|
|
||||||
_, count = args.Diff([]interface{}{"string", 123, true})
|
|
||||||
|
|
||||||
assert.Equal(t, 0, count)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Diff_WithAnythingOfTypeArgument_Failing(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", AnythingOfType("string"), true}
|
|
||||||
var count int
|
|
||||||
var diff string
|
|
||||||
diff, count = args.Diff([]interface{}{"string", 123, true})
|
|
||||||
|
|
||||||
assert.Equal(t, 1, count)
|
|
||||||
assert.Contains(t, diff, `string != type int - %!s(int=123)`)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Assert(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", 123, true}
|
|
||||||
|
|
||||||
assert.True(t, args.Assert(t, "string", 123, true))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_String_Representation(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", 123, true}
|
|
||||||
assert.Equal(t, `string,int,bool`, args.String())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_String(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", 123, true}
|
|
||||||
assert.Equal(t, "string", args.String(0))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Error(t *testing.T) {
|
|
||||||
|
|
||||||
var err error = errors.New("An Error")
|
|
||||||
var args Arguments = []interface{}{"string", 123, true, err}
|
|
||||||
assert.Equal(t, err, args.Error(3))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Error_Nil(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", 123, true, nil}
|
|
||||||
assert.Equal(t, nil, args.Error(3))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Int(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", 123, true}
|
|
||||||
assert.Equal(t, 123, args.Int(1))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Arguments_Bool(t *testing.T) {
|
|
||||||
|
|
||||||
var args Arguments = []interface{}{"string", 123, true}
|
|
||||||
assert.Equal(t, true, args.Bool(2))
|
|
||||||
|
|
||||||
}
|
|
2
Makefile
2
Makefile
@ -29,7 +29,7 @@ lint:
|
|||||||
|
|
||||||
cyclo:
|
cyclo:
|
||||||
@echo "Running $@:"
|
@echo "Running $@:"
|
||||||
@test -z "$$(gocyclo -over 19 . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
|
@test -z "$$(gocyclo -over 25 . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
|
||||||
|
|
||||||
gomake-all: getdeps verifiers
|
gomake-all: getdeps verifiers
|
||||||
@echo "Installing minio:"
|
@echo "Installing minio:"
|
||||||
|
177
commands.go
177
commands.go
@ -2,147 +2,84 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
|
||||||
"github.com/minio/cli"
|
"github.com/minio/cli"
|
||||||
"github.com/minio/minio/pkg/iodine"
|
|
||||||
"github.com/minio/minio/pkg/server"
|
"github.com/minio/minio/pkg/server"
|
||||||
|
"github.com/minio/minio/pkg/server/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func appendUniq(slice []string, i string) []string {
|
|
||||||
for _, ele := range slice {
|
|
||||||
if ele == i {
|
|
||||||
return slice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return append(slice, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
var commands = []cli.Command{
|
var commands = []cli.Command{
|
||||||
modeCmd,
|
serverCmd,
|
||||||
|
controlCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
var modeCommands = []cli.Command{
|
var serverCmd = cli.Command{
|
||||||
donutCmd,
|
Name: "server",
|
||||||
}
|
Description: "Server mode",
|
||||||
|
Action: runServer,
|
||||||
var modeCmd = cli.Command{
|
|
||||||
Name: "mode",
|
|
||||||
Subcommands: modeCommands,
|
|
||||||
Description: "Mode of execution",
|
|
||||||
}
|
|
||||||
|
|
||||||
var donutCmd = cli.Command{
|
|
||||||
Name: "donut",
|
|
||||||
Description: "[status: EXPERIMENTAL]. Path to donut volume.",
|
|
||||||
Action: runDonut,
|
|
||||||
CustomHelpTemplate: `NAME:
|
CustomHelpTemplate: `NAME:
|
||||||
minio mode {{.Name}} - {{.Description}}
|
minio {{.Name}} - {{.Description}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
minio mode {{.Name}} PATH
|
minio {{.Name}}
|
||||||
|
|
||||||
EXAMPLES:
|
EXAMPLES:
|
||||||
1. Create a donut volume under "/mnt/backup", with a cache limit of 64MB with 1hr expiration
|
1. Start in server mode
|
||||||
$ minio mode {{.Name}} limit 64MB expire 1h paths /mnt/backup
|
$ minio server
|
||||||
|
|
||||||
2. Create a donut volume under collection of paths, put a cache limit of 512MB
|
|
||||||
$ minio mode {{.Name}} limit 512MB paths ""
|
|
||||||
|
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDonut(c *cli.Context) {
|
var controlCmd = cli.Command{
|
||||||
var err error
|
Name: "control",
|
||||||
|
Description: "Control mode",
|
||||||
|
Action: runController,
|
||||||
|
CustomHelpTemplate: `NAME:
|
||||||
|
minio {{.Name}} - {{.Description}}
|
||||||
|
|
||||||
u, err := user.Current()
|
USAGE:
|
||||||
|
minio {{.Name}}
|
||||||
|
|
||||||
|
EXAMPLES:
|
||||||
|
1. Start in controller mode
|
||||||
|
$ minio control
|
||||||
|
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAPIServerConfig(c *cli.Context) api.Config {
|
||||||
|
certFile := c.GlobalString("cert")
|
||||||
|
keyFile := c.GlobalString("key")
|
||||||
|
if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
|
||||||
|
Fatalln("Both certificate and key are required to enable https.")
|
||||||
|
}
|
||||||
|
tls := (certFile != "" && keyFile != "")
|
||||||
|
return api.Config{
|
||||||
|
Address: c.GlobalString("address"),
|
||||||
|
TLS: tls,
|
||||||
|
CertFile: certFile,
|
||||||
|
KeyFile: keyFile,
|
||||||
|
RateLimit: c.GlobalInt("ratelimit"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runServer(c *cli.Context) {
|
||||||
|
_, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("Unable to determine current user. Reason: %s\n", err)
|
||||||
|
}
|
||||||
|
apiServerConfig := getAPIServerConfig(c)
|
||||||
|
if err := server.StartServices(apiServerConfig); err != nil {
|
||||||
|
Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runController(c *cli.Context) {
|
||||||
|
_, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Unable to determine current user. Reason: %s\n", err)
|
Fatalf("Unable to determine current user. Reason: %s\n", err)
|
||||||
}
|
}
|
||||||
if len(c.Args()) < 1 {
|
if len(c.Args()) < 1 {
|
||||||
cli.ShowCommandHelpAndExit(c, "donut", 1) // last argument is exit code
|
cli.ShowCommandHelpAndExit(c, "control", 1) // last argument is exit code
|
||||||
}
|
}
|
||||||
var maxMemory uint64
|
|
||||||
maxMemorySet := false
|
|
||||||
|
|
||||||
var expiration time.Duration
|
|
||||||
expirationSet := false
|
|
||||||
|
|
||||||
var paths []string
|
|
||||||
pathSet := false
|
|
||||||
|
|
||||||
args := c.Args()
|
|
||||||
for len(args) > 0 {
|
|
||||||
switch args.First() {
|
|
||||||
case "limit":
|
|
||||||
{
|
|
||||||
if maxMemorySet {
|
|
||||||
Fatalln("Limit should be set only once")
|
|
||||||
}
|
|
||||||
args = args.Tail()
|
|
||||||
maxMemory, err = humanize.ParseBytes(args.First())
|
|
||||||
if err != nil {
|
|
||||||
Fatalf("Invalid memory size [%s] passed. Reason: %s\n", args.First(), iodine.New(err, nil))
|
|
||||||
}
|
|
||||||
if maxMemory < 1024*1024*10 {
|
|
||||||
Fatalf("Invalid memory size [%s] passed. Should be greater than 10M\n", args.First())
|
|
||||||
}
|
|
||||||
args = args.Tail()
|
|
||||||
maxMemorySet = true
|
|
||||||
}
|
|
||||||
case "expire":
|
|
||||||
{
|
|
||||||
if expirationSet {
|
|
||||||
Fatalln("Expiration should be set only once")
|
|
||||||
}
|
|
||||||
args = args.Tail()
|
|
||||||
expiration, err = time.ParseDuration(args.First())
|
|
||||||
if err != nil {
|
|
||||||
Fatalf("Invalid expiration time [%s] passed. Reason: %s\n", args.First(), iodine.New(err, nil))
|
|
||||||
}
|
|
||||||
args = args.Tail()
|
|
||||||
expirationSet = true
|
|
||||||
}
|
|
||||||
case "paths":
|
|
||||||
if pathSet {
|
|
||||||
Fatalln("Path should be set only once")
|
|
||||||
}
|
|
||||||
// supporting multiple paths
|
|
||||||
args = args.Tail()
|
|
||||||
if strings.TrimSpace(args.First()) == "" {
|
|
||||||
p := filepath.Join(u.HomeDir, "minio-storage", "donut")
|
|
||||||
paths = appendUniq(paths, p)
|
|
||||||
} else {
|
|
||||||
for _, arg := range args {
|
|
||||||
paths = appendUniq(paths, strings.TrimSpace(arg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
args = args.Tail()
|
|
||||||
pathSet = true
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
cli.ShowCommandHelpAndExit(c, "donut", 1) // last argument is exit code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if maxMemorySet == false {
|
|
||||||
Fatalln("Memory limit must be set")
|
|
||||||
}
|
|
||||||
if pathSet == false {
|
|
||||||
Fatalln("Path must be set")
|
|
||||||
}
|
|
||||||
apiServerConfig := getAPIServerConfig(c)
|
|
||||||
donutDriver := server.Factory{
|
|
||||||
Config: apiServerConfig,
|
|
||||||
Paths: paths,
|
|
||||||
MaxMemory: maxMemory,
|
|
||||||
Expiration: expiration,
|
|
||||||
}
|
|
||||||
apiServer := donutDriver.GetStartServerFunc()
|
|
||||||
// webServer := getWebServerConfigFunc(c)
|
|
||||||
servers := []server.StartServerFunc{apiServer} //, webServer}
|
|
||||||
server.StartMinio(servers)
|
|
||||||
}
|
}
|
||||||
|
32
main.go
32
main.go
@ -27,7 +27,6 @@ import (
|
|||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/minio/cli"
|
"github.com/minio/cli"
|
||||||
"github.com/minio/minio/pkg/iodine"
|
"github.com/minio/minio/pkg/iodine"
|
||||||
"github.com/minio/minio/pkg/server/httpserver"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var globalDebugFlag = false
|
var globalDebugFlag = false
|
||||||
@ -71,37 +70,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAPIServerConfig(c *cli.Context) httpserver.Config {
|
|
||||||
certFile := c.GlobalString("cert")
|
|
||||||
keyFile := c.GlobalString("key")
|
|
||||||
if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
|
|
||||||
Fatalln("Both certificate and key are required to enable https.")
|
|
||||||
}
|
|
||||||
tls := (certFile != "" && keyFile != "")
|
|
||||||
return httpserver.Config{
|
|
||||||
Address: c.GlobalString("address"),
|
|
||||||
TLS: tls,
|
|
||||||
CertFile: certFile,
|
|
||||||
KeyFile: keyFile,
|
|
||||||
RateLimit: c.GlobalInt("ratelimit"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func getWebServerConfigFunc(c *cli.Context) server.StartServerFunc {
|
|
||||||
config := httpserver.Config{
|
|
||||||
Address: c.GlobalString("address-mgmt"),
|
|
||||||
TLS: false,
|
|
||||||
CertFile: "",
|
|
||||||
KeyFile: "",
|
|
||||||
}
|
|
||||||
webDrivers := server.WebFactory{
|
|
||||||
Config: config,
|
|
||||||
}
|
|
||||||
return webDrivers.GetStartServerFunc()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Tries to get os/arch/platform specific information
|
// Tries to get os/arch/platform specific information
|
||||||
// Returns a map of current os/arch/platform/memstats
|
// Returns a map of current os/arch/platform/memstats
|
||||||
func getSystemData() map[string]string {
|
func getSystemData() map[string]string {
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimalist Object Storage, (C) 2014 Minio, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
router "github.com/gorilla/mux"
|
|
||||||
"github.com/minio/minio/pkg/api/logging"
|
|
||||||
"github.com/minio/minio/pkg/api/quota"
|
|
||||||
"github.com/minio/minio/pkg/storage/drivers"
|
|
||||||
)
|
|
||||||
|
|
||||||
type minioAPI struct {
|
|
||||||
driver drivers.Driver
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config api configurable parameters
|
|
||||||
type Config struct {
|
|
||||||
RateLimit int
|
|
||||||
driver drivers.Driver
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDriver - get a an existing set driver
|
|
||||||
func (c Config) GetDriver() drivers.Driver {
|
|
||||||
return c.driver
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDriver - set a new driver
|
|
||||||
func (c *Config) SetDriver(driver drivers.Driver) {
|
|
||||||
c.driver = driver
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPHandler - http wrapper handler
|
|
||||||
func HTTPHandler(config Config) http.Handler {
|
|
||||||
var mux *router.Router
|
|
||||||
var api = minioAPI{}
|
|
||||||
api.driver = config.GetDriver()
|
|
||||||
|
|
||||||
mux = router.NewRouter()
|
|
||||||
mux.HandleFunc("/", api.listBucketsHandler).Methods("GET")
|
|
||||||
mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET")
|
|
||||||
mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT")
|
|
||||||
mux.HandleFunc("/{bucket}", api.headBucketHandler).Methods("HEAD")
|
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", api.headObjectHandler).Methods("HEAD")
|
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectPartHandler).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").Methods("PUT")
|
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", api.listObjectPartsHandler).Queries("uploadId", "{uploadId:.*}").Methods("GET")
|
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", api.completeMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("POST")
|
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", api.newMultipartUploadHandler).Methods("POST")
|
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", api.abortMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("DELETE")
|
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", api.getObjectHandler).Methods("GET")
|
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectHandler).Methods("PUT")
|
|
||||||
|
|
||||||
// not implemented yet
|
|
||||||
mux.HandleFunc("/{bucket}", api.deleteBucketHandler).Methods("DELETE")
|
|
||||||
|
|
||||||
// unsupported API
|
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", api.deleteObjectHandler).Methods("DELETE")
|
|
||||||
|
|
||||||
handler := validContentTypeHandler(mux)
|
|
||||||
handler = timeValidityHandler(handler)
|
|
||||||
handler = ignoreResourcesHandler(handler)
|
|
||||||
handler = validateAuthHeaderHandler(handler)
|
|
||||||
// handler = quota.BandwidthCap(h, 25*1024*1024, time.Duration(30*time.Minute))
|
|
||||||
// handler = quota.BandwidthCap(h, 100*1024*1024, time.Duration(24*time.Hour))
|
|
||||||
// handler = quota.RequestLimit(h, 100, time.Duration(30*time.Minute))
|
|
||||||
// handler = quota.RequestLimit(h, 1000, time.Duration(24*time.Hour))
|
|
||||||
// handler = quota.ConnectionLimit(handler, config.ConnectionLimit)
|
|
||||||
handler = quota.RateLimit(handler, config.RateLimit)
|
|
||||||
handler = logging.LogHandler(handler)
|
|
||||||
return handler
|
|
||||||
}
|
|
1793
pkg/api/api_test.go
1793
pkg/api/api_test.go
File diff suppressed because it is too large
Load Diff
@ -1,152 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimalist Object Storage, (C) 2015 Minio, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/user"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/iodine"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config context
|
|
||||||
type Config struct {
|
|
||||||
ConfigPath string
|
|
||||||
ConfigFile string
|
|
||||||
ConfigLock *sync.RWMutex
|
|
||||||
Users map[string]User
|
|
||||||
}
|
|
||||||
|
|
||||||
// User context
|
|
||||||
type User struct {
|
|
||||||
Name string
|
|
||||||
AccessKey string
|
|
||||||
SecretKey string
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetupConfig initialize config directory and template config
|
|
||||||
func (c *Config) SetupConfig() error {
|
|
||||||
u, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
return iodine.New(err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
confPath := filepath.Join(u.HomeDir, ".minio")
|
|
||||||
if err := os.MkdirAll(confPath, 0700); err != nil {
|
|
||||||
return iodine.New(err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ConfigPath = confPath
|
|
||||||
c.ConfigFile = filepath.Join(c.ConfigPath, "config.json")
|
|
||||||
if _, err := os.Stat(c.ConfigFile); os.IsNotExist(err) {
|
|
||||||
_, err = os.Create(c.ConfigFile)
|
|
||||||
if err != nil {
|
|
||||||
return iodine.New(err, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ConfigLock = new(sync.RWMutex)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetConfigPath config file location
|
|
||||||
func (c *Config) GetConfigPath() string {
|
|
||||||
return c.ConfigPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsUserExists verify if user exists
|
|
||||||
func (c *Config) IsUserExists(username string) bool {
|
|
||||||
for _, user := range c.Users {
|
|
||||||
if user.Name == username {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUser - get user from username
|
|
||||||
func (c *Config) GetUser(username string) User {
|
|
||||||
for _, user := range c.Users {
|
|
||||||
if user.Name == username {
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return User{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddUser - add a user into existing User list
|
|
||||||
func (c *Config) AddUser(user User) {
|
|
||||||
var currentUsers map[string]User
|
|
||||||
if len(c.Users) == 0 {
|
|
||||||
currentUsers = make(map[string]User)
|
|
||||||
} else {
|
|
||||||
currentUsers = c.Users
|
|
||||||
}
|
|
||||||
currentUsers[user.AccessKey] = user
|
|
||||||
c.Users = currentUsers
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteConfig - write encoded json in config file
|
|
||||||
func (c *Config) WriteConfig() error {
|
|
||||||
c.ConfigLock.Lock()
|
|
||||||
defer c.ConfigLock.Unlock()
|
|
||||||
|
|
||||||
var file *os.File
|
|
||||||
var err error
|
|
||||||
|
|
||||||
file, err = os.OpenFile(c.ConfigFile, os.O_WRONLY, 0666)
|
|
||||||
defer file.Close()
|
|
||||||
if err != nil {
|
|
||||||
return iodine.New(err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder := json.NewEncoder(file)
|
|
||||||
encoder.Encode(c.Users)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadConfig - read json config file and decode
|
|
||||||
func (c *Config) ReadConfig() error {
|
|
||||||
c.ConfigLock.RLock()
|
|
||||||
defer c.ConfigLock.RUnlock()
|
|
||||||
|
|
||||||
var file *os.File
|
|
||||||
var err error
|
|
||||||
|
|
||||||
file, err = os.OpenFile(c.ConfigFile, os.O_RDONLY, 0666)
|
|
||||||
defer file.Close()
|
|
||||||
if err != nil {
|
|
||||||
return iodine.New(err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
users := make(map[string]User)
|
|
||||||
decoder := json.NewDecoder(file)
|
|
||||||
err = decoder.Decode(&users)
|
|
||||||
switch err {
|
|
||||||
case io.EOF:
|
|
||||||
return nil
|
|
||||||
case nil:
|
|
||||||
c.Users = users
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return iodine.New(err, nil)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimalist Object Storage, (C) 2015 Minio, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
. "github.com/minio/check"
|
|
||||||
"github.com/minio/minio/pkg/utils/crypto/keys"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MySuite struct{}
|
|
||||||
|
|
||||||
var _ = Suite(&MySuite{})
|
|
||||||
|
|
||||||
func Test(t *testing.T) { TestingT(t) }
|
|
||||||
|
|
||||||
func (s *MySuite) TestConfig(c *C) {
|
|
||||||
conf := Config{}
|
|
||||||
conf.ConfigLock = new(sync.RWMutex)
|
|
||||||
conf.ConfigPath, _ = ioutil.TempDir("/tmp", "minio-test-")
|
|
||||||
defer os.RemoveAll(conf.ConfigPath)
|
|
||||||
conf.ConfigFile = filepath.Join(conf.ConfigPath, "config.json")
|
|
||||||
if _, err := os.Stat(conf.ConfigFile); os.IsNotExist(err) {
|
|
||||||
_, err = os.Create(conf.ConfigFile)
|
|
||||||
if err != nil {
|
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
accesskey, _ := keys.GenerateRandomAlphaNumeric(keys.MinioAccessID)
|
|
||||||
secretkey, _ := keys.GenerateRandomBase64(keys.MinioSecretID)
|
|
||||||
|
|
||||||
user := User{
|
|
||||||
Name: "gnubot",
|
|
||||||
AccessKey: string(accesskey),
|
|
||||||
SecretKey: string(secretkey),
|
|
||||||
}
|
|
||||||
|
|
||||||
conf.AddUser(user)
|
|
||||||
err := conf.WriteConfig()
|
|
||||||
c.Assert(err, IsNil)
|
|
||||||
|
|
||||||
err = conf.ReadConfig()
|
|
||||||
c.Assert(err, IsNil)
|
|
||||||
|
|
||||||
accesskey, _ = keys.GenerateRandomAlphaNumeric(keys.MinioAccessID)
|
|
||||||
secretkey, _ = keys.GenerateRandomBase64(keys.MinioSecretID)
|
|
||||||
user = User{
|
|
||||||
Name: "minio",
|
|
||||||
AccessKey: string(accesskey),
|
|
||||||
SecretKey: string(secretkey),
|
|
||||||
}
|
|
||||||
conf.AddUser(user)
|
|
||||||
err = conf.WriteConfig()
|
|
||||||
c.Assert(err, IsNil)
|
|
||||||
}
|
|
@ -1,152 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimalist Object Storage, (C) 2015 Minio, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package quota
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/iodine"
|
|
||||||
"github.com/minio/minio/pkg/utils/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// bandwidthQuotaHandler
|
|
||||||
type bandwidthQuotaHandler struct {
|
|
||||||
handler http.Handler
|
|
||||||
quotas *quotaMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP is an http.Handler ServeHTTP method
|
|
||||||
func (h *bandwidthQuotaHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
||||||
host, _, _ := net.SplitHostPort(req.RemoteAddr)
|
|
||||||
longIP := longIP{net.ParseIP(host)}.IptoUint32()
|
|
||||||
if h.quotas.WillExceedQuota(longIP, req.ContentLength) {
|
|
||||||
hosts, _ := net.LookupAddr(uint32ToIP(longIP).String())
|
|
||||||
log.Debug.Printf("Offending Host: %s, BandwidthUsed: %d", hosts, h.quotas.GetQuotaUsed(longIP))
|
|
||||||
writeErrorResponse(w, req, BandWidthInsufficientToProceed, req.URL.Path)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
qr := "aReader{
|
|
||||||
ReadCloser: req.Body,
|
|
||||||
quotas: h.quotas,
|
|
||||||
ip: longIP,
|
|
||||||
w: w,
|
|
||||||
req: req,
|
|
||||||
lock: &sync.RWMutex{},
|
|
||||||
}
|
|
||||||
req.Body = qr
|
|
||||||
w = "aWriter{
|
|
||||||
ResponseWriter: w,
|
|
||||||
quotas: h.quotas,
|
|
||||||
ip: longIP,
|
|
||||||
quotaReader: qr,
|
|
||||||
}
|
|
||||||
h.handler.ServeHTTP(w, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BandwidthCap sets a quote based upon bandwidth used
|
|
||||||
func BandwidthCap(h http.Handler, limit int64, duration time.Duration) http.Handler {
|
|
||||||
return &bandwidthQuotaHandler{
|
|
||||||
handler: h,
|
|
||||||
quotas: "aMap{
|
|
||||||
data: make(map[int64]map[uint32]int64),
|
|
||||||
limit: int64(limit),
|
|
||||||
duration: duration,
|
|
||||||
segmentSize: segmentSize(duration),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type quotaReader struct {
|
|
||||||
io.ReadCloser
|
|
||||||
quotas *quotaMap
|
|
||||||
ip uint32
|
|
||||||
w http.ResponseWriter
|
|
||||||
req *http.Request
|
|
||||||
err bool
|
|
||||||
lock *sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *quotaReader) Read(b []byte) (int, error) {
|
|
||||||
log.Println(q.quotas.GetQuotaUsed(q.ip))
|
|
||||||
log.Println(q.quotas.limit)
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
if q.err {
|
|
||||||
return 0, iodine.New(errors.New("Quota Met"), nil)
|
|
||||||
}
|
|
||||||
if q.err == false && q.quotas.IsQuotaMet(q.ip) {
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
q.err = true
|
|
||||||
hosts, _ := net.LookupAddr(uint32ToIP(q.ip).String())
|
|
||||||
log.Debug.Printf("Offending Host: %s, BandwidthUsed: %d", hosts, q.quotas.GetQuotaUsed(q.ip))
|
|
||||||
writeErrorResponse(q.w, q.req, BandWidthQuotaExceeded, q.req.URL.Path)
|
|
||||||
return 0, iodine.New(errors.New("Quota Met"), nil)
|
|
||||||
}
|
|
||||||
n, err := q.ReadCloser.Read(b)
|
|
||||||
q.quotas.Add(q.ip, int64(n))
|
|
||||||
return n, iodine.New(err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *quotaReader) Close() error {
|
|
||||||
return iodine.New(q.ReadCloser.Close(), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
type quotaWriter struct {
|
|
||||||
ResponseWriter http.ResponseWriter
|
|
||||||
quotas *quotaMap
|
|
||||||
ip uint32
|
|
||||||
quotaReader *quotaReader
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *quotaWriter) Write(b []byte) (int, error) {
|
|
||||||
q.quotaReader.lock.RLock()
|
|
||||||
defer q.quotaReader.lock.RUnlock()
|
|
||||||
if q.quotas.IsQuotaMet(q.ip) {
|
|
||||||
return 0, iodine.New(errors.New("Quota Met"), nil)
|
|
||||||
}
|
|
||||||
q.quotas.Add(q.ip, int64(len(b)))
|
|
||||||
n, err := q.ResponseWriter.Write(b)
|
|
||||||
// remove from quota if a full write isn't performed
|
|
||||||
q.quotas.Add(q.ip, int64(n-len(b)))
|
|
||||||
return n, iodine.New(err, nil)
|
|
||||||
}
|
|
||||||
func (q *quotaWriter) Header() http.Header {
|
|
||||||
return q.ResponseWriter.Header()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *quotaWriter) WriteHeader(status int) {
|
|
||||||
q.quotaReader.lock.RLock()
|
|
||||||
defer q.quotaReader.lock.RUnlock()
|
|
||||||
if q.quotas.IsQuotaMet(q.ip) || q.quotaReader.err {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
q.ResponseWriter.WriteHeader(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func segmentSize(duration time.Duration) time.Duration {
|
|
||||||
var segmentSize time.Duration
|
|
||||||
for i := int64(1); i < duration.Nanoseconds(); i = i * 10 {
|
|
||||||
segmentSize = time.Duration(i)
|
|
||||||
}
|
|
||||||
return segmentSize
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimalist Object Storage, (C) 2015 Minio, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package quota
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/utils/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// requestLimitHandler
|
|
||||||
type connLimit struct {
|
|
||||||
sync.RWMutex
|
|
||||||
handler http.Handler
|
|
||||||
connections map[uint32]int
|
|
||||||
limit int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connLimit) IsLimitExceeded(ip uint32) bool {
|
|
||||||
if c.connections[ip] >= c.limit {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connLimit) GetUsed(ip uint32) int {
|
|
||||||
return c.connections[ip]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connLimit) Add(ip uint32) {
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
count := c.connections[ip]
|
|
||||||
count = count + 1
|
|
||||||
c.connections[ip] = count
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connLimit) Remove(ip uint32) {
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
count, _ := c.connections[ip]
|
|
||||||
count = count - 1
|
|
||||||
if count <= 0 {
|
|
||||||
delete(c.connections, ip)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.connections[ip] = count
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP is an http.Handler ServeHTTP method
|
|
||||||
func (c *connLimit) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
||||||
host, _, _ := net.SplitHostPort(req.RemoteAddr)
|
|
||||||
longIP := longIP{net.ParseIP(host)}.IptoUint32()
|
|
||||||
if c.IsLimitExceeded(longIP) {
|
|
||||||
hosts, _ := net.LookupAddr(uint32ToIP(longIP).String())
|
|
||||||
log.Debug.Printf("Connection limit reached - Host: %s, Total Connections: %d\n", hosts, c.GetUsed(longIP))
|
|
||||||
writeErrorResponse(w, req, ConnectionLimitExceeded, req.URL.Path)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Add(longIP)
|
|
||||||
defer c.Remove(longIP)
|
|
||||||
c.handler.ServeHTTP(w, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectionLimit limits the number of concurrent connections
|
|
||||||
func ConnectionLimit(h http.Handler, limit int) http.Handler {
|
|
||||||
return &connLimit{
|
|
||||||
handler: h,
|
|
||||||
connections: make(map[uint32]int),
|
|
||||||
limit: limit,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimalist Object Storage, (C) 2015 Minio, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package quota
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/xml"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// copied from api, no cyclic deps allowed
|
|
||||||
|
|
||||||
// Error structure
|
|
||||||
type Error struct {
|
|
||||||
Code string
|
|
||||||
Description string
|
|
||||||
HTTPStatusCode int
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorResponse - error response format
|
|
||||||
type ErrorResponse struct {
|
|
||||||
XMLName xml.Name `xml:"Error" json:"-"`
|
|
||||||
Code string
|
|
||||||
Message string
|
|
||||||
Resource string
|
|
||||||
RequestID string
|
|
||||||
HostID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quota standard errors non exhaustive list
|
|
||||||
const (
|
|
||||||
RequestTimeTooSkewed = iota
|
|
||||||
BandWidthQuotaExceeded
|
|
||||||
BandWidthInsufficientToProceed
|
|
||||||
ConnectionLimitExceeded
|
|
||||||
SlowDown
|
|
||||||
)
|
|
||||||
|
|
||||||
// Golang http doesn't implement these
|
|
||||||
const (
|
|
||||||
StatusTooManyRequests = 429
|
|
||||||
)
|
|
||||||
|
|
||||||
func writeErrorResponse(w http.ResponseWriter, req *http.Request, errorType int, resource string) {
|
|
||||||
error := getErrorCode(errorType)
|
|
||||||
errorResponse := getErrorResponse(error, resource)
|
|
||||||
encodedErrorResponse := encodeErrorResponse(errorResponse)
|
|
||||||
// set headers
|
|
||||||
writeErrorHeaders(w)
|
|
||||||
w.WriteHeader(error.HTTPStatusCode)
|
|
||||||
// write body
|
|
||||||
w.Write(encodedErrorResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeErrorHeaders(w http.ResponseWriter) {
|
|
||||||
w.Header().Set("Server", "Minio")
|
|
||||||
w.Header().Set("Accept-Ranges", "bytes")
|
|
||||||
w.Header().Set("Content-Type", "application/xml")
|
|
||||||
w.Header().Set("Connection", "close")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error code to Error structure map
|
|
||||||
var errorCodeResponse = map[int]Error{
|
|
||||||
BandWidthQuotaExceeded: {
|
|
||||||
Code: "BandwidthQuotaExceeded",
|
|
||||||
Description: "Bandwidth Quota Exceeded.",
|
|
||||||
HTTPStatusCode: StatusTooManyRequests,
|
|
||||||
},
|
|
||||||
BandWidthInsufficientToProceed: {
|
|
||||||
Code: "BandwidthQuotaWillBeExceeded",
|
|
||||||
Description: "Bandwidth quota will be exceeded with this request.",
|
|
||||||
HTTPStatusCode: StatusTooManyRequests,
|
|
||||||
},
|
|
||||||
ConnectionLimitExceeded: {
|
|
||||||
Code: "ConnectionLimitExceeded",
|
|
||||||
Description: "Connections Limit Exceeded.",
|
|
||||||
HTTPStatusCode: StatusTooManyRequests,
|
|
||||||
},
|
|
||||||
SlowDown: {
|
|
||||||
Code: "SlowDown",
|
|
||||||
Description: "Reduce your request rate.",
|
|
||||||
HTTPStatusCode: StatusTooManyRequests,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write error response headers
|
|
||||||
func encodeErrorResponse(response interface{}) []byte {
|
|
||||||
var bytesBuffer bytes.Buffer
|
|
||||||
encoder := xml.NewEncoder(&bytesBuffer)
|
|
||||||
encoder.Encode(response)
|
|
||||||
return bytesBuffer.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorCodeError provides errorCode to Error. It returns empty if the code provided is unknown
|
|
||||||
func getErrorCode(code int) Error {
|
|
||||||
return errorCodeResponse[code]
|
|
||||||
}
|
|
||||||
|
|
||||||
// getErrorResponse gets in standard error and resource value and
|
|
||||||
// provides a encodable populated response values
|
|
||||||
func getErrorResponse(err Error, resource string) ErrorResponse {
|
|
||||||
var data = ErrorResponse{}
|
|
||||||
data.Code = err.Code
|
|
||||||
data.Message = err.Description
|
|
||||||
if resource != "" {
|
|
||||||
data.Resource = resource
|
|
||||||
}
|
|
||||||
// TODO implement this in future
|
|
||||||
data.RequestID = "3L137"
|
|
||||||
data.HostID = "3L137"
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimalist Object Storage, (C) 2015 Minio, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package quota
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// map[minute][address] = current quota
|
|
||||||
type quotaMap struct {
|
|
||||||
sync.RWMutex
|
|
||||||
data map[int64]map[uint32]int64
|
|
||||||
limit int64
|
|
||||||
duration time.Duration
|
|
||||||
segmentSize time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *quotaMap) CanExpire() {
|
|
||||||
q.Lock()
|
|
||||||
defer q.Unlock()
|
|
||||||
currentMinute := time.Now().UTC().UnixNano() / q.segmentSize.Nanoseconds()
|
|
||||||
// divide by segmentSize, otherwise expiredQuotas will always be negative
|
|
||||||
expiredQuotas := currentMinute - (q.duration.Nanoseconds() / q.segmentSize.Nanoseconds())
|
|
||||||
for time := range q.data {
|
|
||||||
if time < expiredQuotas {
|
|
||||||
delete(q.data, time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *quotaMap) Add(ip uint32, size int64) {
|
|
||||||
q.CanExpire()
|
|
||||||
q.Lock()
|
|
||||||
defer q.Unlock()
|
|
||||||
currentMinute := time.Now().UTC().UnixNano() / q.segmentSize.Nanoseconds()
|
|
||||||
if _, ok := q.data[currentMinute]; !ok {
|
|
||||||
q.data[currentMinute] = make(map[uint32]int64)
|
|
||||||
}
|
|
||||||
currentData, _ := q.data[currentMinute][ip]
|
|
||||||
proposedDataSize := currentData + size
|
|
||||||
q.data[currentMinute][ip] = proposedDataSize
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *quotaMap) IsQuotaMet(ip uint32) bool {
|
|
||||||
q.CanExpire()
|
|
||||||
if q.GetQuotaUsed(ip) >= q.limit {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *quotaMap) GetQuotaUsed(ip uint32) (total int64) {
|
|
||||||
q.CanExpire()
|
|
||||||
q.RLock()
|
|
||||||
defer q.RUnlock()
|
|
||||||
for _, segment := range q.data {
|
|
||||||
if used, ok := segment[ip]; ok {
|
|
||||||
total += used
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *quotaMap) WillExceedQuota(ip uint32, size int64) (result bool) {
|
|
||||||
return q.GetQuotaUsed(ip)+size > q.limit
|
|
||||||
}
|
|
||||||
|
|
||||||
type longIP struct {
|
|
||||||
net.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
// []byte to uint32 representation
|
|
||||||
func (p longIP) IptoUint32() (result uint32) {
|
|
||||||
ip := p.To4()
|
|
||||||
if ip == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return binary.BigEndian.Uint32(ip)
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimalist Object Storage, (C) 2015 Minio, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package quota
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/utils/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// requestLimitHandler
|
|
||||||
type requestLimitHandler struct {
|
|
||||||
handler http.Handler
|
|
||||||
quotas *quotaMap
|
|
||||||
}
|
|
||||||
|
|
||||||
//convert a uint32 to an ipv4
|
|
||||||
func uint32ToIP(ip uint32) net.IP {
|
|
||||||
addr := net.IP{0, 0, 0, 0}
|
|
||||||
binary.BigEndian.PutUint32(addr, ip)
|
|
||||||
return addr
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP is an http.Handler ServeHTTP method
|
|
||||||
func (h *requestLimitHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
||||||
host, _, _ := net.SplitHostPort(req.RemoteAddr)
|
|
||||||
longIP := longIP{net.ParseIP(host)}.IptoUint32()
|
|
||||||
if h.quotas.IsQuotaMet(longIP) {
|
|
||||||
hosts, _ := net.LookupAddr(uint32ToIP(longIP).String())
|
|
||||||
log.Debug.Printf("Offending Host: %s, RequestUSED: %d\n", hosts, h.quotas.GetQuotaUsed(longIP))
|
|
||||||
writeErrorResponse(w, req, SlowDown, req.URL.Path)
|
|
||||||
}
|
|
||||||
h.quotas.Add(longIP, 1)
|
|
||||||
h.handler.ServeHTTP(w, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RequestLimit sets a quote based upon number of requests allowed over a time period
|
|
||||||
func RequestLimit(h http.Handler, limit int64, duration time.Duration) http.Handler {
|
|
||||||
return &requestLimitHandler{
|
|
||||||
handler: h,
|
|
||||||
quotas: "aMap{
|
|
||||||
data: make(map[int64]map[uint32]int64),
|
|
||||||
limit: int64(limit),
|
|
||||||
duration: duration,
|
|
||||||
segmentSize: segmentSize(duration),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimalist Object Storage, (C) 2015 Minio, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package web
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/minio/minio/pkg/api/config"
|
|
||||||
"github.com/minio/minio/pkg/iodine"
|
|
||||||
"github.com/minio/minio/pkg/utils/crypto/keys"
|
|
||||||
"github.com/minio/minio/pkg/utils/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultWeb = "polygon"
|
|
||||||
)
|
|
||||||
|
|
||||||
type webAPI struct {
|
|
||||||
conf config.Config
|
|
||||||
webPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
// No encoder interface exists, so we create one.
|
|
||||||
type encoder interface {
|
|
||||||
Encode(v interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPHandler - http wrapper handler
|
|
||||||
func HTTPHandler() http.Handler {
|
|
||||||
mux := mux.NewRouter()
|
|
||||||
var api = webAPI{}
|
|
||||||
|
|
||||||
if err := api.conf.SetupConfig(); err != nil {
|
|
||||||
log.Fatal(iodine.New(err, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
api.webPath = filepath.Join(api.conf.GetConfigPath(), defaultWeb)
|
|
||||||
mux.Handle("/{polygon:.*}", http.FileServer(http.Dir(api.webPath))).Methods("GET")
|
|
||||||
mux.HandleFunc("/access", api.accessHandler).Methods("POST")
|
|
||||||
return mux
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeResponse(w http.ResponseWriter, response interface{}) []byte {
|
|
||||||
var bytesBuffer bytes.Buffer
|
|
||||||
var encoder encoder
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
encoder = json.NewEncoder(&bytesBuffer)
|
|
||||||
w.Header().Set("Server", "Minio Management Console")
|
|
||||||
w.Header().Set("Connection", "close")
|
|
||||||
encoder.Encode(response)
|
|
||||||
return bytesBuffer.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (web *webAPI) accessHandler(w http.ResponseWriter, req *http.Request) {
|
|
||||||
var err error
|
|
||||||
var accesskey, secretkey []byte
|
|
||||||
username := req.FormValue("username")
|
|
||||||
if len(username) <= 0 {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = web.conf.ReadConfig()
|
|
||||||
if err != nil {
|
|
||||||
log.Error.Println(iodine.New(err, nil))
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if web.conf.IsUserExists(username) {
|
|
||||||
w.WriteHeader(http.StatusConflict)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var user = config.User{}
|
|
||||||
user.Name = username
|
|
||||||
|
|
||||||
accesskey, err = keys.GenerateRandomAlphaNumeric(keys.MinioAccessID)
|
|
||||||
if err != nil {
|
|
||||||
log.Error.Println(iodine.New(err, nil))
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
user.AccessKey = string(accesskey)
|
|
||||||
|
|
||||||
secretkey, err = keys.GenerateRandomBase64(keys.MinioSecretID)
|
|
||||||
if err != nil {
|
|
||||||
log.Error.Println(iodine.New(err, nil))
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
user.SecretKey = string(secretkey)
|
|
||||||
|
|
||||||
web.conf.AddUser(user)
|
|
||||||
err = web.conf.WriteConfig()
|
|
||||||
if err != nil {
|
|
||||||
log.Error.Println(iodine.New(err, nil))
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = web.conf.ReadConfig()
|
|
||||||
if err != nil {
|
|
||||||
log.Error.Println(iodine.New(err, nil))
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user back for sending it over HTTP reply
|
|
||||||
user = web.conf.GetUser(username)
|
|
||||||
w.Write(writeResponse(w, user))
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/minio/check"
|
. "github.com/minio/check"
|
||||||
"github.com/minio/minio/pkg/utils/crypto/md5"
|
"github.com/minio/minio/pkg/crypto/md5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test(t *testing.T) { TestingT(t) }
|
func Test(t *testing.T) { TestingT(t) }
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user