From a70f01c8c2aa2fad1357bb6d32a966b9cb99156d Mon Sep 17 00:00:00 2001 From: Daniel Hess Date: Mon, 24 Oct 2016 21:49:20 +0000 Subject: [PATCH 1/4] removing godeps dir --- Godeps/Godeps.json | 24 - Godeps/Readme | 5 - Godeps/_workspace/.gitignore | 2 - .../src/github.com/franela/goblin/.gitignore | 22 - .../src/github.com/franela/goblin/.travis.yml | 8 - .../src/github.com/franela/goblin/LICENSE | 19 - .../src/github.com/franela/goblin/Makefile | 3 - .../src/github.com/franela/goblin/README.md | 132 ---- .../github.com/franela/goblin/assertions.go | 59 -- .../src/github.com/franela/goblin/goblin.go | 291 --------- .../github.com/franela/goblin/goblin_logo.jpg | Bin 36893 -> 0 bytes .../franela/goblin/goblin_output.png | Bin 18985 -> 0 bytes .../franela/goblin/mono_reporter.go | 26 - .../github.com/franela/goblin/reporting.go | 137 ---- .../src/github.com/franela/goblin/resolver.go | 21 - .../github.com/gorilla/context/.travis.yml | 7 - .../src/github.com/gorilla/context/LICENSE | 27 - .../src/github.com/gorilla/context/README.md | 7 - .../src/github.com/gorilla/context/context.go | 143 ----- .../src/github.com/gorilla/context/doc.go | 82 --- .../src/github.com/gorilla/mux/.travis.yml | 8 - .../src/github.com/gorilla/mux/LICENSE | 27 - .../src/github.com/gorilla/mux/README.md | 235 ------- .../src/github.com/gorilla/mux/doc.go | 206 ------ .../src/github.com/gorilla/mux/mux.go | 474 -------------- .../src/github.com/gorilla/mux/regexp.go | 317 ---------- .../src/github.com/gorilla/mux/route.go | 595 ------------------ .../src/github.com/onsi/gomega/.gitignore | 3 - .../src/github.com/onsi/gomega/.travis.yml | 11 - .../src/github.com/onsi/gomega/CHANGELOG.md | 70 --- .../src/github.com/onsi/gomega/LICENSE | 20 - .../src/github.com/onsi/gomega/README.md | 17 - .../github.com/onsi/gomega/format/format.go | 276 -------- .../github.com/onsi/gomega/gbytes/buffer.go | 229 ------- .../onsi/gomega/gbytes/say_matcher.go | 105 ---- .../src/github.com/onsi/gomega/gexec/build.go | 78 --- .../onsi/gomega/gexec/exit_matcher.go | 88 --- .../onsi/gomega/gexec/prefixed_writer.go | 53 -- .../github.com/onsi/gomega/gexec/session.go | 214 ------- .../github.com/onsi/gomega/ghttp/handlers.go | 313 --------- .../onsi/gomega/ghttp/protobuf/protobuf.go | 3 - .../ghttp/protobuf/simple_message.pb.go | 55 -- .../ghttp/protobuf/simple_message.proto | 9 - .../onsi/gomega/ghttp/test_server.go | 368 ----------- .../src/github.com/onsi/gomega/gomega_dsl.go | 335 ---------- .../gomega/internal/assertion/assertion.go | 98 --- .../asyncassertion/async_assertion.go | 189 ------ .../internal/fakematcher/fake_matcher.go | 23 - .../internal/oraclematcher/oracle_matcher.go | 25 - .../testingtsupport/testing_t_support.go | 40 -- .../src/github.com/onsi/gomega/matchers.go | 393 ------------ .../github.com/onsi/gomega/matchers/and.go | 64 -- .../matchers/assignable_to_type_of_matcher.go | 31 - .../onsi/gomega/matchers/be_a_directory.go | 54 -- .../onsi/gomega/matchers/be_a_regular_file.go | 54 -- .../gomega/matchers/be_an_existing_file.go | 38 -- .../onsi/gomega/matchers/be_closed_matcher.go | 45 -- .../onsi/gomega/matchers/be_empty_matcher.go | 26 - .../matchers/be_equivalent_to_matcher.go | 33 - .../onsi/gomega/matchers/be_false_matcher.go | 25 - .../onsi/gomega/matchers/be_nil_matcher.go | 18 - .../gomega/matchers/be_numerically_matcher.go | 119 ---- .../onsi/gomega/matchers/be_sent_matcher.go | 71 --- .../gomega/matchers/be_temporally_matcher.go | 65 -- .../onsi/gomega/matchers/be_true_matcher.go | 25 - .../onsi/gomega/matchers/be_zero_matcher.go | 27 - .../onsi/gomega/matchers/consist_of.go | 80 --- .../matchers/contain_element_matcher.go | 56 -- .../matchers/contain_substring_matcher.go | 37 -- .../onsi/gomega/matchers/equal_matcher.go | 27 - .../onsi/gomega/matchers/have_key_matcher.go | 53 -- .../matchers/have_key_with_value_matcher.go | 73 --- .../onsi/gomega/matchers/have_len_matcher.go | 27 - .../gomega/matchers/have_occurred_matcher.go | 30 - .../gomega/matchers/have_prefix_matcher.go | 35 -- .../gomega/matchers/have_suffix_matcher.go | 35 -- .../gomega/matchers/match_error_matcher.go | 50 -- .../gomega/matchers/match_json_matcher.go | 61 -- .../gomega/matchers/match_regexp_matcher.go | 42 -- .../github.com/onsi/gomega/matchers/not.go | 30 - .../src/github.com/onsi/gomega/matchers/or.go | 67 -- .../onsi/gomega/matchers/panic_matcher.go | 42 -- .../onsi/gomega/matchers/receive_matcher.go | 126 ---- .../onsi/gomega/matchers/succeed_matcher.go | 30 - .../matchers/support/goraph/MIT.LICENSE | 20 - .../goraph/bipartitegraph/bipartitegraph.go | 41 -- .../bipartitegraph/bipartitegraphmatching.go | 161 ----- .../matchers/support/goraph/edge/edge.go | 61 -- .../matchers/support/goraph/node/node.go | 7 - .../matchers/support/goraph/util/util.go | 7 - .../onsi/gomega/matchers/type_support.go | 165 ----- .../onsi/gomega/matchers/with_transform.go | 72 --- .../src/github.com/onsi/gomega/types/types.go | 17 - 93 files changed, 8039 deletions(-) delete mode 100644 Godeps/Godeps.json delete mode 100644 Godeps/Readme delete mode 100644 Godeps/_workspace/.gitignore delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/.gitignore delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/.travis.yml delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/Makefile delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/README.md delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/assertions.go delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/goblin.go delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/goblin_logo.jpg delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/goblin_output.png delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/mono_reporter.go delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/reporting.go delete mode 100644 Godeps/_workspace/src/github.com/franela/goblin/resolver.go delete mode 100644 Godeps/_workspace/src/github.com/gorilla/context/.travis.yml delete mode 100644 Godeps/_workspace/src/github.com/gorilla/context/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/gorilla/context/README.md delete mode 100644 Godeps/_workspace/src/github.com/gorilla/context/context.go delete mode 100644 Godeps/_workspace/src/github.com/gorilla/context/doc.go delete mode 100644 Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml delete mode 100644 Godeps/_workspace/src/github.com/gorilla/mux/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/gorilla/mux/README.md delete mode 100644 Godeps/_workspace/src/github.com/gorilla/mux/doc.go delete mode 100644 Godeps/_workspace/src/github.com/gorilla/mux/mux.go delete mode 100644 Godeps/_workspace/src/github.com/gorilla/mux/regexp.go delete mode 100644 Godeps/_workspace/src/github.com/gorilla/mux/route.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/.gitignore delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/.travis.yml delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/CHANGELOG.md delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/README.md delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/format/format.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/gbytes/buffer.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/gbytes/say_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/gexec/build.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/gexec/exit_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/gexec/prefixed_writer.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/gexec/session.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/ghttp/handlers.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/protobuf.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.pb.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.proto delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/ghttp/test_server.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/gomega_dsl.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/internal/assertion/assertion.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/internal/fakematcher/fake_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/internal/oraclematcher/oracle_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/and.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_directory.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_regular_file.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_an_existing_file.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_closed_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_empty_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_equivalent_to_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_false_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_nil_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_numerically_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_sent_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_temporally_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_true_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_zero_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/consist_of.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/contain_element_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/contain_substring_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/equal_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_key_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_len_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_occurred_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_prefix_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_suffix_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_error_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_json_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_regexp_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/not.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/or.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/panic_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/receive_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/MIT.LICENSE delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraph.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/node/node.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/util/util.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/type_support.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/with_transform.go delete mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/types/types.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json deleted file mode 100644 index eb9c6ab..0000000 --- a/Godeps/Godeps.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "ImportPath": "github.com/gomicro/steward", - "GoVersion": "go1.5.2", - "Deps": [ - { - "ImportPath": "github.com/franela/goblin", - "Comment": "0.0.1-58-gac712d3", - "Rev": "ac712d36d1b123f786cbedcfa19b5d25697a14cc" - }, - { - "ImportPath": "github.com/gorilla/context", - "Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a" - }, - { - "ImportPath": "github.com/gorilla/mux", - "Rev": "ad4d7a5882b961e07e2626045eb995c022ac6664" - }, - { - "ImportPath": "github.com/onsi/gomega", - "Comment": "v1.0-71-g2152b45", - "Rev": "2152b45fa28a361beba9aab0885972323a444e28" - } - ] -} diff --git a/Godeps/Readme b/Godeps/Readme deleted file mode 100644 index 4cdaa53..0000000 --- a/Godeps/Readme +++ /dev/null @@ -1,5 +0,0 @@ -This directory tree is generated automatically by godep. - -Please do not edit. - -See https://github.com/tools/godep for more information. diff --git a/Godeps/_workspace/.gitignore b/Godeps/_workspace/.gitignore deleted file mode 100644 index f037d68..0000000 --- a/Godeps/_workspace/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/pkg -/bin diff --git a/Godeps/_workspace/src/github.com/franela/goblin/.gitignore b/Godeps/_workspace/src/github.com/franela/goblin/.gitignore deleted file mode 100644 index 0026861..0000000 --- a/Godeps/_workspace/src/github.com/franela/goblin/.gitignore +++ /dev/null @@ -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 diff --git a/Godeps/_workspace/src/github.com/franela/goblin/.travis.yml b/Godeps/_workspace/src/github.com/franela/goblin/.travis.yml deleted file mode 100644 index 27a6316..0000000 --- a/Godeps/_workspace/src/github.com/franela/goblin/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: go -go: - - 1.1.2 - - tip -notifications: - email: - - ionathan@gmail.com - - marcosnils@gmail.com diff --git a/Godeps/_workspace/src/github.com/franela/goblin/LICENSE b/Godeps/_workspace/src/github.com/franela/goblin/LICENSE deleted file mode 100644 index b2d6522..0000000 --- a/Godeps/_workspace/src/github.com/franela/goblin/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2013 Marcos Lilljedahl and Jonathan Leibiusky - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/franela/goblin/Makefile b/Godeps/_workspace/src/github.com/franela/goblin/Makefile deleted file mode 100644 index 66763dc..0000000 --- a/Godeps/_workspace/src/github.com/franela/goblin/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -export GOPATH=$(shell pwd) -test: - go test -v diff --git a/Godeps/_workspace/src/github.com/franela/goblin/README.md b/Godeps/_workspace/src/github.com/franela/goblin/README.md deleted file mode 100644 index 2972b15..0000000 --- a/Godeps/_workspace/src/github.com/franela/goblin/README.md +++ /dev/null @@ -1,132 +0,0 @@ -[![Build Status](https://travis-ci.org/franela/goblin.png?branch=master)](https://travis-ci.org/franela/goblin) -Goblin -====== - -![](https://github.com/marcosnils/goblin/blob/master/goblin_logo.jpg?raw=true) - -A [Mocha](http://visionmedia.github.io/mocha/) like BDD testing framework for Go - -No extensive documentation nor complicated steps to get it running - -Run tests as usual with `go test` - -Colorful reports and beautiful syntax - - -Why Goblin? ------------ - -Inspired by the flexibility and simplicity of Node BDD and frustrated by the -rigorousness of Go way of testing, we wanted to bring a new tool to -write self-describing and comprehensive code. - - - -What do I get with it? ----------------------- - -- Preserve the exact same syntax and behaviour as Node's Mocha -- Nest as many `Describe` and `It` blocks as you want -- Use `Before`, `BeforeEach`, `After` and `AfterEach` for setup and teardown your tests -- No need to remember confusing parameters in `Describe` and `It` blocks -- Use a declarative and expressive language to write your tests -- Plug different assertion libraries ([Gomega](https://github.com/onsi/gomega) supported so far) -- Skip your tests the same way as you would do in Mocha -- Automatic terminal support for colored outputs -- Two line setup is all you need to get up running - - - -How do I use it? ----------------- - -Since ```go test``` is not currently extensive, you will have to hook Goblin to it. You do that by -adding a single test method in your test file. All your goblin tests will be implemented inside this function. - -```go -package foobar - -import ( - "testing" - . "github.com/franela/goblin" -) - -func Test(t *testing.T) { - g := Goblin(t) - g.Describe("Numbers", func() { - g.It("Should add two numbers ", func() { - g.Assert(1+1).Equal(2) - }) - g.It("Should match equal numbers", func() { - g.Assert(2).Equal(4) - }) - g.It("Should substract two numbers") - }) -} -``` - -Ouput will be something like: - -![](https://github.com/marcosnils/goblin/blob/master/goblin_output.png?raw=true) - -Nice and easy, right? - -Can I do asynchronous tests? ----------------------------- - -Yes! Goblin will help you to test asynchronous things, like goroutines, etc. You just need to add a ```done``` parameter to the handler function of your ```It```. This handler function should be called when your test passes. - -```go - ... - g.Describe("Numbers", func() { - g.It("Should add two numbers asynchronously", func(done Done) { - go func() { - g.Assert(1+1).Equal(2) - done() - }() - }) - }) - ... -``` - -Goblin will wait for the ```done``` call, a ```Fail``` call or any false assertion. - -How do I use it with Gomega? ----------------------------- - -Gomega is a nice assertion framework. But it doesn't provide a nice way to hook it to testing frameworks. It should just panic instead of requiring a fail function. There is an issue about that [here](https://github.com/onsi/gomega/issues/5). -While this is being discussed and hopefully fixed, the way to use Gomega with Goblin is: - -```go -package foobar - -import ( - "testing" - . "github.com/franela/goblin" - . "github.com/onsi/gomega" -) - -func Test(t *testing.T) { - g := Goblin(t) - - //special hook for gomega - RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) - - g.Describe("lala", func() { - g.It("lslslslsls", func() { - Expect(1).To(Equal(10)) - }) - }) -} -``` - -TODO: ------ - -We do have a couple of [issues](https://github.com/franela/goblin/issues) pending we'll be addressing soon. But feel free to -contribute and send us PRs (with tests please :smile:). - -Contributions: ------------- - -Special thanks to [Leandro Reox](https://github.com/leandroreox) (Leitan) for the goblin logo. diff --git a/Godeps/_workspace/src/github.com/franela/goblin/assertions.go b/Godeps/_workspace/src/github.com/franela/goblin/assertions.go deleted file mode 100644 index 5ccae7d..0000000 --- a/Godeps/_workspace/src/github.com/franela/goblin/assertions.go +++ /dev/null @@ -1,59 +0,0 @@ -package goblin - -import ( - "fmt" - "reflect" - "strings" -) - -type Assertion struct { - src interface{} - fail func(interface{}) -} - -func objectsAreEqual(a, b interface{}) bool { - if reflect.TypeOf(a) != reflect.TypeOf(b) { - return false - } - - if reflect.DeepEqual(a, b) { - return true - } - - if fmt.Sprintf("%#v", a) == fmt.Sprintf("%#v", b) { - return true - } - - return false -} - -func formatMessages(messages ...string) string { - if len(messages) > 0 { - return ", " + strings.Join(messages, " ") - } - return "" -} - -func (a *Assertion) Eql(dst interface{}) { - a.Equal(dst) -} - -func (a *Assertion) Equal(dst interface{}) { - if !objectsAreEqual(a.src, dst) { - a.fail(fmt.Sprintf("%#v %s %#v", a.src, "does not equal", dst)) - } -} - -func (a *Assertion) IsTrue(messages ...string) { - if !objectsAreEqual(a.src, true) { - message := fmt.Sprintf("%v %s%s", a.src, "expected false to be truthy", formatMessages(messages...)) - a.fail(message) - } -} - -func (a *Assertion) IsFalse(messages ...string) { - if !objectsAreEqual(a.src, false) { - message := fmt.Sprintf("%v %s%s", a.src, "expected true to be falsey", formatMessages(messages...)) - a.fail(message) - } -} diff --git a/Godeps/_workspace/src/github.com/franela/goblin/goblin.go b/Godeps/_workspace/src/github.com/franela/goblin/goblin.go deleted file mode 100644 index 36eb54e..0000000 --- a/Godeps/_workspace/src/github.com/franela/goblin/goblin.go +++ /dev/null @@ -1,291 +0,0 @@ -package goblin - -import ( - "flag" - "fmt" - "regexp" - "runtime" - "testing" - "time" -) - -type Done func(error ...interface{}) - -type Runnable interface { - run(*G) bool -} - -func (g *G) Describe(name string, h func()) { - d := &Describe{name: name, h: h, parent: g.parent} - - if d.parent != nil { - d.parent.children = append(d.parent.children, Runnable(d)) - } - - g.parent = d - - h() - - g.parent = d.parent - - if g.parent == nil && d.hasTests { - g.reporter.begin() - if d.run(g) { - g.t.Fail() - } - g.reporter.end() - } -} - -type Describe struct { - name string - h func() - children []Runnable - befores []func() - afters []func() - afterEach []func() - beforeEach []func() - hasTests bool - parent *Describe -} - -func (d *Describe) runBeforeEach() { - if d.parent != nil { - d.parent.runBeforeEach() - } - - for _, b := range d.beforeEach { - b() - } -} - -func (d *Describe) runAfterEach() { - - if d.parent != nil { - d.parent.runAfterEach() - } - - for _, a := range d.afterEach { - a() - } -} - -func (d *Describe) run(g *G) bool { - g.reporter.beginDescribe(d.name) - - failed := "" - - if d.hasTests { - for _, b := range d.befores { - b() - } - } - - for _, r := range d.children { - if r.run(g) { - failed = "true" - } - } - - if d.hasTests { - for _, a := range d.afters { - a() - } - } - - g.reporter.endDescribe() - - return failed != "" -} - -type Failure struct { - stack []string - testName string - message string -} - -type It struct { - h interface{} - name string - parent *Describe - failure *Failure - reporter Reporter - isAsync bool -} - -func (it *It) run(g *G) bool { - g.currentIt = it - - if it.h == nil { - g.reporter.itIsPending(it.name) - return false - } - //TODO: should handle errors for beforeEach - it.parent.runBeforeEach() - - runIt(g, it.h) - - it.parent.runAfterEach() - - failed := false - if it.failure != nil { - failed = true - } - - if failed { - g.reporter.itFailed(it.name) - g.reporter.failure(it.failure) - } else { - g.reporter.itPassed(it.name) - } - return failed -} - -func (it *It) failed(msg string, stack []string) { - it.failure = &Failure{stack: stack, message: msg, testName: it.parent.name + " " + it.name} -} - -func parseFlags() { - //Flag parsing - flag.Parse() - if *regexParam != "" { - runRegex = regexp.MustCompile(*regexParam) - } else { - runRegex = nil - } -} - -var timeout = flag.Duration("goblin.timeout", 5*time.Second, "Sets default timeouts for all tests") -var isTty = flag.Bool("goblin.tty", true, "Sets the default output format (color / monochrome)") -var regexParam = flag.String("goblin.run", "", "Runs only tests which match the supplied regex") -var runRegex *regexp.Regexp - -func init() { - parseFlags() -} - -func Goblin(t *testing.T, arguments ...string) *G { - g := &G{t: t, timeout: *timeout} - var fancy TextFancier - if *isTty { - fancy = &TerminalFancier{} - } else { - fancy = &Monochrome{} - } - - g.reporter = Reporter(&DetailedReporter{fancy: fancy}) - return g -} - -func runIt(g *G, h interface{}) { - defer timeTrack(time.Now(), g) - g.timedOut = false - g.shouldContinue = make(chan bool) - if call, ok := h.(func()); ok { - // the test is synchronous - go func() { call(); g.shouldContinue <- true }() - } else if call, ok := h.(func(Done)); ok { - doneCalled := 0 - go func() { - call(func(msg ...interface{}) { - if len(msg) > 0 { - g.Fail(msg) - } else { - doneCalled++ - if doneCalled > 1 { - g.Fail("Done called multiple times") - } - g.shouldContinue <- true - } - }) - }() - } else { - panic("Not implemented.") - } - select { - case <-g.shouldContinue: - case <-time.After(g.timeout): - //Set to nil as it shouldn't continue - g.shouldContinue = nil - g.timedOut = true - g.Fail("Test exceeded " + fmt.Sprintf("%s", g.timeout)) - } -} - -type G struct { - t *testing.T - parent *Describe - currentIt *It - timeout time.Duration - reporter Reporter - timedOut bool - shouldContinue chan bool -} - -func (g *G) SetReporter(r Reporter) { - g.reporter = r -} - -func (g *G) It(name string, h ...interface{}) { - if matchesRegex(name) { - it := &It{name: name, parent: g.parent, reporter: g.reporter} - notifyParents(g.parent) - if len(h) > 0 { - it.h = h[0] - } - g.parent.children = append(g.parent.children, Runnable(it)) - } -} - -func matchesRegex(value string) bool { - if runRegex != nil { - return runRegex.MatchString(value) - } - return true -} - -func notifyParents(d *Describe) { - d.hasTests = true - if d.parent != nil { - notifyParents(d.parent) - } -} - -func (g *G) Before(h func()) { - g.parent.befores = append(g.parent.befores, h) -} - -func (g *G) BeforeEach(h func()) { - g.parent.beforeEach = append(g.parent.beforeEach, h) -} - -func (g *G) After(h func()) { - g.parent.afters = append(g.parent.afters, h) -} - -func (g *G) AfterEach(h func()) { - g.parent.afterEach = append(g.parent.afterEach, h) -} - -func (g *G) Assert(src interface{}) *Assertion { - return &Assertion{src: src, fail: g.Fail} -} - -func timeTrack(start time.Time, g *G) { - g.reporter.itTook(time.Since(start)) -} - -func (g *G) Fail(error interface{}) { - //Skips 7 stacks due to the functions between the stack and the test - stack := ResolveStack(4) - message := fmt.Sprintf("%v", error) - g.currentIt.failed(message, stack) - if g.shouldContinue != nil { - g.shouldContinue <- true - } - - if !g.timedOut { - //Stop test function execution - runtime.Goexit() - } -} diff --git a/Godeps/_workspace/src/github.com/franela/goblin/goblin_logo.jpg b/Godeps/_workspace/src/github.com/franela/goblin/goblin_logo.jpg deleted file mode 100644 index 44534f297b14af552cc040a32e29a85892d364a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36893 zcmd421z23m(kQxy!QF#HaCdiiffH-L>eN`?>p709{^MP8t9~3Im7%0Pfdt?WDY{EdW4C z37`i603Lt?VFF+X1cLklAOZm9Cky~4Aj03U4T$zH8Yl=28vwZjNRZ0~#Qqb;hQR*e z)&TThyor$SDF`K`#h+4te#^)ysgSWV^D?so06Qxi2R|DtKN}AjD=R-c7e6Z>0Kmip z033t^nw6c6HS2Gfjg>VE^iL17p#E+JWRL~^8wM^Qd;kCo0{=w``=>4-@_%#z(f_Fn z6zFdWAa@$S9d<$jBKfh=^!;=oneqI5{~V z!oedTB0&i1&;ckA7z_mshJk^Gm=E+EQV&35z+gUR6NAN4HHIU1#%6z!kPS~EUe$@C zHhE0RVd4^mfQb7D51)XFnueB+o|B84hnJ6ELQ+avMpjNBqushPQjrIodf ztDC!rr4|-uh^n!wh21CO= z=mmoEgj`?@Xqd-ru$W@1aK_G9EM~ zPqCo?E6si@_K#lk05TW^F&-EL5CJZ)QLt5@v?-o%=-9VN358a~I6t?v<5qTd*n6rF zovgvTQYW7joasZb>kiixr`YUEsonhaSy$ym8|uXKn#i?VK5J8!FndS6%R6})k3+Iy zo;Mtf;XiT|esCiK2b>w6inEWcId5v;WcMVTR^p$V3l?8TPF9c~YD=B;Wq5!VI0*4G znwW^9n#uc%dlIUn?*X3=J^+V8+R0b6y-b=E2zC_Wr}Bh9o`Pwd_TbM2Ctz#%a~^phW|<{A0TsFdB)g|$ zGxJpCUz(f>ZIR%+rNbxlP0;-dp;^Su6=E{%%@}6ruNrN&76H366Ar*M_K{uNA&SK8n zl;uJ(sg2R734!Uu3@b(C&)(tsQGL7>ZJ!q=sUdh$Y_f6a<3iT`Vtk0-EUwvyM>auAaQ7n?E{bvt=3+I}H$_#XF&`8GJg9_mlcCu@$7S%UDi^I1{c`&0bdG z`U<6y6&z>$UI4LNV?-d63 zTIqw1qNQCGD+8s{&W^YGMUi3mJzz1Crg<@)*PYWc%BggqI=Jy;k<_Q}!(?B}sSU2N z+dG`6#9?0Fk)7i!?*Sol<4=NdCMbZ1MB0|0^ZLwjUn;VPO8HEU-i3pcHIDZ7KhoSvn_8ZhZ?+|1+sP)~ES$uI2cc!-Sa$p4wly?0pBP3_$8H;@z#jvhRUP0CU?z?FekIPV`M7bBNXR0>d=E0&HO!d0Oi2Z zZ1bF*<*V`ddPVy&J>dAVjAlxZV@uN66R{S4si!Lbq+xxs^2en7k+T8=RP?pkrej?|}mm-O*#oUBo>f-c{XasabG$UZXeDw^97} zK!+8Wp^IIA?wMD&T#cmfu#iqgPUzSqrAtZUvu|T~;JNjt43FV%f4UQom_q9rPXhl! zUl%>?hWTT*AhxCy$(!<~_h8qHiiTd5#VgC639qrCWw}FS5%cNQm*&x;Tjt;<#kV(F z?P2cv0=qrV73s^!KEguVJ%%E|}NJN?LfvgEw%eY$!xX?rUuZ0a~_6Np^yG91! z0Yn+RYcTWk_h>fjZN#@$Gq=j?vIkuDB-qtWNkfreW_XaWLoiEv5>Z8kU+oIBO&U$t zZrMgjctaZ}t}pqY$G`7}xd*J!D}+7~??38TK@}8O%l(m^yRy8rFtThX$GDcG)kzo> zC~Sm)FBl1YEhV^sX^*HKPF}0PRnURE3{)piI;Ri#nyUvL$o>Szsd+o*oR~r_TzlFv zg;*HItDH4I?TdQML7=-P*x@Lo>QOBy9YgxJXGHvz7;+2PaEJOLr0zOpcbKGb0yXajF*~nYX!@eB(jBSe6ItZ) zqnkl=%B^q<6W7=d&@3lt#H$+bmTmoqNFS@tDEm;1@pIx@z0G*m5lPV05hwsdu4Iot zzv0EL*t(DjmL+G3LrT3=wUIcyO8?D457s%j&OMVn%)3Rz_nY*BJCXELw7jS@mOU6J zVX~RElf&__Tb_bpR@&S z%hyhkIWG2+4~5^o83K_8qKbTt%yQ1X`Zn6JfHRlaCvh<{Njg5Ns(NKWS<^JdiQvE0 z^6_)1(7N3)+FDgcz$)igY%y2&wVk6?HOKNE&YC%1nd(SJNp@udhaS{en;$6cZIv#(rG)K%)H`WJTnp*%u{N z+0{|E!i{sepp3CGDC4lQscZc=E3S6F_6A94%hl>t{2`;-AaMnHLPTf#Y zn0p-~zStvEHt~)PZmKY?j=Q1KT322_s#ScMg_CNjn!`LJjQxcSjev~Utwm$hu!N^+ zp)JYd8Fv~5dlLnHC6viXUU=rnfk%X*RDAnk`+7XW6qz+cib6ChIbaMm%8Ef^!o8d>>J~{Km|dLJ;amRZ(UDtIs06|pp-t|)tP&sYdEy= z$~_+k8h(1c6OY37{iZ+8XfIk%0G3x+w^owkCqWUM!~yafiPTMVClEBF`RXLHgK-tX?@)eadzInN|RP4f3=zP5FPI}qH zj&ej=Fy=~EvwkUA5MLs6ynKC^#c4wjynSk)3&+KFx&5RRfUPP8;0$^p-3hs`+@Kl+ z?6}X&QmwLFUChc9Ge{;c!hy-&o#28=j9TLbbOQj|hxMIWu;P2iG zA6|?JXa)2L6x(y^p;ax6ItTp72J3bs2I(g>yxL<$WI)^>b$N$w+7{KlTe=(7b|#nM zh{-{6ya&>ThC=v`9|u=6W!rGtHfFVk(!7sT37|PbsebDsL&e+Q?(<Vz~f zhc5`a5KbNy(Wss_wzhLH&A_Rz|1|VYoIm)$hRINYAP>D{?Auu$JeGV}uv?z)E4R(=T|vQaDG=>F|HONY|4fwNqT@=7 z_z7K${5Q{;T>@Ke%1&og?Tqq_jv)TaQO~+guYf1bNj)S?wgM|u?X?457IC9tQdXvd z71C%l@iJ{*9dL&EE5p7-#mH3ZLLoNbLt|*~vxrZcOfqId8-1k=lf&02 z+5DzCIG<$%$^^>g|F;SFpbcFMx>%taNiAq|w(5w4@i--J?Yrd?e``y+t2elOCY<$~=YzMQ0(ZC>AMSyY z+fl&tb1oNT=j-a2Wp2|K=^!`=Ip+}|BTXmpS-O>;O>k`mw20{|TIp``5|BF$dP{O!T%@;l{)q5` z6o0=VZ6X9;7hi9%E*e$tbQXB=H7)-*Ymhs4U0~*geRiuCGt=R;_{(hib*_%xQy7sL zz{mbSgtNcHTWUJ1EU1=uauJVbYlj1~Wc^f&di2^yiNZ;*c5X{&7zVbLjJ0m9(!5%y zYQN$P^LKo$wM(QHh&T}T0WL}y#w@O0JQwM1)i#LiEw3ZA?X8O){O(MBHI+@0mvT$b zcgOfS#-K}kuH^MSNA|NuTg8v%issX=6%IAxB9T*o&|BNLd$P9-ggt5X#btZi^O8O;%u$8UWT@_PP5~Tr8E$M(2YBIBoTlT`87o0TTW6Pi z2ghZFU9mmnvS;8c3{hgG|C99|@Xq{g<49g(OfI623FTNfaN`o#srI^sGUHc&rKNy#eVg=sR!j;K-w+<_?KBK}J}>6Z zrfSWqu3aHnp+=YMS5Fb4rTLK{$J4EhG+f?Y+G{e?pgK~MU{4`K*!(tVKv;de&V5P+ z%6`<9zv0DstMsfk?(R2%C8dotg~mP|^Eg-@Qt_;tvweMG=cg={0oE%Dg;rP#%7QeH zc%Z+=E=-`qFK*v7;q5(#hhr$Rn$Vq_yA}Clp5|RSx4759_&I>a!^~5-nlu@$GT(=t zyf5-4R7oH}9O|Nr`u6V8)V98~y0>t#@J0!1x4=Ds{Dhwt9NJIZDCBefZ#Psvz1w$pZLvrv5)q9Vw`6(>ioq)6<4Oxby$8O<$k zpAugiO*bC%KQI1z{=-(YYAUZk)uqM}GoF>FuiN+*sHlhpHvQU3cK1WlC8wu6Uzydq zes_F!rxD&m!VwpKtmyFEWLhO|TFU)g1&)mCD}k(2CQ_)-)^t4DBnmGnsR63C=qJz- zfnEV=7`nEe6ySWg8Wp2r+T;JYeJb~>JuZ`xh~^X7>#A}-KMr$SYqW1nEd#X2%ke1}7>7wwq9<9euiLT_b$4gAPM?wI@1}a>Q4#Ax;*0Me>=ITCAG)#SLP5I1dM1&Cqz4*QC zo$SrsjLE$0?HpYBy@Y6f3g?Hw4`>z|2$74K1;3i4^j{>9Cn1`@`r_&7$?VC&?C4_2 z!p6tP$HL0a!p_bFp_}O^*IXRhFIrv#w|KR>b`8TtYqnWjZ_rGE0`SRM@U$Kd@Z18Qdadpw=oUF?2Z#>|w(+|JzI+`-Ki z;wx+qCWVZtnJK@8ql>+|Ef7{}B^Z#Pb-_<{z^;ZXe zm# zZb^1ANj3>d4n8(cUNOmE#(@z26!Ftif64f>EC05|KUDhv(FmxUJO4i(fxl$@S0nI0 zacszpR#N)+&h_^iXm9_QYyYI>mveA+Glp1LPEr_xV79h4<7egJWo2XI;bi4uXXWN) zW8;-z;}n+^m*(W)<>BPul>RyOm6ZNueej%L@|0byAq%Uq-QTwSORm|2T+aWV++F6OR|?k=Y0LNpJf@!#n+4H?UWC;We;zO41bRP_Eej~+VwwV^tMtZ-F{1md@7^Aju;>G9X76AIp?T=DlbOBQ1Kb0FUwXKkLtwBX1P=4CHur?UQxKTa z&fVS`0$)L3Y@&n$@+|=sffEofLyJ)CMKwv@0Av&_eR`_I4!+y1IQQ~hZ(5bnr7xPLhR!8zst059aU9UT7;&LkB88bSd8Z~h+~ zWflOSg#bYP_#gV91NZ<5KmpJK zOaME;0|)@3fD|APC<7XR4qyP70#<-M-~xC8e!vSL42S~afmc8p@D9iWih&BC8fXAo zfKH$f7zQSQX(0M^cl1U+6A40uA#tC$WYiA{wEKs~q zVo(ZD8c_OBmQc=6K2RZ0u~2WIa-hnf>Y+NJhM}gRR-yKxE+B~}NMKy>V=xn#7c2o* z2J3R@_dreU^VzQMx6;=cHB-`oc!TX24d$cEC=;ZopnZvQO~g7~q89l;Mow+~C6CQsK(r+TlLJZNXi^ zBf*oxv%^cn>%u$02f?Snm%z8de}vzLzd=ApphDnBP)0CA@J5J3$U|sG7)RJbxIx4~ zq(Kxy)IhXBe1Z4|u>!FdaS`zh2?>cDi4RE?$r>pT=?&6*qyeNgr0>X>$n?k($j^}7 zkz{}AK&M8RKsQGBM^8s@LZ3yyz`(*_!%)Vs$B4!#!5GBY!Gy!4 z#+1S|!wkX9#q7r1zyf1YU`b$^Vg+O6VfA8dVZ&n6V9R0KU`Jt>V~=4U<6z=&;%MP` z;iTiV;;iC=aj9|TaP4s8aI0}=aIYT`KN5Rn{wU&6#iNgpF7XKQMDfh=BJnEmrtz-v z$?&D{ZSfQE8}OG1pb6*+R0%u@G6{MK4hXRc1qe+EBMGYs=ZT<*=!w*be2H?2Mu;wm zNr+{LorqJ3yNM4-a7jc-Y)Fzx+DLXuu}FnUtx1zf+e!DxaLB~S?8x4b^^hGyQhj6} zyFbo;Jofm8oQ7PJJczuKe2D^)f|tUABAKF#;+T?@Qi;-!vXpY33W183%8KeWRX^1w zH4U{kbvShc^$raljU0^+O)1SHEefp&tut*7?Gzm>9WR|NT?X9*Jrq3`y%l{r{Wt>@ z12=;;Lk7bnBMc)SqXT0O<17<0lNgf+Qz_FLGcL0ta}aX_^AQUri!Mt7OFzpUD;KLB zYaZ(Y8z!4PjyUv zw+nX#_bv}5j}gyXo>^XOUKQSG-T^)sK5@PPz81dk{M`I*{MGzl1(*bE1xf^V1Zf1# z1@iG_7Y^#afrzlG=&dt4~>;K7Tr_ zL!e`=)2IultEF3@d!Z+#_eyW;8P~J0XEXXV`d<2j1_TCn2Car@hQ@|9MleR&MkPkK z#>&Py#up}XCT~rSO(jfUo9>&5nkAdPqHa=>)3b?9*x9_b!eo?4zYUKm~uUL)T0-Vxs0KGHtr9|CEn??`Cu*JNIxsSDo9gE|R%Z!JO zcZi=!5J@OX#7Oi>+(=SNs!t|Mj!Zs(W%z0^g)`;dYsA;?uUFqFzG+OQOpQ;yO|wdy zP8UzFd`t8;;_YRIS;ogq(aef>#P6csU1wQm&1K7E*XK~@q~yZpdgOk|)5`15=gluJ zASj3`_)+LkxLTxE)Kkn;TwFp_5?2aU>R!5A_N;88T(Z2ef~g|A61OtyJ@DTB{a%$( z)l9WQbyp2k?) z4mAvO57&=yjns{Djn<8EkJXR!j5kj3O*BsmPPTm%`PlhM@>AcG+|=;&lj+GB&6&B` zXS1twW^+5A?LME(d(7V~1TMlXMlPW*r7ROI=d94JRIYNbwysI8jjXG$FKn1@>~Fem z-fo3_LHUxhO}bsY!@kq9E4@3hr?0{&fo=0*O)3k6B&gZf?h-v#cw9`YhDKn7qCBqi|gyhsvANCOcD5&jUW z{4DSYAUG&6EDSUvB)<^n&zfKLp!>hl^U%TQkcgr~{X_;h^AFT(;h!iw zs17zpE%0P>#h$@|;Uc(HDNrXKxcOgTMea~07+aRYZHnk5!PYahT!-a~q`MxcAAIaB ze>X|D1s~!=KJ=u!C>v|(k%LDdgbCEKt|`Yj-YiqyrNkK^By%oruZENMHi7JmS0XA- zPk^*cx=_TxNx^3_44Ztc6c@46ttYDxK7iF7=e@jZ1O}2IXB8F-rO(+37UfcS6RRIP z8==)F=tdFiflidsnE)=Db2B^gqT$y9q7Q7ul;}M8d+_KwP3Q6{l%Fx8d>Fj)&#nO) zR`O!{NM(`C;A=l7Rt&S`P}L0RLGlPINS@NqfDaX}lYZpA>uMVEI~b;vk6O+SQsl`b z0U9u&spQ%8@~QN!cZqV8&g9mNqiVL|%t?}vl%1Z z*J@Gn%&zj_Ij!FhzI5!c&K<_&#TgQ>zDCaIc;Eni)c#B!SEBvd7VabcW-V6Z7paXi zqBvO1Y?=J5M|01ysK>0#}aPl%; zNKkwQw+nR8x72RX=qx_a0(kuLUDODRlBa`1txQA%KxcvMTdz+mgifytS&{MS7i%R_ zgX4cv8tpNZCHFfU_P&1=sVfQqk~JEHJ@NgJw?{3IWgi3qDWDl!azrfFX-QE*XAelA z4BIG}dV)Y;1|Z`E>74wM12}VLKcUPKUMoY((sRBE{K=^K-SUg*PY4v0A15tOUKK+m zA2c-j@IDHOy5A!S@%@A#ar)=1Vdls9%xKFpi9fvt889mi>Kb=*`9%N+E<2=k`)+nC z!eSu!b>b-t( zODwD+fTH4)G#dySSeK}=Oke#)0vPSGt48d`P3bzH#Rk zgxlJ);mn=0?Hm9Mt4QK@Lz5(eFClaO^sfPhSQQ7JQ}Wa&q2-vmm-_pbvZAfJJ;80X zbQE_nKjID*oqZwM-{RK&#fy+Y^tEp&j|C!SX;KN%TI^i-aBtn??JC3OjY4iyV88RL{(9m7`w-|s(-kmy|5p4p3K;zV2tCPz!`&~ysdu*1^ke6Xu z$8H9OMH>>uQo!wyB$pwC|4oAKH3yf(J%}^T>`od@9KJ6wM!Ch^i7-wgr|uj0Hx)Q5 zuHZ`eMG}Ay3$UXNc_po+K2(7rt04P|ihraZCK`fLi*g(_NW$+5%*IwZ zwn{JD!_LKDH}!F#u-uK2W?T}<45gL;F{O;S_@yVIe)xc8ma&nwbILaK>Iu@-w{NFA zhzSIB-#3<|cxa!{&jliD?m#i~*9anNVa4nEsl7NmQQK30t(BaYqqXrKlcbs}mn7-V z=De=|OtLHc-2ojDf!>_H`^ginzD(IzL|xI`14~uW4(F`h4N|IIS0c|qcG!}wqwUM5 zZJkX*ee;pBJG;hA5k!{dhJ8Ooi{1n-@g@flcb6elawnEap!UNzHWSCpoJfV8v{o{T zu{WlJ=&%u0nmVy;_~>n@UT2!Xze=Gdo~`&?KnlD@1*PsL5Rb2U%+>b5P-Jq;btj7J zmd9Oq+@i8tYmgZewW8S@wPNniZKV2E-P8=#<>r>HmlG4|#eaB=u97F1KucDF+3*%G z&J1z8Q?XUgSr>+a2&P&VOtF@zI=L7?h$tO{{IxjKyZGvdZRNOuoq&8OK%q?cNQiw& z1G)xDzJ;9e$a*Rw<$x4S%meOMtrj)zDN^KWfkvD%b zHZhE!st|3{l6HsSvicDmu};Fv=)P-t>}$8p>uIM1?`KoZq8?82pYeOsqQ|wa-WA^6 zzS>BH9veL&=tW+K*39+oWapd}+W!!l26IL{^|C%N+Q_e|jKXjsyud&aW1}ikwYk0q zw=)N2G>TtdhqhpusE=3aHJW%*7GJFw^U>s!aK5`e>+Tn|z3XiBWVLJQCd%Ke`gn=5 z;%S05Lj{P=W2jBCq#7*fz~)X}JVGu`QN^?`xem3&#K?Sjr)0uMmEpCkN^zKECVP94 zN2@)44WAk$0vZ1~27 zAm0@kfh_k|dS}(qxtcFr4D1HF`KwyA)Jd3s5a&)KytSuFN z4c*Z-SJIrwIRk1G_h1s{b&6PEduiUx31_*E=O{s1@AdNBC5#kKi^3x*i0vtnyH9J2+-9D??%}!U|N10x>%q^@=lK?rDYRGk>?w)y%w>94)7v!XX9|0 zQi3+5b(U3Vc}bJZsfRa~T$Oi?pU5eVSq&R5)(pj-Upd?*%D|dTA)@qRMFD_I35Y{ZYr zg%Jkou}U%H@T-@}#aAYx+=MA?-I6B_0%z^NKB&lPqEd4tlL^(H+x`!q-Ia7u@Km?6 ze}E>@)kZ=If|}YZy}CttA=u1i(Z0UXUZe@%!pDy&-Za9*h)yUsel&;!oaJ1fMbNzL z6fQjv@2!-1V}yTO?E`{G54-q2O$Tr7P!K3+S*vbKF{W)sh2g+`#AsAfRPXXgM^SxB zDD7Q&0+aXkEauro1=W_tT;khNVTnfG2CK(z8ZL`o8!%II2yZJEg1CJor5}}?WVP96 zyEU1>Q@5_8;kFbiW#0q&?|nLUeM_@cTvu>KRrZ!GX7KcQ7)s(>y>HY*zdu|`nxVTR z1cOLM>%8P-5?WPa9#0yyQYI`SN0~yMrUjKYJa%hl?r60%KO8fDUu3zq(j`8tw!zDN zP9QB9^Qz{gE3wtS@e%u!7h6ICG5<>3}` zNR*Qa(fM69=cy&^NW}?7(Rb{+3dE;ovRTEGZcVHW5tV>P)d7PhbsW6{_S*|YWySE3_YQ=SZ(h*#p8(7h_ z437p~1yuHR)%0Ud8G7+pi@V{~R)ccm9}i-La7xS2ykhN)p8L|?>FCcg6M@r{!V%oL zeuI?IC{<&J%K7c!gxYpOFy<{e5Z`actTt@zREyieMlS@V0589U!y)19(2U5-2KH9Z z;`q2gp@WjBg!?^$L;t;teab@Y3bUHEHZmT~8~o#f-q$2{0azbC*dkefObt`S4YR`` zH8~xN)Qru=A|)iI7>>gAQ;5b}<5r(Ny5fmzu!z_6HDD&I;c^m5b@ z`>~=H-ZoH2PY}76%Q|LotA85!g z$)I4N;oy;wksf|{1_k*69R?-2sQ20BEAvPNq9C%_Zsz%yymROEF*JI)tADLr z+@LD$=`bKR9O=TN?K`Rc(o^Kf7BvM|9L^tQer~zH2Dw+2wT%&Dl>t`;ik!UCqd`-0 zHk4aEQD>gV_3c0ie`JfmYkiOb>RYkh+fjc%nd2m7^H{+&<-RX+KAp5HIlNUkHy_*d z+@|5jhA$J;1BZv26h;ULHsCGni;R7ql)YfUIN+smGw$V;ou0<2G+0Cxw5#UQ_()G- zyh%Yt$0INpL2kkgep$L8ovNuF;4CdR-^Szymm%*QqEr2QD7^ID(%- zZa_ll(+RIOU8G`7RKmZXJ^RceX=xfD4JOLhJUw`e7<^nw-g_25F*DyTSj;%n>)sRo z?&G8b$%j=+Q{pbKFmuPEwwcnUxG%)$X9r@%C6-sBbGy##_ki2dW-#E6fOwlHO44?@o_BT3fb@LTxRIbtni&;d#7t zU3S7U>8`~S_h>ZJeDkw!#A0n-p*t6p&EXOYGL@z;enDEUOeJRSsu#57lD>S5S_+13 z-hxh7MBR2eN3Rf-Z1i>@vCvNWw=305zAot-3EE7ahHrI;Qp&j~FIcyzG?9mQaH9Pq zCmDmJ-&)l9olEhYI}N-y8Azopt#w`xNko>qVimW|+*;+<(MU0#KzzB@6OWVRApP(9dw=Ys*PrS9 zD~OBr+i@CwY^L07iN}%ss~e_0RbFWOe3(BZ>|%Is(S)J;P<(T8ZYKRhH8rH`a5S~6 z$!Xj*4JjqpKP2PvEjGqz%nT16DYC|$an>h)BB@nTZWPOHk9Bb& zRrlky0o!n~h21djGS_#;L*Ww7HH)Fac@%ZYAOy3>4mMv@UV_X+uQE3=N_oFn#S)~n z8NNX+iKjhmWCO=GZ6s_6WQb_0<$H9KkCys$hEp9pqqdecO*Qf1n_H(`hxQSJ{5;%- zHt*tc*{Z{|p)?pBmL*+LJmRH@$PXxpFAe%Vr7l_UDg|kycqsZRKl<6-MTgMI!UpY` zNk6qM*_(ZdqLsST^W2lZ>POBd?`U9QN8;4t%c4Z%bY%m|ox4SAaf6{*|EXz#A2`$4 z@gneNBhjN#ai%7Ff_Z8Jg&UO7D9(Zca-YjD_@gSKK6{}$jw!&5aj55-QQ75-Z@f8e zQi%yh#Pt7`T=jvxB7=BvukLvR7WUQ!+M0(LlAU!CR(%at?+rOu!(|Z`G&PoQ9&N5* zzT`KZ5*A_w2b1nX-%r|NJyl+Gcu2SDmYd9VsgDV-eWfaQ3SSfMduPdoJlR1`!Y)Wh zkIYo^-4jCKdim8So0r&-hjobGTWx^%Rg;}6;x=kCn%g!}G$)12^@yI-g#LoewCYTlP|UQUp~I!GkTRTb%O zUpwO2%aS9`Yo@gZ)2JCtL<26ntpi7cm6(*;62aO8Z4{deW*+i9bJ)ff1qtjOG0QX7 zw|d-Ff(b3crqttGN9X}pTnT}l+xXa0M=9*ebonGqhExI}N`wV1woOO4AG~DJ@giIq zl*`{Gv`I?{i|>_G3Z}aZyRNLnjPzzr%iS@12p9H$c@dzCf^Y`x-`#T zqsX$$T!LPy#Pvc`1-v%wc7PlG(H8xY!MAa)bwT~x6`19^u|ManDeqIeNa%R{a+8XU1<0;zo`R<<3GS?SKes#y z9pU#?BxNj$dVEGVjw>c6EUC(;p_%X1XOyOcp9}4#RMPBD67eA_)9;I-jK0}c!Xw<5 z=i0GpFP}K^bZg%*QRq#W>_nE?zP{>RfA{*T_EoTVQnh5dUW%tb+skfO7;6XU$-!Nn zdKWrLL6OU_Br-8i(QvE!qIQ5;|c0d;daTa68I^D66Nx%etd0?!Ir z`;FcE#W_zVpr-YT~Ue&#o9r?jVEE>`CiN*?+<+y(A ze3fGIkxzD->FI4|$?G(6Zj(l81WNXFga6GrGW!Md_Lm*
  • +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 W|I)^Hm^PjO0000>>" + text -} - -func (self *Monochrome) Green(text string) string { - return text -} diff --git a/Godeps/_workspace/src/github.com/franela/goblin/reporting.go b/Godeps/_workspace/src/github.com/franela/goblin/reporting.go deleted file mode 100644 index 1d67d66..0000000 --- a/Godeps/_workspace/src/github.com/franela/goblin/reporting.go +++ /dev/null @@ -1,137 +0,0 @@ -package goblin - -import ( - "fmt" - "strconv" - "strings" - "time" -) - -type Reporter interface { - beginDescribe(string) - endDescribe() - begin() - end() - failure(*Failure) - itTook(time.Duration) - itFailed(string) - itPassed(string) - itIsPending(string) -} - -type TextFancier interface { - Red(text string) string - Gray(text string) string - Cyan(text string) string - Green(text string) string - WithCheck(text string) string -} - -type DetailedReporter struct { - level, failed, passed, pending int - failures []*Failure - executionTime, totalExecutionTime time.Duration - fancy TextFancier -} - -func (r *DetailedReporter) SetTextFancier(f TextFancier) { - r.fancy = f -} - -type TerminalFancier struct { -} - -func (self *TerminalFancier) Red(text string) string { - return "\033[31m" + text + "\033[0m" -} - -func (self *TerminalFancier) Gray(text string) string { - return "\033[90m" + text + "\033[0m" -} - -func (self *TerminalFancier) Cyan(text string) string { - return "\033[36m" + text + "\033[0m" -} - -func (self *TerminalFancier) Green(text string) string { - return "\033[32m" + text + "\033[0m" -} - -func (self *TerminalFancier) WithCheck(text string) string { - return "\033[32m\u2713\033[0m " + text -} - -func (r *DetailedReporter) getSpace() string { - return strings.Repeat(" ", (r.level+1)*2) -} - -func (r *DetailedReporter) failure(failure *Failure) { - r.failures = append(r.failures, failure) -} - -func (r *DetailedReporter) print(text string) { - fmt.Printf("%v%v\n", r.getSpace(), text) -} - -func (r *DetailedReporter) printWithCheck(text string) { - fmt.Printf("%v%v\n", r.getSpace(), r.fancy.WithCheck(text)) -} - -func (r *DetailedReporter) beginDescribe(name string) { - fmt.Println("") - r.print(name) - r.level++ -} - -func (r *DetailedReporter) endDescribe() { - r.level-- -} - -func (r *DetailedReporter) itTook(duration time.Duration) { - r.executionTime = duration - r.totalExecutionTime += duration -} - -func (r *DetailedReporter) itFailed(name string) { - r.failed++ - r.print(r.fancy.Red(strconv.Itoa(r.failed) + ") " + name)) -} - -func (r *DetailedReporter) itPassed(name string) { - r.passed++ - r.printWithCheck(r.fancy.Gray(name)) -} - -func (r *DetailedReporter) itIsPending(name string) { - r.pending++ - r.print(r.fancy.Cyan("- " + name)) -} - -func (r *DetailedReporter) begin() { -} - -func (r *DetailedReporter) end() { - comp := fmt.Sprintf("%d tests complete", r.passed) - t := fmt.Sprintf("(%d ms)", r.totalExecutionTime/time.Millisecond) - - //fmt.Printf("\n\n \033[32m%d tests complete\033[0m \033[90m(%d ms)\033[0m\n", r.passed, r.totalExecutionTime/time.Millisecond) - fmt.Printf("\n\n %v %v\n", r.fancy.Green(comp), r.fancy.Gray(t)) - - if r.pending > 0 { - pend := fmt.Sprintf("%d test(s) pending", r.pending) - fmt.Printf(" %v\n\n", r.fancy.Cyan(pend)) - } - - if len(r.failures) > 0 { - fmt.Printf("%s \n\n", r.fancy.Red(fmt.Sprintf(" %d tests failed:", len(r.failures)))) - - } - - for i, failure := range r.failures { - fmt.Printf(" %d) %s:\n\n", i+1, failure.testName) - fmt.Printf(" %s\n", r.fancy.Red(failure.message)) - for _, stackItem := range failure.stack { - fmt.Printf(" %s\n", r.fancy.Gray(stackItem)) - } - } -} diff --git a/Godeps/_workspace/src/github.com/franela/goblin/resolver.go b/Godeps/_workspace/src/github.com/franela/goblin/resolver.go deleted file mode 100644 index 125fcec..0000000 --- a/Godeps/_workspace/src/github.com/franela/goblin/resolver.go +++ /dev/null @@ -1,21 +0,0 @@ -package goblin - -import ( - "runtime/debug" - "strings" -) - -func ResolveStack(skip int) []string { - return cleanStack(debug.Stack(), skip) -} - -func cleanStack(stack []byte, skip int) []string { - arrayStack := strings.Split(string(stack), "\n") - var finalStack []string - for i := skip; i < len(arrayStack); i++ { - if strings.Contains(arrayStack[i], ".go") { - finalStack = append(finalStack, arrayStack[i]) - } - } - return finalStack -} diff --git a/Godeps/_workspace/src/github.com/gorilla/context/.travis.yml b/Godeps/_workspace/src/github.com/gorilla/context/.travis.yml deleted file mode 100644 index d87d465..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/context/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: go - -go: - - 1.0 - - 1.1 - - 1.2 - - tip diff --git a/Godeps/_workspace/src/github.com/gorilla/context/LICENSE b/Godeps/_workspace/src/github.com/gorilla/context/LICENSE deleted file mode 100644 index 0e5fb87..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/context/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -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. diff --git a/Godeps/_workspace/src/github.com/gorilla/context/README.md b/Godeps/_workspace/src/github.com/gorilla/context/README.md deleted file mode 100644 index c60a31b..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/context/README.md +++ /dev/null @@ -1,7 +0,0 @@ -context -======= -[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context) - -gorilla/context is a general purpose registry for global request variables. - -Read the full documentation here: http://www.gorillatoolkit.org/pkg/context diff --git a/Godeps/_workspace/src/github.com/gorilla/context/context.go b/Godeps/_workspace/src/github.com/gorilla/context/context.go deleted file mode 100644 index 81cb128..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/context/context.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package context - -import ( - "net/http" - "sync" - "time" -) - -var ( - mutex sync.RWMutex - data = make(map[*http.Request]map[interface{}]interface{}) - datat = make(map[*http.Request]int64) -) - -// Set stores a value for a given key in a given request. -func Set(r *http.Request, key, val interface{}) { - mutex.Lock() - if data[r] == nil { - data[r] = make(map[interface{}]interface{}) - datat[r] = time.Now().Unix() - } - data[r][key] = val - mutex.Unlock() -} - -// Get returns a value stored for a given key in a given request. -func Get(r *http.Request, key interface{}) interface{} { - mutex.RLock() - if ctx := data[r]; ctx != nil { - value := ctx[key] - mutex.RUnlock() - return value - } - mutex.RUnlock() - return nil -} - -// GetOk returns stored value and presence state like multi-value return of map access. -func GetOk(r *http.Request, key interface{}) (interface{}, bool) { - mutex.RLock() - if _, ok := data[r]; ok { - value, ok := data[r][key] - mutex.RUnlock() - return value, ok - } - mutex.RUnlock() - return nil, false -} - -// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests. -func GetAll(r *http.Request) map[interface{}]interface{} { - mutex.RLock() - if context, ok := data[r]; ok { - result := make(map[interface{}]interface{}, len(context)) - for k, v := range context { - result[k] = v - } - mutex.RUnlock() - return result - } - mutex.RUnlock() - return nil -} - -// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if -// the request was registered. -func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { - mutex.RLock() - context, ok := data[r] - result := make(map[interface{}]interface{}, len(context)) - for k, v := range context { - result[k] = v - } - mutex.RUnlock() - return result, ok -} - -// Delete removes a value stored for a given key in a given request. -func Delete(r *http.Request, key interface{}) { - mutex.Lock() - if data[r] != nil { - delete(data[r], key) - } - mutex.Unlock() -} - -// Clear removes all values stored for a given request. -// -// This is usually called by a handler wrapper to clean up request -// variables at the end of a request lifetime. See ClearHandler(). -func Clear(r *http.Request) { - mutex.Lock() - clear(r) - mutex.Unlock() -} - -// clear is Clear without the lock. -func clear(r *http.Request) { - delete(data, r) - delete(datat, r) -} - -// Purge removes request data stored for longer than maxAge, in seconds. -// It returns the amount of requests removed. -// -// If maxAge <= 0, all request data is removed. -// -// This is only used for sanity check: in case context cleaning was not -// properly set some request data can be kept forever, consuming an increasing -// amount of memory. In case this is detected, Purge() must be called -// periodically until the problem is fixed. -func Purge(maxAge int) int { - mutex.Lock() - count := 0 - if maxAge <= 0 { - count = len(data) - data = make(map[*http.Request]map[interface{}]interface{}) - datat = make(map[*http.Request]int64) - } else { - min := time.Now().Unix() - int64(maxAge) - for r := range data { - if datat[r] < min { - clear(r) - count++ - } - } - } - mutex.Unlock() - return count -} - -// ClearHandler wraps an http.Handler and clears request values at the end -// of a request lifetime. -func ClearHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defer Clear(r) - h.ServeHTTP(w, r) - }) -} diff --git a/Godeps/_workspace/src/github.com/gorilla/context/doc.go b/Godeps/_workspace/src/github.com/gorilla/context/doc.go deleted file mode 100644 index 73c7400..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/context/doc.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package context stores values shared during a request lifetime. - -For example, a router can set variables extracted from the URL and later -application handlers can access those values, or it can be used to store -sessions values to be saved at the end of a request. There are several -others common uses. - -The idea was posted by Brad Fitzpatrick to the go-nuts mailing list: - - http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53 - -Here's the basic usage: first define the keys that you will need. The key -type is interface{} so a key can be of any type that supports equality. -Here we define a key using a custom int type to avoid name collisions: - - package foo - - import ( - "github.com/gorilla/context" - ) - - type key int - - const MyKey key = 0 - -Then set a variable. Variables are bound to an http.Request object, so you -need a request instance to set a value: - - context.Set(r, MyKey, "bar") - -The application can later access the variable using the same key you provided: - - func MyHandler(w http.ResponseWriter, r *http.Request) { - // val is "bar". - val := context.Get(r, foo.MyKey) - - // returns ("bar", true) - val, ok := context.GetOk(r, foo.MyKey) - // ... - } - -And that's all about the basic usage. We discuss some other ideas below. - -Any type can be stored in the context. To enforce a given type, make the key -private and wrap Get() and Set() to accept and return values of a specific -type: - - type key int - - const mykey key = 0 - - // GetMyKey returns a value for this package from the request values. - func GetMyKey(r *http.Request) SomeType { - if rv := context.Get(r, mykey); rv != nil { - return rv.(SomeType) - } - return nil - } - - // SetMyKey sets a value for this package in the request values. - func SetMyKey(r *http.Request, val SomeType) { - context.Set(r, mykey, val) - } - -Variables must be cleared at the end of a request, to remove all values -that were stored. This can be done in an http.Handler, after a request was -served. Just call Clear() passing the request: - - context.Clear(r) - -...or use ClearHandler(), which conveniently wraps an http.Handler to clear -variables at the end of a request lifetime. - -The Routers from the packages gorilla/mux and gorilla/pat call Clear() -so if you are using either of them you don't need to clear the context manually. -*/ -package context diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml b/Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml deleted file mode 100644 index f983b60..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: go -sudo: false - -go: - - 1.3 - - 1.4 - - 1.5 - - tip diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/LICENSE b/Godeps/_workspace/src/github.com/gorilla/mux/LICENSE deleted file mode 100644 index 0e5fb87..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/mux/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -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. diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/README.md b/Godeps/_workspace/src/github.com/gorilla/mux/README.md deleted file mode 100644 index 55dd4e5..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/mux/README.md +++ /dev/null @@ -1,235 +0,0 @@ -mux -=== -[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) -[![Build Status](https://travis-ci.org/gorilla/mux.png?branch=master)](https://travis-ci.org/gorilla/mux) - -Package gorilla/mux implements a request router and dispatcher. - -The name mux stands for "HTTP request multiplexer". Like the standard -http.ServeMux, mux.Router matches incoming requests against a list of -registered routes and calls a handler for the route that matches the URL -or other conditions. The main features are: - - * Requests can be matched based on URL host, path, path prefix, schemes, - header and query values, HTTP methods or using custom matchers. - * URL hosts and paths can have variables with an optional regular - expression. - * Registered URLs can be built, or "reversed", which helps maintaining - references to resources. - * Routes can be used as subrouters: nested routes are only tested if the - parent route matches. This is useful to define groups of routes that - share common conditions like a host, a path prefix or other repeated - attributes. As a bonus, this optimizes request matching. - * It implements the http.Handler interface so it is compatible with the - standard http.ServeMux. - -Let's start registering a couple of URL paths and handlers: - - func main() { - r := mux.NewRouter() - r.HandleFunc("/", HomeHandler) - r.HandleFunc("/products", ProductsHandler) - r.HandleFunc("/articles", ArticlesHandler) - http.Handle("/", r) - } - -Here we register three routes mapping URL paths to handlers. This is -equivalent to how http.HandleFunc() works: if an incoming request URL matches -one of the paths, the corresponding handler is called passing -(http.ResponseWriter, *http.Request) as parameters. - -Paths can have variables. They are defined using the format {name} or -{name:pattern}. If a regular expression pattern is not defined, the matched -variable will be anything until the next slash. For example: - - r := mux.NewRouter() - r.HandleFunc("/products/{key}", ProductHandler) - r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) - r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) - -The names are used to create a map of route variables which can be retrieved -calling mux.Vars(): - - vars := mux.Vars(request) - category := vars["category"] - -And this is all you need to know about the basic usage. More advanced options -are explained below. - -Routes can also be restricted to a domain or subdomain. Just define a host -pattern to be matched. They can also have variables: - - r := mux.NewRouter() - // Only matches if domain is "www.example.com". - r.Host("www.example.com") - // Matches a dynamic subdomain. - r.Host("{subdomain:[a-z]+}.domain.com") - -There are several other matchers that can be added. To match path prefixes: - - r.PathPrefix("/products/") - -...or HTTP methods: - - r.Methods("GET", "POST") - -...or URL schemes: - - r.Schemes("https") - -...or header values: - - r.Headers("X-Requested-With", "XMLHttpRequest") - -...or query values: - - r.Queries("key", "value") - -...or to use a custom matcher function: - - r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { - return r.ProtoMajor == 0 - }) - -...and finally, it is possible to combine several matchers in a single route: - - r.HandleFunc("/products", ProductsHandler). - Host("www.example.com"). - Methods("GET"). - Schemes("http") - -Setting the same matching conditions again and again can be boring, so we have -a way to group several routes that share the same requirements. -We call it "subrouting". - -For example, let's say we have several URLs that should only match when the -host is `www.example.com`. Create a route for that host and get a "subrouter" -from it: - - r := mux.NewRouter() - s := r.Host("www.example.com").Subrouter() - -Then register routes in the subrouter: - - s.HandleFunc("/products/", ProductsHandler) - s.HandleFunc("/products/{key}", ProductHandler) - s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) - -The three URL paths we registered above will only be tested if the domain is -`www.example.com`, because the subrouter is tested first. This is not -only convenient, but also optimizes request matching. You can create -subrouters combining any attribute matchers accepted by a route. - -Subrouters can be used to create domain or path "namespaces": you define -subrouters in a central place and then parts of the app can register its -paths relatively to a given subrouter. - -There's one more thing about subroutes. When a subrouter has a path prefix, -the inner routes use it as base for their paths: - - r := mux.NewRouter() - s := r.PathPrefix("/products").Subrouter() - // "/products/" - s.HandleFunc("/", ProductsHandler) - // "/products/{key}/" - s.HandleFunc("/{key}/", ProductHandler) - // "/products/{key}/details" - s.HandleFunc("/{key}/details", ProductDetailsHandler) - -Now let's see how to build registered URLs. - -Routes can be named. All routes that define a name can have their URLs built, -or "reversed". We define a name calling Name() on a route. For example: - - r := mux.NewRouter() - r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). - Name("article") - -To build a URL, get the route and call the URL() method, passing a sequence of -key/value pairs for the route variables. For the previous route, we would do: - - url, err := r.Get("article").URL("category", "technology", "id", "42") - -...and the result will be a url.URL with the following path: - - "/articles/technology/42" - -This also works for host variables: - - r := mux.NewRouter() - r.Host("{subdomain}.domain.com"). - Path("/articles/{category}/{id:[0-9]+}"). - HandlerFunc(ArticleHandler). - Name("article") - - // url.String() will be "http://news.domain.com/articles/technology/42" - url, err := r.Get("article").URL("subdomain", "news", - "category", "technology", - "id", "42") - -All variables defined in the route are required, and their values must -conform to the corresponding patterns. These requirements guarantee that a -generated URL will always match a registered route -- the only exception is -for explicitly defined "build-only" routes which never match. - -Regex support also exists for matching Headers within a route. For example, we could do: - - r.HeadersRegexp("Content-Type", "application/(text|json)") - -...and the route will match both requests with a Content-Type of `application/json` as well as -`application/text` - -There's also a way to build only the URL host or path for a route: -use the methods URLHost() or URLPath() instead. For the previous route, -we would do: - - // "http://news.domain.com/" - host, err := r.Get("article").URLHost("subdomain", "news") - - // "/articles/technology/42" - path, err := r.Get("article").URLPath("category", "technology", "id", "42") - -And if you use subrouters, host and path defined separately can be built -as well: - - r := mux.NewRouter() - s := r.Host("{subdomain}.domain.com").Subrouter() - s.Path("/articles/{category}/{id:[0-9]+}"). - HandlerFunc(ArticleHandler). - Name("article") - - // "http://news.domain.com/articles/technology/42" - url, err := r.Get("article").URL("subdomain", "news", - "category", "technology", - "id", "42") - -## Full Example - -Here's a complete, runnable example of a small mux based server: - -```go -package main - -import ( - "net/http" - - "github.com/gorilla/mux" -) - -func YourHandler(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Gorilla!\n")) -} - -func main() { - r := mux.NewRouter() - // Routes consist of a path and a handler function. - r.HandleFunc("/", YourHandler) - - // Bind to a port and pass our router in - http.ListenAndServe(":8000", r) -} -``` - -## License - -BSD licensed. See the LICENSE file for details. diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/doc.go b/Godeps/_workspace/src/github.com/gorilla/mux/doc.go deleted file mode 100644 index 49798cb..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/mux/doc.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package gorilla/mux implements a request router and dispatcher. - -The name mux stands for "HTTP request multiplexer". Like the standard -http.ServeMux, mux.Router matches incoming requests against a list of -registered routes and calls a handler for the route that matches the URL -or other conditions. The main features are: - - * Requests can be matched based on URL host, path, path prefix, schemes, - header and query values, HTTP methods or using custom matchers. - * URL hosts and paths can have variables with an optional regular - expression. - * Registered URLs can be built, or "reversed", which helps maintaining - references to resources. - * Routes can be used as subrouters: nested routes are only tested if the - parent route matches. This is useful to define groups of routes that - share common conditions like a host, a path prefix or other repeated - attributes. As a bonus, this optimizes request matching. - * It implements the http.Handler interface so it is compatible with the - standard http.ServeMux. - -Let's start registering a couple of URL paths and handlers: - - func main() { - r := mux.NewRouter() - r.HandleFunc("/", HomeHandler) - r.HandleFunc("/products", ProductsHandler) - r.HandleFunc("/articles", ArticlesHandler) - http.Handle("/", r) - } - -Here we register three routes mapping URL paths to handlers. This is -equivalent to how http.HandleFunc() works: if an incoming request URL matches -one of the paths, the corresponding handler is called passing -(http.ResponseWriter, *http.Request) as parameters. - -Paths can have variables. They are defined using the format {name} or -{name:pattern}. If a regular expression pattern is not defined, the matched -variable will be anything until the next slash. For example: - - r := mux.NewRouter() - r.HandleFunc("/products/{key}", ProductHandler) - r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) - r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) - -The names are used to create a map of route variables which can be retrieved -calling mux.Vars(): - - vars := mux.Vars(request) - category := vars["category"] - -And this is all you need to know about the basic usage. More advanced options -are explained below. - -Routes can also be restricted to a domain or subdomain. Just define a host -pattern to be matched. They can also have variables: - - r := mux.NewRouter() - // Only matches if domain is "www.example.com". - r.Host("www.example.com") - // Matches a dynamic subdomain. - r.Host("{subdomain:[a-z]+}.domain.com") - -There are several other matchers that can be added. To match path prefixes: - - r.PathPrefix("/products/") - -...or HTTP methods: - - r.Methods("GET", "POST") - -...or URL schemes: - - r.Schemes("https") - -...or header values: - - r.Headers("X-Requested-With", "XMLHttpRequest") - -...or query values: - - r.Queries("key", "value") - -...or to use a custom matcher function: - - r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { - return r.ProtoMajor == 0 - }) - -...and finally, it is possible to combine several matchers in a single route: - - r.HandleFunc("/products", ProductsHandler). - Host("www.example.com"). - Methods("GET"). - Schemes("http") - -Setting the same matching conditions again and again can be boring, so we have -a way to group several routes that share the same requirements. -We call it "subrouting". - -For example, let's say we have several URLs that should only match when the -host is "www.example.com". Create a route for that host and get a "subrouter" -from it: - - r := mux.NewRouter() - s := r.Host("www.example.com").Subrouter() - -Then register routes in the subrouter: - - s.HandleFunc("/products/", ProductsHandler) - s.HandleFunc("/products/{key}", ProductHandler) - s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) - -The three URL paths we registered above will only be tested if the domain is -"www.example.com", because the subrouter is tested first. This is not -only convenient, but also optimizes request matching. You can create -subrouters combining any attribute matchers accepted by a route. - -Subrouters can be used to create domain or path "namespaces": you define -subrouters in a central place and then parts of the app can register its -paths relatively to a given subrouter. - -There's one more thing about subroutes. When a subrouter has a path prefix, -the inner routes use it as base for their paths: - - r := mux.NewRouter() - s := r.PathPrefix("/products").Subrouter() - // "/products/" - s.HandleFunc("/", ProductsHandler) - // "/products/{key}/" - s.HandleFunc("/{key}/", ProductHandler) - // "/products/{key}/details" - s.HandleFunc("/{key}/details", ProductDetailsHandler) - -Now let's see how to build registered URLs. - -Routes can be named. All routes that define a name can have their URLs built, -or "reversed". We define a name calling Name() on a route. For example: - - r := mux.NewRouter() - r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). - Name("article") - -To build a URL, get the route and call the URL() method, passing a sequence of -key/value pairs for the route variables. For the previous route, we would do: - - url, err := r.Get("article").URL("category", "technology", "id", "42") - -...and the result will be a url.URL with the following path: - - "/articles/technology/42" - -This also works for host variables: - - r := mux.NewRouter() - r.Host("{subdomain}.domain.com"). - Path("/articles/{category}/{id:[0-9]+}"). - HandlerFunc(ArticleHandler). - Name("article") - - // url.String() will be "http://news.domain.com/articles/technology/42" - url, err := r.Get("article").URL("subdomain", "news", - "category", "technology", - "id", "42") - -All variables defined in the route are required, and their values must -conform to the corresponding patterns. These requirements guarantee that a -generated URL will always match a registered route -- the only exception is -for explicitly defined "build-only" routes which never match. - -Regex support also exists for matching Headers within a route. For example, we could do: - - r.HeadersRegexp("Content-Type", "application/(text|json)") - -...and the route will match both requests with a Content-Type of `application/json` as well as -`application/text` - -There's also a way to build only the URL host or path for a route: -use the methods URLHost() or URLPath() instead. For the previous route, -we would do: - - // "http://news.domain.com/" - host, err := r.Get("article").URLHost("subdomain", "news") - - // "/articles/technology/42" - path, err := r.Get("article").URLPath("category", "technology", "id", "42") - -And if you use subrouters, host and path defined separately can be built -as well: - - r := mux.NewRouter() - s := r.Host("{subdomain}.domain.com").Subrouter() - s.Path("/articles/{category}/{id:[0-9]+}"). - HandlerFunc(ArticleHandler). - Name("article") - - // "http://news.domain.com/articles/technology/42" - url, err := r.Get("article").URL("subdomain", "news", - "category", "technology", - "id", "42") -*/ -package mux diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/mux.go b/Godeps/_workspace/src/github.com/gorilla/mux/mux.go deleted file mode 100644 index 68c4ea5..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/mux/mux.go +++ /dev/null @@ -1,474 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package mux - -import ( - "errors" - "fmt" - "net/http" - "path" - "regexp" - - "github.com/gorilla/context" -) - -// NewRouter returns a new router instance. -func NewRouter() *Router { - return &Router{namedRoutes: make(map[string]*Route), KeepContext: false} -} - -// Router registers routes to be matched and dispatches a handler. -// -// It implements the http.Handler interface, so it can be registered to serve -// requests: -// -// var router = mux.NewRouter() -// -// func main() { -// http.Handle("/", router) -// } -// -// Or, for Google App Engine, register it in a init() function: -// -// func init() { -// http.Handle("/", router) -// } -// -// This will send all incoming requests to the router. -type Router struct { - // Configurable Handler to be used when no route matches. - NotFoundHandler http.Handler - // Parent route, if this is a subrouter. - parent parentRoute - // Routes to be matched, in order. - routes []*Route - // Routes by name for URL building. - namedRoutes map[string]*Route - // See Router.StrictSlash(). This defines the flag for new routes. - strictSlash bool - // If true, do not clear the request context after handling the request - KeepContext bool -} - -// Match matches registered routes against the request. -func (r *Router) Match(req *http.Request, match *RouteMatch) bool { - for _, route := range r.routes { - if route.Match(req, match) { - return true - } - } - return false -} - -// ServeHTTP dispatches the handler registered in the matched route. -// -// When there is a match, the route variables can be retrieved calling -// mux.Vars(request). -func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // Clean path to canonical form and redirect. - if p := cleanPath(req.URL.Path); p != req.URL.Path { - - // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. - // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: - // http://code.google.com/p/go/issues/detail?id=5252 - url := *req.URL - url.Path = p - p = url.String() - - w.Header().Set("Location", p) - w.WriteHeader(http.StatusMovedPermanently) - return - } - var match RouteMatch - var handler http.Handler - if r.Match(req, &match) { - handler = match.Handler - setVars(req, match.Vars) - setCurrentRoute(req, match.Route) - } - if handler == nil { - handler = r.NotFoundHandler - if handler == nil { - handler = http.NotFoundHandler() - } - } - if !r.KeepContext { - defer context.Clear(req) - } - handler.ServeHTTP(w, req) -} - -// Get returns a route registered with the given name. -func (r *Router) Get(name string) *Route { - return r.getNamedRoutes()[name] -} - -// GetRoute returns a route registered with the given name. This method -// was renamed to Get() and remains here for backwards compatibility. -func (r *Router) GetRoute(name string) *Route { - return r.getNamedRoutes()[name] -} - -// StrictSlash defines the trailing slash behavior for new routes. The initial -// value is false. -// -// When true, if the route path is "/path/", accessing "/path" will redirect -// to the former and vice versa. In other words, your application will always -// see the path as specified in the route. -// -// When false, if the route path is "/path", accessing "/path/" will not match -// this route and vice versa. -// -// Special case: when a route sets a path prefix using the PathPrefix() method, -// strict slash is ignored for that route because the redirect behavior can't -// be determined from a prefix alone. However, any subrouters created from that -// route inherit the original StrictSlash setting. -func (r *Router) StrictSlash(value bool) *Router { - r.strictSlash = value - return r -} - -// ---------------------------------------------------------------------------- -// parentRoute -// ---------------------------------------------------------------------------- - -// getNamedRoutes returns the map where named routes are registered. -func (r *Router) getNamedRoutes() map[string]*Route { - if r.namedRoutes == nil { - if r.parent != nil { - r.namedRoutes = r.parent.getNamedRoutes() - } else { - r.namedRoutes = make(map[string]*Route) - } - } - return r.namedRoutes -} - -// getRegexpGroup returns regexp definitions from the parent route, if any. -func (r *Router) getRegexpGroup() *routeRegexpGroup { - if r.parent != nil { - return r.parent.getRegexpGroup() - } - return nil -} - -func (r *Router) buildVars(m map[string]string) map[string]string { - if r.parent != nil { - m = r.parent.buildVars(m) - } - return m -} - -// ---------------------------------------------------------------------------- -// Route factories -// ---------------------------------------------------------------------------- - -// NewRoute registers an empty route. -func (r *Router) NewRoute() *Route { - route := &Route{parent: r, strictSlash: r.strictSlash} - r.routes = append(r.routes, route) - return route -} - -// Handle registers a new route with a matcher for the URL path. -// See Route.Path() and Route.Handler(). -func (r *Router) Handle(path string, handler http.Handler) *Route { - return r.NewRoute().Path(path).Handler(handler) -} - -// HandleFunc registers a new route with a matcher for the URL path. -// See Route.Path() and Route.HandlerFunc(). -func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, - *http.Request)) *Route { - return r.NewRoute().Path(path).HandlerFunc(f) -} - -// Headers registers a new route with a matcher for request header values. -// See Route.Headers(). -func (r *Router) Headers(pairs ...string) *Route { - return r.NewRoute().Headers(pairs...) -} - -// Host registers a new route with a matcher for the URL host. -// See Route.Host(). -func (r *Router) Host(tpl string) *Route { - return r.NewRoute().Host(tpl) -} - -// MatcherFunc registers a new route with a custom matcher function. -// See Route.MatcherFunc(). -func (r *Router) MatcherFunc(f MatcherFunc) *Route { - return r.NewRoute().MatcherFunc(f) -} - -// Methods registers a new route with a matcher for HTTP methods. -// See Route.Methods(). -func (r *Router) Methods(methods ...string) *Route { - return r.NewRoute().Methods(methods...) -} - -// Path registers a new route with a matcher for the URL path. -// See Route.Path(). -func (r *Router) Path(tpl string) *Route { - return r.NewRoute().Path(tpl) -} - -// PathPrefix registers a new route with a matcher for the URL path prefix. -// See Route.PathPrefix(). -func (r *Router) PathPrefix(tpl string) *Route { - return r.NewRoute().PathPrefix(tpl) -} - -// Queries registers a new route with a matcher for URL query values. -// See Route.Queries(). -func (r *Router) Queries(pairs ...string) *Route { - return r.NewRoute().Queries(pairs...) -} - -// Schemes registers a new route with a matcher for URL schemes. -// See Route.Schemes(). -func (r *Router) Schemes(schemes ...string) *Route { - return r.NewRoute().Schemes(schemes...) -} - -// BuildVars registers a new route with a custom function for modifying -// route variables before building a URL. -func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route { - return r.NewRoute().BuildVarsFunc(f) -} - -// Walk walks the router and all its sub-routers, calling walkFn for each route -// in the tree. The routes are walked in the order they were added. Sub-routers -// are explored depth-first. -func (r *Router) Walk(walkFn WalkFunc) error { - return r.walk(walkFn, []*Route{}) -} - -// SkipRouter is used as a return value from WalkFuncs to indicate that the -// router that walk is about to descend down to should be skipped. -var SkipRouter = errors.New("skip this router") - -// WalkFunc is the type of the function called for each route visited by Walk. -// At every invocation, it is given the current route, and the current router, -// and a list of ancestor routes that lead to the current route. -type WalkFunc func(route *Route, router *Router, ancestors []*Route) error - -func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { - for _, t := range r.routes { - if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" { - continue - } - - err := walkFn(t, r, ancestors) - if err == SkipRouter { - continue - } - for _, sr := range t.matchers { - if h, ok := sr.(*Router); ok { - err := h.walk(walkFn, ancestors) - if err != nil { - return err - } - } - } - if h, ok := t.handler.(*Router); ok { - ancestors = append(ancestors, t) - err := h.walk(walkFn, ancestors) - if err != nil { - return err - } - ancestors = ancestors[:len(ancestors)-1] - } - } - return nil -} - -// ---------------------------------------------------------------------------- -// Context -// ---------------------------------------------------------------------------- - -// RouteMatch stores information about a matched route. -type RouteMatch struct { - Route *Route - Handler http.Handler - Vars map[string]string -} - -type contextKey int - -const ( - varsKey contextKey = iota - routeKey -) - -// Vars returns the route variables for the current request, if any. -func Vars(r *http.Request) map[string]string { - if rv := context.Get(r, varsKey); rv != nil { - return rv.(map[string]string) - } - return nil -} - -// CurrentRoute returns the matched route for the current request, if any. -// This only works when called inside the handler of the matched route -// because the matched route is stored in the request context which is cleared -// after the handler returns, unless the KeepContext option is set on the -// Router. -func CurrentRoute(r *http.Request) *Route { - if rv := context.Get(r, routeKey); rv != nil { - return rv.(*Route) - } - return nil -} - -func setVars(r *http.Request, val interface{}) { - context.Set(r, varsKey, val) -} - -func setCurrentRoute(r *http.Request, val interface{}) { - context.Set(r, routeKey, val) -} - -// ---------------------------------------------------------------------------- -// Helpers -// ---------------------------------------------------------------------------- - -// cleanPath returns the canonical path for p, eliminating . and .. elements. -// Borrowed from the net/http package. -func cleanPath(p string) string { - if p == "" { - return "/" - } - if p[0] != '/' { - p = "/" + p - } - np := path.Clean(p) - // path.Clean removes trailing slash except for root; - // put the trailing slash back if necessary. - if p[len(p)-1] == '/' && np != "/" { - np += "/" - } - return np -} - -// uniqueVars returns an error if two slices contain duplicated strings. -func uniqueVars(s1, s2 []string) error { - for _, v1 := range s1 { - for _, v2 := range s2 { - if v1 == v2 { - return fmt.Errorf("mux: duplicated route variable %q", v2) - } - } - } - return nil -} - -// checkPairs returns the count of strings passed in, and an error if -// the count is not an even number. -func checkPairs(pairs ...string) (int, error) { - length := len(pairs) - if length%2 != 0 { - return length, fmt.Errorf( - "mux: number of parameters must be multiple of 2, got %v", pairs) - } - return length, nil -} - -// mapFromPairsToString converts variadic string parameters to a -// string to string map. -func mapFromPairsToString(pairs ...string) (map[string]string, error) { - length, err := checkPairs(pairs...) - if err != nil { - return nil, err - } - m := make(map[string]string, length/2) - for i := 0; i < length; i += 2 { - m[pairs[i]] = pairs[i+1] - } - return m, nil -} - -// mapFromPairsToRegex converts variadic string paramers to a -// string to regex map. -func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { - length, err := checkPairs(pairs...) - if err != nil { - return nil, err - } - m := make(map[string]*regexp.Regexp, length/2) - for i := 0; i < length; i += 2 { - regex, err := regexp.Compile(pairs[i+1]) - if err != nil { - return nil, err - } - m[pairs[i]] = regex - } - return m, nil -} - -// matchInArray returns true if the given string value is in the array. -func matchInArray(arr []string, value string) bool { - for _, v := range arr { - if v == value { - return true - } - } - return false -} - -// matchMapWithString returns true if the given key/value pairs exist in a given map. -func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool { - for k, v := range toCheck { - // Check if key exists. - if canonicalKey { - k = http.CanonicalHeaderKey(k) - } - if values := toMatch[k]; values == nil { - return false - } else if v != "" { - // If value was defined as an empty string we only check that the - // key exists. Otherwise we also check for equality. - valueExists := false - for _, value := range values { - if v == value { - valueExists = true - break - } - } - if !valueExists { - return false - } - } - } - return true -} - -// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against -// the given regex -func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool { - for k, v := range toCheck { - // Check if key exists. - if canonicalKey { - k = http.CanonicalHeaderKey(k) - } - if values := toMatch[k]; values == nil { - return false - } else if v != nil { - // If value was defined as an empty string we only check that the - // key exists. Otherwise we also check for equality. - valueExists := false - for _, value := range values { - if v.MatchString(value) { - valueExists = true - break - } - } - if !valueExists { - return false - } - } - } - return true -} diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/regexp.go b/Godeps/_workspace/src/github.com/gorilla/mux/regexp.go deleted file mode 100644 index 06728dd..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/mux/regexp.go +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package mux - -import ( - "bytes" - "fmt" - "net/http" - "net/url" - "regexp" - "strconv" - "strings" -) - -// newRouteRegexp parses a route template and returns a routeRegexp, -// used to match a host, a path or a query string. -// -// It will extract named variables, assemble a regexp to be matched, create -// a "reverse" template to build URLs and compile regexps to validate variable -// values used in URL building. -// -// Previously we accepted only Python-like identifiers for variable -// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that -// name and pattern can't be empty, and names can't contain a colon. -func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) { - // Check if it is well-formed. - idxs, errBraces := braceIndices(tpl) - if errBraces != nil { - return nil, errBraces - } - // Backup the original. - template := tpl - // Now let's parse it. - defaultPattern := "[^/]+" - if matchQuery { - defaultPattern = "[^?&]*" - } else if matchHost { - defaultPattern = "[^.]+" - matchPrefix = false - } - // Only match strict slash if not matching - if matchPrefix || matchHost || matchQuery { - strictSlash = false - } - // Set a flag for strictSlash. - endSlash := false - if strictSlash && strings.HasSuffix(tpl, "/") { - tpl = tpl[:len(tpl)-1] - endSlash = true - } - varsN := make([]string, len(idxs)/2) - varsR := make([]*regexp.Regexp, len(idxs)/2) - pattern := bytes.NewBufferString("") - pattern.WriteByte('^') - reverse := bytes.NewBufferString("") - var end int - var err error - for i := 0; i < len(idxs); i += 2 { - // Set all values we are interested in. - raw := tpl[end:idxs[i]] - end = idxs[i+1] - parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2) - name := parts[0] - patt := defaultPattern - if len(parts) == 2 { - patt = parts[1] - } - // Name or pattern can't be empty. - if name == "" || patt == "" { - return nil, fmt.Errorf("mux: missing name or pattern in %q", - tpl[idxs[i]:end]) - } - // Build the regexp pattern. - varIdx := i / 2 - fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(varIdx), patt) - // Build the reverse template. - fmt.Fprintf(reverse, "%s%%s", raw) - - // Append variable name and compiled pattern. - varsN[varIdx] = name - varsR[varIdx], err = regexp.Compile(fmt.Sprintf("^%s$", patt)) - if err != nil { - return nil, err - } - } - // Add the remaining. - raw := tpl[end:] - pattern.WriteString(regexp.QuoteMeta(raw)) - if strictSlash { - pattern.WriteString("[/]?") - } - if matchQuery { - // Add the default pattern if the query value is empty - if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" { - pattern.WriteString(defaultPattern) - } - } - if !matchPrefix { - pattern.WriteByte('$') - } - reverse.WriteString(raw) - if endSlash { - reverse.WriteByte('/') - } - // Compile full regexp. - reg, errCompile := regexp.Compile(pattern.String()) - if errCompile != nil { - return nil, errCompile - } - // Done! - return &routeRegexp{ - template: template, - matchHost: matchHost, - matchQuery: matchQuery, - strictSlash: strictSlash, - regexp: reg, - reverse: reverse.String(), - varsN: varsN, - varsR: varsR, - }, nil -} - -// routeRegexp stores a regexp to match a host or path and information to -// collect and validate route variables. -type routeRegexp struct { - // The unmodified template. - template string - // True for host match, false for path or query string match. - matchHost bool - // True for query string match, false for path and host match. - matchQuery bool - // The strictSlash value defined on the route, but disabled if PathPrefix was used. - strictSlash bool - // Expanded regexp. - regexp *regexp.Regexp - // Reverse template. - reverse string - // Variable names. - varsN []string - // Variable regexps (validators). - varsR []*regexp.Regexp -} - -// Match matches the regexp against the URL host or path. -func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { - if !r.matchHost { - if r.matchQuery { - return r.matchQueryString(req) - } else { - return r.regexp.MatchString(req.URL.Path) - } - } - return r.regexp.MatchString(getHost(req)) -} - -// url builds a URL part using the given values. -func (r *routeRegexp) url(values map[string]string) (string, error) { - urlValues := make([]interface{}, len(r.varsN)) - for k, v := range r.varsN { - value, ok := values[v] - if !ok { - return "", fmt.Errorf("mux: missing route variable %q", v) - } - urlValues[k] = value - } - rv := fmt.Sprintf(r.reverse, urlValues...) - if !r.regexp.MatchString(rv) { - // The URL is checked against the full regexp, instead of checking - // individual variables. This is faster but to provide a good error - // message, we check individual regexps if the URL doesn't match. - for k, v := range r.varsN { - if !r.varsR[k].MatchString(values[v]) { - return "", fmt.Errorf( - "mux: variable %q doesn't match, expected %q", values[v], - r.varsR[k].String()) - } - } - } - return rv, nil -} - -// getUrlQuery returns a single query parameter from a request URL. -// For a URL with foo=bar&baz=ding, we return only the relevant key -// value pair for the routeRegexp. -func (r *routeRegexp) getUrlQuery(req *http.Request) string { - if !r.matchQuery { - return "" - } - templateKey := strings.SplitN(r.template, "=", 2)[0] - for key, vals := range req.URL.Query() { - if key == templateKey && len(vals) > 0 { - return key + "=" + vals[0] - } - } - return "" -} - -func (r *routeRegexp) matchQueryString(req *http.Request) bool { - return r.regexp.MatchString(r.getUrlQuery(req)) -} - -// braceIndices returns the first level curly brace indices from a string. -// It returns an error in case of unbalanced braces. -func braceIndices(s string) ([]int, error) { - var level, idx int - idxs := make([]int, 0) - for i := 0; i < len(s); i++ { - switch s[i] { - case '{': - if level++; level == 1 { - idx = i - } - case '}': - if level--; level == 0 { - idxs = append(idxs, idx, i+1) - } else if level < 0 { - return nil, fmt.Errorf("mux: unbalanced braces in %q", s) - } - } - } - if level != 0 { - return nil, fmt.Errorf("mux: unbalanced braces in %q", s) - } - return idxs, nil -} - -// varGroupName builds a capturing group name for the indexed variable. -func varGroupName(idx int) string { - return "v" + strconv.Itoa(idx) -} - -// ---------------------------------------------------------------------------- -// routeRegexpGroup -// ---------------------------------------------------------------------------- - -// routeRegexpGroup groups the route matchers that carry variables. -type routeRegexpGroup struct { - host *routeRegexp - path *routeRegexp - queries []*routeRegexp -} - -// setMatch extracts the variables from the URL once a route matches. -func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) { - // Store host variables. - if v.host != nil { - hostVars := v.host.regexp.FindStringSubmatch(getHost(req)) - if hostVars != nil { - subexpNames := v.host.regexp.SubexpNames() - varName := 0 - for i, name := range subexpNames[1:] { - if name != "" && name == varGroupName(varName) { - m.Vars[v.host.varsN[varName]] = hostVars[i+1] - varName++ - } - } - } - } - // Store path variables. - if v.path != nil { - pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path) - if pathVars != nil { - subexpNames := v.path.regexp.SubexpNames() - varName := 0 - for i, name := range subexpNames[1:] { - if name != "" && name == varGroupName(varName) { - m.Vars[v.path.varsN[varName]] = pathVars[i+1] - varName++ - } - } - // Check if we should redirect. - if v.path.strictSlash { - p1 := strings.HasSuffix(req.URL.Path, "/") - p2 := strings.HasSuffix(v.path.template, "/") - if p1 != p2 { - u, _ := url.Parse(req.URL.String()) - if p1 { - u.Path = u.Path[:len(u.Path)-1] - } else { - u.Path += "/" - } - m.Handler = http.RedirectHandler(u.String(), 301) - } - } - } - } - // Store query string variables. - for _, q := range v.queries { - queryVars := q.regexp.FindStringSubmatch(q.getUrlQuery(req)) - if queryVars != nil { - subexpNames := q.regexp.SubexpNames() - varName := 0 - for i, name := range subexpNames[1:] { - if name != "" && name == varGroupName(varName) { - m.Vars[q.varsN[varName]] = queryVars[i+1] - varName++ - } - } - } - } -} - -// getHost tries its best to return the request host. -func getHost(r *http.Request) string { - if r.URL.IsAbs() { - return r.URL.Host - } - host := r.Host - // Slice off any port information. - if i := strings.Index(host, ":"); i != -1 { - host = host[:i] - } - return host - -} diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/route.go b/Godeps/_workspace/src/github.com/gorilla/mux/route.go deleted file mode 100644 index 913432c..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/mux/route.go +++ /dev/null @@ -1,595 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package mux - -import ( - "errors" - "fmt" - "net/http" - "net/url" - "regexp" - "strings" -) - -// Route stores information to match a request and build URLs. -type Route struct { - // Parent where the route was registered (a Router). - parent parentRoute - // Request handler for the route. - handler http.Handler - // List of matchers. - matchers []matcher - // Manager for the variables from host and path. - regexp *routeRegexpGroup - // If true, when the path pattern is "/path/", accessing "/path" will - // redirect to the former and vice versa. - strictSlash bool - // If true, this route never matches: it is only used to build URLs. - buildOnly bool - // The name used to build URLs. - name string - // Error resulted from building a route. - err error - - buildVarsFunc BuildVarsFunc -} - -// Match matches the route against the request. -func (r *Route) Match(req *http.Request, match *RouteMatch) bool { - if r.buildOnly || r.err != nil { - return false - } - // Match everything. - for _, m := range r.matchers { - if matched := m.Match(req, match); !matched { - return false - } - } - // Yay, we have a match. Let's collect some info about it. - if match.Route == nil { - match.Route = r - } - if match.Handler == nil { - match.Handler = r.handler - } - if match.Vars == nil { - match.Vars = make(map[string]string) - } - // Set variables. - if r.regexp != nil { - r.regexp.setMatch(req, match, r) - } - return true -} - -// ---------------------------------------------------------------------------- -// Route attributes -// ---------------------------------------------------------------------------- - -// GetError returns an error resulted from building the route, if any. -func (r *Route) GetError() error { - return r.err -} - -// BuildOnly sets the route to never match: it is only used to build URLs. -func (r *Route) BuildOnly() *Route { - r.buildOnly = true - return r -} - -// Handler -------------------------------------------------------------------- - -// Handler sets a handler for the route. -func (r *Route) Handler(handler http.Handler) *Route { - if r.err == nil { - r.handler = handler - } - return r -} - -// HandlerFunc sets a handler function for the route. -func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route { - return r.Handler(http.HandlerFunc(f)) -} - -// GetHandler returns the handler for the route, if any. -func (r *Route) GetHandler() http.Handler { - return r.handler -} - -// Name ----------------------------------------------------------------------- - -// Name sets the name for the route, used to build URLs. -// If the name was registered already it will be overwritten. -func (r *Route) Name(name string) *Route { - if r.name != "" { - r.err = fmt.Errorf("mux: route already has name %q, can't set %q", - r.name, name) - } - if r.err == nil { - r.name = name - r.getNamedRoutes()[name] = r - } - return r -} - -// GetName returns the name for the route, if any. -func (r *Route) GetName() string { - return r.name -} - -// ---------------------------------------------------------------------------- -// Matchers -// ---------------------------------------------------------------------------- - -// matcher types try to match a request. -type matcher interface { - Match(*http.Request, *RouteMatch) bool -} - -// addMatcher adds a matcher to the route. -func (r *Route) addMatcher(m matcher) *Route { - if r.err == nil { - r.matchers = append(r.matchers, m) - } - return r -} - -// addRegexpMatcher adds a host or path matcher and builder to a route. -func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error { - if r.err != nil { - return r.err - } - r.regexp = r.getRegexpGroup() - if !matchHost && !matchQuery { - if len(tpl) == 0 || tpl[0] != '/' { - return fmt.Errorf("mux: path must start with a slash, got %q", tpl) - } - if r.regexp.path != nil { - tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl - } - } - rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash) - if err != nil { - return err - } - for _, q := range r.regexp.queries { - if err = uniqueVars(rr.varsN, q.varsN); err != nil { - return err - } - } - if matchHost { - if r.regexp.path != nil { - if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil { - return err - } - } - r.regexp.host = rr - } else { - if r.regexp.host != nil { - if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil { - return err - } - } - if matchQuery { - r.regexp.queries = append(r.regexp.queries, rr) - } else { - r.regexp.path = rr - } - } - r.addMatcher(rr) - return nil -} - -// Headers -------------------------------------------------------------------- - -// headerMatcher matches the request against header values. -type headerMatcher map[string]string - -func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool { - return matchMapWithString(m, r.Header, true) -} - -// Headers adds a matcher for request header values. -// It accepts a sequence of key/value pairs to be matched. For example: -// -// r := mux.NewRouter() -// r.Headers("Content-Type", "application/json", -// "X-Requested-With", "XMLHttpRequest") -// -// The above route will only match if both request header values match. -// If the value is an empty string, it will match any value if the key is set. -func (r *Route) Headers(pairs ...string) *Route { - if r.err == nil { - var headers map[string]string - headers, r.err = mapFromPairsToString(pairs...) - return r.addMatcher(headerMatcher(headers)) - } - return r -} - -// headerRegexMatcher matches the request against the route given a regex for the header -type headerRegexMatcher map[string]*regexp.Regexp - -func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool { - return matchMapWithRegex(m, r.Header, true) -} - -// Regular expressions can be used with headers as well. -// It accepts a sequence of key/value pairs, where the value has regex support. For example -// r := mux.NewRouter() -// r.HeadersRegexp("Content-Type", "application/(text|json)", -// "X-Requested-With", "XMLHttpRequest") -// -// The above route will only match if both the request header matches both regular expressions. -// It the value is an empty string, it will match any value if the key is set. -func (r *Route) HeadersRegexp(pairs ...string) *Route { - if r.err == nil { - var headers map[string]*regexp.Regexp - headers, r.err = mapFromPairsToRegex(pairs...) - return r.addMatcher(headerRegexMatcher(headers)) - } - return r -} - -// Host ----------------------------------------------------------------------- - -// Host adds a matcher for the URL host. -// It accepts a template with zero or more URL variables enclosed by {}. -// Variables can define an optional regexp pattern to be matched: -// -// - {name} matches anything until the next dot. -// -// - {name:pattern} matches the given regexp pattern. -// -// For example: -// -// r := mux.NewRouter() -// r.Host("www.example.com") -// r.Host("{subdomain}.domain.com") -// r.Host("{subdomain:[a-z]+}.domain.com") -// -// Variable names must be unique in a given route. They can be retrieved -// calling mux.Vars(request). -func (r *Route) Host(tpl string) *Route { - r.err = r.addRegexpMatcher(tpl, true, false, false) - return r -} - -// MatcherFunc ---------------------------------------------------------------- - -// MatcherFunc is the function signature used by custom matchers. -type MatcherFunc func(*http.Request, *RouteMatch) bool - -func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool { - return m(r, match) -} - -// MatcherFunc adds a custom function to be used as request matcher. -func (r *Route) MatcherFunc(f MatcherFunc) *Route { - return r.addMatcher(f) -} - -// Methods -------------------------------------------------------------------- - -// methodMatcher matches the request against HTTP methods. -type methodMatcher []string - -func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool { - return matchInArray(m, r.Method) -} - -// Methods adds a matcher for HTTP methods. -// It accepts a sequence of one or more methods to be matched, e.g.: -// "GET", "POST", "PUT". -func (r *Route) Methods(methods ...string) *Route { - for k, v := range methods { - methods[k] = strings.ToUpper(v) - } - return r.addMatcher(methodMatcher(methods)) -} - -// Path ----------------------------------------------------------------------- - -// Path adds a matcher for the URL path. -// It accepts a template with zero or more URL variables enclosed by {}. The -// template must start with a "/". -// Variables can define an optional regexp pattern to be matched: -// -// - {name} matches anything until the next slash. -// -// - {name:pattern} matches the given regexp pattern. -// -// For example: -// -// r := mux.NewRouter() -// r.Path("/products/").Handler(ProductsHandler) -// r.Path("/products/{key}").Handler(ProductsHandler) -// r.Path("/articles/{category}/{id:[0-9]+}"). -// Handler(ArticleHandler) -// -// Variable names must be unique in a given route. They can be retrieved -// calling mux.Vars(request). -func (r *Route) Path(tpl string) *Route { - r.err = r.addRegexpMatcher(tpl, false, false, false) - return r -} - -// PathPrefix ----------------------------------------------------------------- - -// PathPrefix adds a matcher for the URL path prefix. This matches if the given -// template is a prefix of the full URL path. See Route.Path() for details on -// the tpl argument. -// -// Note that it does not treat slashes specially ("/foobar/" will be matched by -// the prefix "/foo") so you may want to use a trailing slash here. -// -// Also note that the setting of Router.StrictSlash() has no effect on routes -// with a PathPrefix matcher. -func (r *Route) PathPrefix(tpl string) *Route { - r.err = r.addRegexpMatcher(tpl, false, true, false) - return r -} - -// Query ---------------------------------------------------------------------- - -// Queries adds a matcher for URL query values. -// It accepts a sequence of key/value pairs. Values may define variables. -// For example: -// -// r := mux.NewRouter() -// r.Queries("foo", "bar", "id", "{id:[0-9]+}") -// -// The above route will only match if the URL contains the defined queries -// values, e.g.: ?foo=bar&id=42. -// -// It the value is an empty string, it will match any value if the key is set. -// -// Variables can define an optional regexp pattern to be matched: -// -// - {name} matches anything until the next slash. -// -// - {name:pattern} matches the given regexp pattern. -func (r *Route) Queries(pairs ...string) *Route { - length := len(pairs) - if length%2 != 0 { - r.err = fmt.Errorf( - "mux: number of parameters must be multiple of 2, got %v", pairs) - return nil - } - for i := 0; i < length; i += 2 { - if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil { - return r - } - } - - return r -} - -// Schemes -------------------------------------------------------------------- - -// schemeMatcher matches the request against URL schemes. -type schemeMatcher []string - -func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool { - return matchInArray(m, r.URL.Scheme) -} - -// Schemes adds a matcher for URL schemes. -// It accepts a sequence of schemes to be matched, e.g.: "http", "https". -func (r *Route) Schemes(schemes ...string) *Route { - for k, v := range schemes { - schemes[k] = strings.ToLower(v) - } - return r.addMatcher(schemeMatcher(schemes)) -} - -// BuildVarsFunc -------------------------------------------------------------- - -// BuildVarsFunc is the function signature used by custom build variable -// functions (which can modify route variables before a route's URL is built). -type BuildVarsFunc func(map[string]string) map[string]string - -// BuildVarsFunc adds a custom function to be used to modify build variables -// before a route's URL is built. -func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { - r.buildVarsFunc = f - return r -} - -// Subrouter ------------------------------------------------------------------ - -// Subrouter creates a subrouter for the route. -// -// It will test the inner routes only if the parent route matched. For example: -// -// r := mux.NewRouter() -// s := r.Host("www.example.com").Subrouter() -// s.HandleFunc("/products/", ProductsHandler) -// s.HandleFunc("/products/{key}", ProductHandler) -// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) -// -// Here, the routes registered in the subrouter won't be tested if the host -// doesn't match. -func (r *Route) Subrouter() *Router { - router := &Router{parent: r, strictSlash: r.strictSlash} - r.addMatcher(router) - return router -} - -// ---------------------------------------------------------------------------- -// URL building -// ---------------------------------------------------------------------------- - -// URL builds a URL for the route. -// -// It accepts a sequence of key/value pairs for the route variables. For -// example, given this route: -// -// r := mux.NewRouter() -// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). -// Name("article") -// -// ...a URL for it can be built using: -// -// url, err := r.Get("article").URL("category", "technology", "id", "42") -// -// ...which will return an url.URL with the following path: -// -// "/articles/technology/42" -// -// This also works for host variables: -// -// r := mux.NewRouter() -// r.Host("{subdomain}.domain.com"). -// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). -// Name("article") -// -// // url.String() will be "http://news.domain.com/articles/technology/42" -// url, err := r.Get("article").URL("subdomain", "news", -// "category", "technology", -// "id", "42") -// -// All variables defined in the route are required, and their values must -// conform to the corresponding patterns. -func (r *Route) URL(pairs ...string) (*url.URL, error) { - if r.err != nil { - return nil, r.err - } - if r.regexp == nil { - return nil, errors.New("mux: route doesn't have a host or path") - } - values, err := r.prepareVars(pairs...) - if err != nil { - return nil, err - } - var scheme, host, path string - if r.regexp.host != nil { - // Set a default scheme. - scheme = "http" - if host, err = r.regexp.host.url(values); err != nil { - return nil, err - } - } - if r.regexp.path != nil { - if path, err = r.regexp.path.url(values); err != nil { - return nil, err - } - } - return &url.URL{ - Scheme: scheme, - Host: host, - Path: path, - }, nil -} - -// URLHost builds the host part of the URL for a route. See Route.URL(). -// -// The route must have a host defined. -func (r *Route) URLHost(pairs ...string) (*url.URL, error) { - if r.err != nil { - return nil, r.err - } - if r.regexp == nil || r.regexp.host == nil { - return nil, errors.New("mux: route doesn't have a host") - } - values, err := r.prepareVars(pairs...) - if err != nil { - return nil, err - } - host, err := r.regexp.host.url(values) - if err != nil { - return nil, err - } - return &url.URL{ - Scheme: "http", - Host: host, - }, nil -} - -// URLPath builds the path part of the URL for a route. See Route.URL(). -// -// The route must have a path defined. -func (r *Route) URLPath(pairs ...string) (*url.URL, error) { - if r.err != nil { - return nil, r.err - } - if r.regexp == nil || r.regexp.path == nil { - return nil, errors.New("mux: route doesn't have a path") - } - values, err := r.prepareVars(pairs...) - if err != nil { - return nil, err - } - path, err := r.regexp.path.url(values) - if err != nil { - return nil, err - } - return &url.URL{ - Path: path, - }, nil -} - -// prepareVars converts the route variable pairs into a map. If the route has a -// BuildVarsFunc, it is invoked. -func (r *Route) prepareVars(pairs ...string) (map[string]string, error) { - m, err := mapFromPairsToString(pairs...) - if err != nil { - return nil, err - } - return r.buildVars(m), nil -} - -func (r *Route) buildVars(m map[string]string) map[string]string { - if r.parent != nil { - m = r.parent.buildVars(m) - } - if r.buildVarsFunc != nil { - m = r.buildVarsFunc(m) - } - return m -} - -// ---------------------------------------------------------------------------- -// parentRoute -// ---------------------------------------------------------------------------- - -// parentRoute allows routes to know about parent host and path definitions. -type parentRoute interface { - getNamedRoutes() map[string]*Route - getRegexpGroup() *routeRegexpGroup - buildVars(map[string]string) map[string]string -} - -// getNamedRoutes returns the map where named routes are registered. -func (r *Route) getNamedRoutes() map[string]*Route { - if r.parent == nil { - // During tests router is not always set. - r.parent = NewRouter() - } - return r.parent.getNamedRoutes() -} - -// getRegexpGroup returns regexp definitions from this route. -func (r *Route) getRegexpGroup() *routeRegexpGroup { - if r.regexp == nil { - if r.parent == nil { - // During tests router is not always set. - r.parent = NewRouter() - } - regexp := r.parent.getRegexpGroup() - if regexp == nil { - r.regexp = new(routeRegexpGroup) - } else { - // Copy. - r.regexp = &routeRegexpGroup{ - host: regexp.host, - path: regexp.path, - queries: regexp.queries, - } - } - } - return r.regexp -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore b/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore deleted file mode 100644 index 5514532..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.DS_Store -*.test -. diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/.travis.yml b/Godeps/_workspace/src/github.com/onsi/gomega/.travis.yml deleted file mode 100644 index 79780ec..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: go -go: - - 1.4 - - 1.5 - -install: - - go get -v ./... - - go get github.com/onsi/ginkgo - - go install github.com/onsi/ginkgo/ginkgo - -script: $HOME/gopath/bin/ginkgo -r --randomizeAllSpecs --failOnPending --randomizeSuites --race diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/CHANGELOG.md b/Godeps/_workspace/src/github.com/onsi/gomega/CHANGELOG.md deleted file mode 100644 index 0c5ede5..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/CHANGELOG.md +++ /dev/null @@ -1,70 +0,0 @@ -## HEAD - -Improvements: - -- Added `BeSent` which attempts to send a value down a channel and fails if the attempt blocks. Can be paired with `Eventually` to safely send a value down a channel with a timeout. -- `Ω`, `Expect`, `Eventually`, and `Consistently` now immediately `panic` if there is no registered fail handler. This is always a mistake that can hide failing tests. -- `Receive()` no longer errors when passed a closed channel, it's perfectly fine to attempt to read from a closed channel so Ω(c).Should(Receive()) always fails and Ω(c).ShoudlNot(Receive()) always passes with a closed channel. -- Added `HavePrefix` and `HaveSuffix` matchers. -- `ghttp` can now handle concurrent requests. -- Added `Succeed` which allows one to write `Ω(MyFunction()).Should(Succeed())`. -- Improved `ghttp`'s behavior around failing assertions and panics: - - If a registered handler makes a failing assertion `ghttp` will return `500`. - - If a registered handler panics, `ghttp` will return `500` *and* fail the test. This is new behavior that may cause existing code to break. This code is almost certainly incorrect and creating a false positive. -- `ghttp` servers can take an `io.Writer`. `ghttp` will write a line to the writer when each request arrives. -- Added `WithTransform` matcher to allow munging input data before feeding into the relevant matcher -- Added boolean `And`, `Or`, and `Not` matchers to allow creating composite matchers - -Bug Fixes: -- gexec: `session.Wait` now uses `EventuallyWithOffset` to get the right line number in the failure. -- `ContainElement` no longer bails if a passed-in matcher errors. - -## 1.0 (8/2/2014) - -No changes. Dropping "beta" from the version number. - -## 1.0.0-beta (7/8/2014) -Breaking Changes: - -- Changed OmegaMatcher interface. Instead of having `Match` return failure messages, two new methods `FailureMessage` and `NegatedFailureMessage` are called instead. -- Moved and renamed OmegaFailHandler to types.GomegaFailHandler and OmegaMatcher to types.GomegaMatcher. Any references to OmegaMatcher in any custom matchers will need to be changed to point to types.GomegaMatcher - -New Test-Support Features: - -- `ghttp`: supports testing http clients - - Provides a flexible fake http server - - Provides a collection of chainable http handlers that perform assertions. -- `gbytes`: supports making ordered assertions against streams of data - - Provides a `gbytes.Buffer` - - Provides a `Say` matcher to perform ordered assertions against output data -- `gexec`: supports testing external processes - - Provides support for building Go binaries - - Wraps and starts `exec.Cmd` commands - - Makes it easy to assert against stdout and stderr - - Makes it easy to send signals and wait for processes to exit - - Provides an `Exit` matcher to assert against exit code. - -DSL Changes: - -- `Eventually` and `Consistently` can accept `time.Duration` interval and polling inputs. -- The default timeouts for `Eventually` and `Consistently` are now configurable. - -New Matchers: - -- `ConsistOf`: order-independent assertion against the elements of an array/slice or keys of a map. -- `BeTemporally`: like `BeNumerically` but for `time.Time` -- `HaveKeyWithValue`: asserts a map has a given key with the given value. - -Updated Matchers: - -- `Receive` matcher can take a matcher as an argument and passes only if the channel under test receives an objet that satisfies the passed-in matcher. -- Matchers that implement `MatchMayChangeInTheFuture(actual interface{}) bool` can inform `Eventually` and/or `Consistently` when a match has no chance of changing status in the future. For example, `Receive` returns `false` when a channel is closed. - -Misc: - -- Start using semantic versioning -- Start maintaining changelog - -Major refactor: - -- Pull out Gomega's internal to `internal` diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/LICENSE b/Godeps/_workspace/src/github.com/onsi/gomega/LICENSE deleted file mode 100644 index 9415ee7..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2013-2014 Onsi Fakhouri - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/README.md b/Godeps/_workspace/src/github.com/onsi/gomega/README.md deleted file mode 100644 index c825591..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/README.md +++ /dev/null @@ -1,17 +0,0 @@ -![Gomega: Ginkgo's Preferred Matcher Library](http://onsi.github.io/gomega/images/gomega.png) - -[![Build Status](https://travis-ci.org/onsi/gomega.png)](https://travis-ci.org/onsi/gomega) - -Jump straight to the [docs](http://onsi.github.io/gomega/) to learn about Gomega, including a list of [all available matchers](http://onsi.github.io/gomega/#provided-matchers). - -To discuss Gomega and get updates, join the [google group](https://groups.google.com/d/forum/ginkgo-and-gomega). - -## [Ginkgo](http://github.com/onsi/ginkgo): a BDD Testing Framework for Golang - -Learn more about Ginkgo [here](http://onsi.github.io/ginkgo/) - -## License - -Gomega is MIT-Licensed - -The `ConsistOf` matcher uses [goraph](https://github.com/amitkgupta/goraph) which is embedded in the source to simplify distribution. goraph has an MIT license. diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/format/format.go b/Godeps/_workspace/src/github.com/onsi/gomega/format/format.go deleted file mode 100644 index ec9c91a..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/format/format.go +++ /dev/null @@ -1,276 +0,0 @@ -/* -Gomega's format package pretty-prints objects. It explores input objects recursively and generates formatted, indented output with type information. -*/ -package format - -import ( - "fmt" - "reflect" - "strings" -) - -// Use MaxDepth to set the maximum recursion depth when printing deeply nested objects -var MaxDepth = uint(10) - -/* -By default, all objects (even those that implement fmt.Stringer and fmt.GoStringer) are recursively inspected to generate output. - -Set UseStringerRepresentation = true to use GoString (for fmt.GoStringers) or String (for fmt.Stringer) instead. - -Note that GoString and String don't always have all the information you need to understand why a test failed! -*/ -var UseStringerRepresentation = false - -//The default indentation string emitted by the format package -var Indent = " " - -var longFormThreshold = 20 - -/* -Generates a formatted matcher success/failure message of the form: - - Expected - - - - -If expected is omited, then the message looks like: - - Expected - - -*/ -func Message(actual interface{}, message string, expected ...interface{}) string { - if len(expected) == 0 { - return fmt.Sprintf("Expected\n%s\n%s", Object(actual, 1), message) - } else { - return fmt.Sprintf("Expected\n%s\n%s\n%s", Object(actual, 1), message, Object(expected[0], 1)) - } -} - -/* -Pretty prints the passed in object at the passed in indentation level. - -Object recurses into deeply nested objects emitting pretty-printed representations of their components. - -Modify format.MaxDepth to control how deep the recursion is allowed to go -Set format.UseStringerRepresentation to true to return object.GoString() or object.String() when available instead of -recursing into the object. -*/ -func Object(object interface{}, indentation uint) string { - indent := strings.Repeat(Indent, int(indentation)) - value := reflect.ValueOf(object) - return fmt.Sprintf("%s<%s>: %s", indent, formatType(object), formatValue(value, indentation)) -} - -/* -IndentString takes a string and indents each line by the specified amount. -*/ -func IndentString(s string, indentation uint) string { - components := strings.Split(s, "\n") - result := "" - indent := strings.Repeat(Indent, int(indentation)) - for i, component := range components { - result += indent + component - if i < len(components)-1 { - result += "\n" - } - } - - return result -} - -func formatType(object interface{}) string { - t := reflect.TypeOf(object) - if t == nil { - return "nil" - } - switch t.Kind() { - case reflect.Chan: - v := reflect.ValueOf(object) - return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap()) - case reflect.Ptr: - return fmt.Sprintf("%T | %p", object, object) - case reflect.Slice: - v := reflect.ValueOf(object) - return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap()) - case reflect.Map: - v := reflect.ValueOf(object) - return fmt.Sprintf("%T | len:%d", object, v.Len()) - default: - return fmt.Sprintf("%T", object) - } -} - -func formatValue(value reflect.Value, indentation uint) string { - if indentation > MaxDepth { - return "..." - } - - if isNilValue(value) { - return "nil" - } - - if UseStringerRepresentation { - if value.CanInterface() { - obj := value.Interface() - switch x := obj.(type) { - case fmt.GoStringer: - return x.GoString() - case fmt.Stringer: - return x.String() - } - } - } - - switch value.Kind() { - case reflect.Bool: - return fmt.Sprintf("%v", value.Bool()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return fmt.Sprintf("%v", value.Int()) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return fmt.Sprintf("%v", value.Uint()) - case reflect.Uintptr: - return fmt.Sprintf("0x%x", value.Uint()) - case reflect.Float32, reflect.Float64: - return fmt.Sprintf("%v", value.Float()) - case reflect.Complex64, reflect.Complex128: - return fmt.Sprintf("%v", value.Complex()) - case reflect.Chan: - return fmt.Sprintf("0x%x", value.Pointer()) - case reflect.Func: - return fmt.Sprintf("0x%x", value.Pointer()) - case reflect.Ptr: - return formatValue(value.Elem(), indentation) - case reflect.Slice: - if value.Type().Elem().Kind() == reflect.Uint8 { - return formatString(value.Bytes(), indentation) - } - return formatSlice(value, indentation) - case reflect.String: - return formatString(value.String(), indentation) - case reflect.Array: - return formatSlice(value, indentation) - case reflect.Map: - return formatMap(value, indentation) - case reflect.Struct: - return formatStruct(value, indentation) - case reflect.Interface: - return formatValue(value.Elem(), indentation) - default: - if value.CanInterface() { - return fmt.Sprintf("%#v", value.Interface()) - } else { - return fmt.Sprintf("%#v", value) - } - } -} - -func formatString(object interface{}, indentation uint) string { - if indentation == 1 { - s := fmt.Sprintf("%s", object) - components := strings.Split(s, "\n") - result := "" - for i, component := range components { - if i == 0 { - result += component - } else { - result += Indent + component - } - if i < len(components)-1 { - result += "\n" - } - } - - return fmt.Sprintf("%s", result) - } else { - return fmt.Sprintf("%q", object) - } -} - -func formatSlice(v reflect.Value, indentation uint) string { - l := v.Len() - result := make([]string, l) - longest := 0 - for i := 0; i < l; i++ { - result[i] = formatValue(v.Index(i), indentation+1) - if len(result[i]) > longest { - longest = len(result[i]) - } - } - - if longest > longFormThreshold { - indenter := strings.Repeat(Indent, int(indentation)) - return fmt.Sprintf("[\n%s%s,\n%s]", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter) - } else { - return fmt.Sprintf("[%s]", strings.Join(result, ", ")) - } -} - -func formatMap(v reflect.Value, indentation uint) string { - l := v.Len() - result := make([]string, l) - - longest := 0 - for i, key := range v.MapKeys() { - value := v.MapIndex(key) - result[i] = fmt.Sprintf("%s: %s", formatValue(key, 0), formatValue(value, indentation+1)) - if len(result[i]) > longest { - longest = len(result[i]) - } - } - - if longest > longFormThreshold { - indenter := strings.Repeat(Indent, int(indentation)) - return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter) - } else { - return fmt.Sprintf("{%s}", strings.Join(result, ", ")) - } -} - -func formatStruct(v reflect.Value, indentation uint) string { - t := v.Type() - - l := v.NumField() - result := []string{} - longest := 0 - for i := 0; i < l; i++ { - structField := t.Field(i) - fieldEntry := v.Field(i) - representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1)) - result = append(result, representation) - if len(representation) > longest { - longest = len(representation) - } - } - if longest > longFormThreshold { - indenter := strings.Repeat(Indent, int(indentation)) - return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter) - } else { - return fmt.Sprintf("{%s}", strings.Join(result, ", ")) - } -} - -func isNilValue(a reflect.Value) bool { - switch a.Kind() { - case reflect.Invalid: - return true - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: - return a.IsNil() - } - - return false -} - -func isNil(a interface{}) bool { - if a == nil { - return true - } - - switch reflect.TypeOf(a).Kind() { - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: - return reflect.ValueOf(a).IsNil() - } - - return false -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/gbytes/buffer.go b/Godeps/_workspace/src/github.com/onsi/gomega/gbytes/buffer.go deleted file mode 100644 index 8775b86..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/gbytes/buffer.go +++ /dev/null @@ -1,229 +0,0 @@ -/* -Package gbytes provides a buffer that supports incrementally detecting input. - -You use gbytes.Buffer with the gbytes.Say matcher. When Say finds a match, it fastforwards the buffer's read cursor to the end of that match. - -Subsequent matches against the buffer will only operate against data that appears *after* the read cursor. - -The read cursor is an opaque implementation detail that you cannot access. You should use the Say matcher to sift through the buffer. You can always -access the entire buffer's contents with Contents(). - -*/ -package gbytes - -import ( - "errors" - "fmt" - "io" - "regexp" - "sync" - "time" -) - -/* -gbytes.Buffer implements an io.Writer and can be used with the gbytes.Say matcher. - -You should only use a gbytes.Buffer in test code. It stores all writes in an in-memory buffer - behavior that is inappropriate for production code! -*/ -type Buffer struct { - contents []byte - readCursor uint64 - lock *sync.Mutex - detectCloser chan interface{} - closed bool -} - -/* -NewBuffer returns a new gbytes.Buffer -*/ -func NewBuffer() *Buffer { - return &Buffer{ - lock: &sync.Mutex{}, - } -} - -/* -BufferWithBytes returns a new gbytes.Buffer seeded with the passed in bytes -*/ -func BufferWithBytes(bytes []byte) *Buffer { - return &Buffer{ - lock: &sync.Mutex{}, - contents: bytes, - } -} - -/* -Write implements the io.Writer interface -*/ -func (b *Buffer) Write(p []byte) (n int, err error) { - b.lock.Lock() - defer b.lock.Unlock() - - if b.closed { - return 0, errors.New("attempt to write to closed buffer") - } - - b.contents = append(b.contents, p...) - return len(p), nil -} - -/* -Read implements the io.Reader interface. It advances the -cursor as it reads. - -Returns an error if called after Close. -*/ -func (b *Buffer) Read(d []byte) (int, error) { - b.lock.Lock() - defer b.lock.Unlock() - - if b.closed { - return 0, errors.New("attempt to read from closed buffer") - } - - if uint64(len(b.contents)) <= b.readCursor { - return 0, io.EOF - } - - n := copy(d, b.contents[b.readCursor:]) - b.readCursor += uint64(n) - - return n, nil -} - -/* -Close signifies that the buffer will no longer be written to -*/ -func (b *Buffer) Close() error { - b.lock.Lock() - defer b.lock.Unlock() - - b.closed = true - - return nil -} - -/* -Closed returns true if the buffer has been closed -*/ -func (b *Buffer) Closed() bool { - b.lock.Lock() - defer b.lock.Unlock() - - return b.closed -} - -/* -Contents returns all data ever written to the buffer. -*/ -func (b *Buffer) Contents() []byte { - b.lock.Lock() - defer b.lock.Unlock() - - contents := make([]byte, len(b.contents)) - copy(contents, b.contents) - return contents -} - -/* -Detect takes a regular expression and returns a channel. - -The channel will receive true the first time data matching the regular expression is written to the buffer. -The channel is subsequently closed and the buffer's read-cursor is fast-forwarded to just after the matching region. - -You typically don't need to use Detect and should use the ghttp.Say matcher instead. Detect is useful, however, in cases where your code must -be branch and handle different outputs written to the buffer. - -For example, consider a buffer hooked up to the stdout of a client library. You may (or may not, depending on state outside of your control) need to authenticate the client library. - -You could do something like: - -select { -case <-buffer.Detect("You are not logged in"): - //log in -case <-buffer.Detect("Success"): - //carry on -case <-time.After(time.Second): - //welp -} -buffer.CancelDetects() - -You should always call CancelDetects after using Detect. This will close any channels that have not detected and clean up the goroutines that were spawned to support them. - -Finally, you can pass detect a format string followed by variadic arguments. This will construct the regexp using fmt.Sprintf. -*/ -func (b *Buffer) Detect(desired string, args ...interface{}) chan bool { - formattedRegexp := desired - if len(args) > 0 { - formattedRegexp = fmt.Sprintf(desired, args...) - } - re := regexp.MustCompile(formattedRegexp) - - b.lock.Lock() - defer b.lock.Unlock() - - if b.detectCloser == nil { - b.detectCloser = make(chan interface{}) - } - - closer := b.detectCloser - response := make(chan bool) - go func() { - ticker := time.NewTicker(10 * time.Millisecond) - defer ticker.Stop() - defer close(response) - for { - select { - case <-ticker.C: - b.lock.Lock() - data, cursor := b.contents[b.readCursor:], b.readCursor - loc := re.FindIndex(data) - b.lock.Unlock() - - if loc != nil { - response <- true - b.lock.Lock() - newCursorPosition := cursor + uint64(loc[1]) - if newCursorPosition >= b.readCursor { - b.readCursor = newCursorPosition - } - b.lock.Unlock() - return - } - case <-closer: - return - } - } - }() - - return response -} - -/* -CancelDetects cancels any pending detects and cleans up their goroutines. You should always call this when you're done with a set of Detect channels. -*/ -func (b *Buffer) CancelDetects() { - b.lock.Lock() - defer b.lock.Unlock() - - close(b.detectCloser) - b.detectCloser = nil -} - -func (b *Buffer) didSay(re *regexp.Regexp) (bool, []byte) { - b.lock.Lock() - defer b.lock.Unlock() - - unreadBytes := b.contents[b.readCursor:] - copyOfUnreadBytes := make([]byte, len(unreadBytes)) - copy(copyOfUnreadBytes, unreadBytes) - - loc := re.FindIndex(unreadBytes) - - if loc != nil { - b.readCursor += uint64(loc[1]) - return true, copyOfUnreadBytes - } else { - return false, copyOfUnreadBytes - } -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/gbytes/say_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/gbytes/say_matcher.go deleted file mode 100644 index ce5ebcb..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/gbytes/say_matcher.go +++ /dev/null @@ -1,105 +0,0 @@ -package gbytes - -import ( - "fmt" - "regexp" - - "github.com/onsi/gomega/format" -) - -//Objects satisfying the BufferProvider can be used with the Say matcher. -type BufferProvider interface { - Buffer() *Buffer -} - -/* -Say is a Gomega matcher that operates on gbytes.Buffers: - - Ω(buffer).Should(Say("something")) - -will succeed if the unread portion of the buffer matches the regular expression "something". - -When Say succeeds, it fast forwards the gbytes.Buffer's read cursor to just after the succesful match. -Thus, subsequent calls to Say will only match against the unread portion of the buffer - -Say pairs very well with Eventually. To asser that a buffer eventually receives data matching "[123]-star" within 3 seconds you can: - - Eventually(buffer, 3).Should(Say("[123]-star")) - -Ditto with consistently. To assert that a buffer does not receive data matching "never-see-this" for 1 second you can: - - Consistently(buffer, 1).ShouldNot(Say("never-see-this")) - -In addition to bytes.Buffers, Say can operate on objects that implement the gbytes.BufferProvider interface. -In such cases, Say simply operates on the *gbytes.Buffer returned by Buffer() - -If the buffer is closed, the Say matcher will tell Eventually to abort. -*/ -func Say(expected string, args ...interface{}) *sayMatcher { - formattedRegexp := expected - if len(args) > 0 { - formattedRegexp = fmt.Sprintf(expected, args...) - } - return &sayMatcher{ - re: regexp.MustCompile(formattedRegexp), - } -} - -type sayMatcher struct { - re *regexp.Regexp - receivedSayings []byte -} - -func (m *sayMatcher) buffer(actual interface{}) (*Buffer, bool) { - var buffer *Buffer - - switch x := actual.(type) { - case *Buffer: - buffer = x - case BufferProvider: - buffer = x.Buffer() - default: - return nil, false - } - - return buffer, true -} - -func (m *sayMatcher) Match(actual interface{}) (success bool, err error) { - buffer, ok := m.buffer(actual) - if !ok { - return false, fmt.Errorf("Say must be passed a *gbytes.Buffer or BufferProvider. Got:\n%s", format.Object(actual, 1)) - } - - didSay, sayings := buffer.didSay(m.re) - m.receivedSayings = sayings - - return didSay, nil -} - -func (m *sayMatcher) FailureMessage(actual interface{}) (message string) { - return fmt.Sprintf( - "Got stuck at:\n%s\nWaiting for:\n%s", - format.IndentString(string(m.receivedSayings), 1), - format.IndentString(m.re.String(), 1), - ) -} - -func (m *sayMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return fmt.Sprintf( - "Saw:\n%s\nWhich matches the unexpected:\n%s", - format.IndentString(string(m.receivedSayings), 1), - format.IndentString(m.re.String(), 1), - ) -} - -func (m *sayMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { - switch x := actual.(type) { - case *Buffer: - return !x.Closed() - case BufferProvider: - return !x.Buffer().Closed() - default: - return true - } -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/gexec/build.go b/Godeps/_workspace/src/github.com/onsi/gomega/gexec/build.go deleted file mode 100644 index 3e9bf9f..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/gexec/build.go +++ /dev/null @@ -1,78 +0,0 @@ -package gexec - -import ( - "errors" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path" - "path/filepath" - "runtime" -) - -var tmpDir string - -/* -Build uses go build to compile the package at packagePath. The resulting binary is saved off in a temporary directory. -A path pointing to this binary is returned. - -Build uses the $GOPATH set in your environment. It passes the variadic args on to `go build`. -*/ -func Build(packagePath string, args ...string) (compiledPath string, err error) { - return BuildIn(os.Getenv("GOPATH"), packagePath, args...) -} - -/* -BuildIn is identical to Build but allows you to specify a custom $GOPATH (the first argument). -*/ -func BuildIn(gopath string, packagePath string, args ...string) (compiledPath string, err error) { - tmpDir, err := temporaryDirectory() - if err != nil { - return "", err - } - - if len(gopath) == 0 { - return "", errors.New("$GOPATH not provided when building " + packagePath) - } - - executable := filepath.Join(tmpDir, path.Base(packagePath)) - if runtime.GOOS == "windows" { - executable = executable + ".exe" - } - - cmdArgs := append([]string{"build"}, args...) - cmdArgs = append(cmdArgs, "-o", executable, packagePath) - - build := exec.Command("go", cmdArgs...) - build.Env = append([]string{"GOPATH=" + gopath}, os.Environ()...) - - output, err := build.CombinedOutput() - if err != nil { - return "", fmt.Errorf("Failed to build %s:\n\nError:\n%s\n\nOutput:\n%s", packagePath, err, string(output)) - } - - return executable, nil -} - -/* -You should call CleanupBuildArtifacts before your test ends to clean up any temporary artifacts generated by -gexec. In Ginkgo this is typically done in an AfterSuite callback. -*/ -func CleanupBuildArtifacts() { - if tmpDir != "" { - os.RemoveAll(tmpDir) - } -} - -func temporaryDirectory() (string, error) { - var err error - if tmpDir == "" { - tmpDir, err = ioutil.TempDir("", "gexec_artifacts") - if err != nil { - return "", err - } - } - - return ioutil.TempDir(tmpDir, "g") -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/gexec/exit_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/gexec/exit_matcher.go deleted file mode 100644 index e6f4329..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/gexec/exit_matcher.go +++ /dev/null @@ -1,88 +0,0 @@ -package gexec - -import ( - "fmt" - - "github.com/onsi/gomega/format" -) - -/* -The Exit matcher operates on a session: - - Ω(session).Should(Exit()) - -Exit passes if the session has already exited. - -If no status code is provided, then Exit will succeed if the session has exited regardless of exit code. -Otherwise, Exit will only succeed if the process has exited with the provided status code. - -Note that the process must have already exited. To wait for a process to exit, use Eventually: - - Eventually(session, 3).Should(Exit(0)) -*/ -func Exit(optionalExitCode ...int) *exitMatcher { - exitCode := -1 - if len(optionalExitCode) > 0 { - exitCode = optionalExitCode[0] - } - - return &exitMatcher{ - exitCode: exitCode, - } -} - -type exitMatcher struct { - exitCode int - didExit bool - actualExitCode int -} - -type Exiter interface { - ExitCode() int -} - -func (m *exitMatcher) Match(actual interface{}) (success bool, err error) { - exiter, ok := actual.(Exiter) - if !ok { - return false, fmt.Errorf("Exit must be passed a gexec.Exiter (Missing method ExitCode() int) Got:\n%s", format.Object(actual, 1)) - } - - m.actualExitCode = exiter.ExitCode() - - if m.actualExitCode == -1 { - return false, nil - } - - if m.exitCode == -1 { - return true, nil - } - return m.exitCode == m.actualExitCode, nil -} - -func (m *exitMatcher) FailureMessage(actual interface{}) (message string) { - if m.actualExitCode == -1 { - return "Expected process to exit. It did not." - } else { - return format.Message(m.actualExitCode, "to match exit code:", m.exitCode) - } -} - -func (m *exitMatcher) NegatedFailureMessage(actual interface{}) (message string) { - if m.actualExitCode == -1 { - return "you really shouldn't be able to see this!" - } else { - if m.exitCode == -1 { - return "Expected process not to exit. It did." - } else { - return format.Message(m.actualExitCode, "not to match exit code:", m.exitCode) - } - } -} - -func (m *exitMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { - session, ok := actual.(*Session) - if ok { - return session.ExitCode() == -1 - } - return true -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/gexec/prefixed_writer.go b/Godeps/_workspace/src/github.com/onsi/gomega/gexec/prefixed_writer.go deleted file mode 100644 index 05e695a..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/gexec/prefixed_writer.go +++ /dev/null @@ -1,53 +0,0 @@ -package gexec - -import ( - "io" - "sync" -) - -/* -PrefixedWriter wraps an io.Writer, emiting the passed in prefix at the beginning of each new line. -This can be useful when running multiple gexec.Sessions concurrently - you can prefix the log output of each -session by passing in a PrefixedWriter: - -gexec.Start(cmd, NewPrefixedWriter("[my-cmd] ", GinkgoWriter), NewPrefixedWriter("[my-cmd] ", GinkgoWriter)) -*/ -type PrefixedWriter struct { - prefix []byte - writer io.Writer - lock *sync.Mutex - atStartOfLine bool -} - -func NewPrefixedWriter(prefix string, writer io.Writer) *PrefixedWriter { - return &PrefixedWriter{ - prefix: []byte(prefix), - writer: writer, - lock: &sync.Mutex{}, - atStartOfLine: true, - } -} - -func (w *PrefixedWriter) Write(b []byte) (int, error) { - w.lock.Lock() - defer w.lock.Unlock() - - toWrite := []byte{} - - for _, c := range b { - if w.atStartOfLine { - toWrite = append(toWrite, w.prefix...) - } - - toWrite = append(toWrite, c) - - w.atStartOfLine = c == '\n' - } - - _, err := w.writer.Write(toWrite) - if err != nil { - return 0, err - } - - return len(b), nil -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/gexec/session.go b/Godeps/_workspace/src/github.com/onsi/gomega/gexec/session.go deleted file mode 100644 index 46e7122..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/gexec/session.go +++ /dev/null @@ -1,214 +0,0 @@ -/* -Package gexec provides support for testing external processes. -*/ -package gexec - -import ( - "io" - "os" - "os/exec" - "reflect" - "sync" - "syscall" - - . "github.com/onsi/gomega" - "github.com/onsi/gomega/gbytes" -) - -const INVALID_EXIT_CODE = 254 - -type Session struct { - //The wrapped command - Command *exec.Cmd - - //A *gbytes.Buffer connected to the command's stdout - Out *gbytes.Buffer - - //A *gbytes.Buffer connected to the command's stderr - Err *gbytes.Buffer - - //A channel that will close when the command exits - Exited <-chan struct{} - - lock *sync.Mutex - exitCode int -} - -/* -Start starts the passed-in *exec.Cmd command. It wraps the command in a *gexec.Session. - -The session pipes the command's stdout and stderr to two *gbytes.Buffers available as properties on the session: session.Out and session.Err. -These buffers can be used with the gbytes.Say matcher to match against unread output: - - Ω(session.Out).Should(gbytes.Say("foo-out")) - Ω(session.Err).Should(gbytes.Say("foo-err")) - -In addition, Session satisfies the gbytes.BufferProvider interface and provides the stdout *gbytes.Buffer. This allows you to replace the first line, above, with: - - Ω(session).Should(gbytes.Say("foo-out")) - -When outWriter and/or errWriter are non-nil, the session will pipe stdout and/or stderr output both into the session *gybtes.Buffers and to the passed-in outWriter/errWriter. -This is useful for capturing the process's output or logging it to screen. In particular, when using Ginkgo it can be convenient to direct output to the GinkgoWriter: - - session, err := Start(command, GinkgoWriter, GinkgoWriter) - -This will log output when running tests in verbose mode, but - otherwise - will only log output when a test fails. - -The session wrapper is responsible for waiting on the *exec.Cmd command. You *should not* call command.Wait() yourself. -Instead, to assert that the command has exited you can use the gexec.Exit matcher: - - Ω(session).Should(gexec.Exit()) - -When the session exits it closes the stdout and stderr gbytes buffers. This will short circuit any -Eventuallys waiting fo the buffers to Say something. -*/ -func Start(command *exec.Cmd, outWriter io.Writer, errWriter io.Writer) (*Session, error) { - exited := make(chan struct{}) - - session := &Session{ - Command: command, - Out: gbytes.NewBuffer(), - Err: gbytes.NewBuffer(), - Exited: exited, - lock: &sync.Mutex{}, - exitCode: -1, - } - - var commandOut, commandErr io.Writer - - commandOut, commandErr = session.Out, session.Err - - if outWriter != nil && !reflect.ValueOf(outWriter).IsNil() { - commandOut = io.MultiWriter(commandOut, outWriter) - } - - if errWriter != nil && !reflect.ValueOf(errWriter).IsNil() { - commandErr = io.MultiWriter(commandErr, errWriter) - } - - command.Stdout = commandOut - command.Stderr = commandErr - - err := command.Start() - if err == nil { - go session.monitorForExit(exited) - } - - return session, err -} - -/* -Buffer implements the gbytes.BufferProvider interface and returns s.Out -This allows you to make gbytes.Say matcher assertions against stdout without having to reference .Out: - - Eventually(session).Should(gbytes.Say("foo")) -*/ -func (s *Session) Buffer() *gbytes.Buffer { - return s.Out -} - -/* -ExitCode returns the wrapped command's exit code. If the command hasn't exited yet, ExitCode returns -1. - -To assert that the command has exited it is more convenient to use the Exit matcher: - - Eventually(s).Should(gexec.Exit()) - -When the process exits because it has received a particular signal, the exit code will be 128+signal-value -(See http://www.tldp.org/LDP/abs/html/exitcodes.html and http://man7.org/linux/man-pages/man7/signal.7.html) - -*/ -func (s *Session) ExitCode() int { - s.lock.Lock() - defer s.lock.Unlock() - return s.exitCode -} - -/* -Wait waits until the wrapped command exits. It can be passed an optional timeout. -If the command does not exit within the timeout, Wait will trigger a test failure. - -Wait returns the session, making it possible to chain: - - session.Wait().Out.Contents() - -will wait for the command to exit then return the entirety of Out's contents. - -Wait uses eventually under the hood and accepts the same timeout/polling intervals that eventually does. -*/ -func (s *Session) Wait(timeout ...interface{}) *Session { - EventuallyWithOffset(1, s, timeout...).Should(Exit()) - return s -} - -/* -Kill sends the running command a SIGKILL signal. It does not wait for the process to exit. - -If the command has already exited, Kill returns silently. - -The session is returned to enable chaining. -*/ -func (s *Session) Kill() *Session { - if s.ExitCode() != -1 { - return s - } - s.Command.Process.Kill() - return s -} - -/* -Interrupt sends the running command a SIGINT signal. It does not wait for the process to exit. - -If the command has already exited, Interrupt returns silently. - -The session is returned to enable chaining. -*/ -func (s *Session) Interrupt() *Session { - return s.Signal(syscall.SIGINT) -} - -/* -Terminate sends the running command a SIGTERM signal. It does not wait for the process to exit. - -If the command has already exited, Terminate returns silently. - -The session is returned to enable chaining. -*/ -func (s *Session) Terminate() *Session { - return s.Signal(syscall.SIGTERM) -} - -/* -Terminate sends the running command the passed in signal. It does not wait for the process to exit. - -If the command has already exited, Signal returns silently. - -The session is returned to enable chaining. -*/ -func (s *Session) Signal(signal os.Signal) *Session { - if s.ExitCode() != -1 { - return s - } - s.Command.Process.Signal(signal) - return s -} - -func (s *Session) monitorForExit(exited chan<- struct{}) { - err := s.Command.Wait() - s.lock.Lock() - s.Out.Close() - s.Err.Close() - status := s.Command.ProcessState.Sys().(syscall.WaitStatus) - if status.Signaled() { - s.exitCode = 128 + int(status.Signal()) - } else { - exitStatus := status.ExitStatus() - if exitStatus == -1 && err != nil { - s.exitCode = INVALID_EXIT_CODE - } - s.exitCode = exitStatus - } - s.lock.Unlock() - - close(exited) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/handlers.go b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/handlers.go deleted file mode 100644 index 63ff691..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/handlers.go +++ /dev/null @@ -1,313 +0,0 @@ -package ghttp - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "reflect" - - "github.com/golang/protobuf/proto" - . "github.com/onsi/gomega" - "github.com/onsi/gomega/types" -) - -//CombineHandler takes variadic list of handlers and produces one handler -//that calls each handler in order. -func CombineHandlers(handlers ...http.HandlerFunc) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - for _, handler := range handlers { - handler(w, req) - } - } -} - -//VerifyRequest returns a handler that verifies that a request uses the specified method to connect to the specified path -//You may also pass in an optional rawQuery string which is tested against the request's `req.URL.RawQuery` -// -//For path, you may pass in a string, in which case strict equality will be applied -//Alternatively you can pass in a matcher (ContainSubstring("/foo") and MatchRegexp("/foo/[a-f0-9]+") for example) -func VerifyRequest(method string, path interface{}, rawQuery ...string) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - Ω(req.Method).Should(Equal(method), "Method mismatch") - switch p := path.(type) { - case types.GomegaMatcher: - Ω(req.URL.Path).Should(p, "Path mismatch") - default: - Ω(req.URL.Path).Should(Equal(path), "Path mismatch") - } - if len(rawQuery) > 0 { - values, err := url.ParseQuery(rawQuery[0]) - Ω(err).ShouldNot(HaveOccurred(), "Expected RawQuery is malformed") - - Ω(req.URL.Query()).Should(Equal(values), "RawQuery mismatch") - } - } -} - -//VerifyContentType returns a handler that verifies that a request has a Content-Type header set to the -//specified value -func VerifyContentType(contentType string) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - Ω(req.Header.Get("Content-Type")).Should(Equal(contentType)) - } -} - -//VerifyBasicAuth returns a handler that verifies the request contains a BasicAuth Authorization header -//matching the passed in username and password -func VerifyBasicAuth(username string, password string) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - auth := req.Header.Get("Authorization") - Ω(auth).ShouldNot(Equal(""), "Authorization header must be specified") - - decoded, err := base64.StdEncoding.DecodeString(auth[6:]) - Ω(err).ShouldNot(HaveOccurred()) - - Ω(string(decoded)).Should(Equal(fmt.Sprintf("%s:%s", username, password)), "Authorization mismatch") - } -} - -//VerifyHeader returns a handler that verifies the request contains the passed in headers. -//The passed in header keys are first canonicalized via http.CanonicalHeaderKey. -// -//The request must contain *all* the passed in headers, but it is allowed to have additional headers -//beyond the passed in set. -func VerifyHeader(header http.Header) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - for key, values := range header { - key = http.CanonicalHeaderKey(key) - Ω(req.Header[key]).Should(Equal(values), "Header mismatch for key: %s", key) - } - } -} - -//VerifyHeaderKV returns a handler that verifies the request contains a header matching the passed in key and values -//(recall that a `http.Header` is a mapping from string (key) to []string (values)) -//It is a convenience wrapper around `VerifyHeader` that allows you to avoid having to create an `http.Header` object. -func VerifyHeaderKV(key string, values ...string) http.HandlerFunc { - return VerifyHeader(http.Header{key: values}) -} - -//VerifyBody returns a handler that verifies that the body of the request matches the passed in byte array. -//It does this using Equal(). -func VerifyBody(expectedBody []byte) http.HandlerFunc { - return CombineHandlers( - func(w http.ResponseWriter, req *http.Request) { - body, err := ioutil.ReadAll(req.Body) - req.Body.Close() - Ω(err).ShouldNot(HaveOccurred()) - Ω(body).Should(Equal(expectedBody), "Body Mismatch") - }, - ) -} - -//VerifyJSON returns a handler that verifies that the body of the request is a valid JSON representation -//matching the passed in JSON string. It does this using Gomega's MatchJSON method -// -//VerifyJSON also verifies that the request's content type is application/json -func VerifyJSON(expectedJSON string) http.HandlerFunc { - return CombineHandlers( - VerifyContentType("application/json"), - func(w http.ResponseWriter, req *http.Request) { - body, err := ioutil.ReadAll(req.Body) - req.Body.Close() - Ω(err).ShouldNot(HaveOccurred()) - Ω(body).Should(MatchJSON(expectedJSON), "JSON Mismatch") - }, - ) -} - -//VerifyJSONRepresenting is similar to VerifyJSON. Instead of taking a JSON string, however, it -//takes an arbitrary JSON-encodable object and verifies that the requests's body is a JSON representation -//that matches the object -func VerifyJSONRepresenting(object interface{}) http.HandlerFunc { - data, err := json.Marshal(object) - Ω(err).ShouldNot(HaveOccurred()) - return CombineHandlers( - VerifyContentType("application/json"), - VerifyJSON(string(data)), - ) -} - -//VerifyForm returns a handler that verifies a request contains the specified form values. -// -//The request must contain *all* of the specified values, but it is allowed to have additional -//form values beyond the passed in set. -func VerifyForm(values url.Values) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - err := r.ParseForm() - Ω(err).ShouldNot(HaveOccurred()) - for key, vals := range values { - Ω(r.Form[key]).Should(Equal(vals), "Form mismatch for key: %s", key) - } - } -} - -//VerifyFormKV returns a handler that verifies a request contains a form key with the specified values. -// -//It is a convenience wrapper around `VerifyForm` that lets you avoid having to create a `url.Values` object. -func VerifyFormKV(key string, values ...string) http.HandlerFunc { - return VerifyForm(url.Values{key: values}) -} - -//VerifyProtoRepresenting returns a handler that verifies that the body of the request is a valid protobuf -//representation of the passed message. -// -//VerifyProtoRepresenting also verifies that the request's content type is application/x-protobuf -func VerifyProtoRepresenting(expected proto.Message) http.HandlerFunc { - return CombineHandlers( - VerifyContentType("application/x-protobuf"), - func(w http.ResponseWriter, req *http.Request) { - body, err := ioutil.ReadAll(req.Body) - Ω(err).ShouldNot(HaveOccurred()) - req.Body.Close() - - expectedType := reflect.TypeOf(expected) - actualValuePtr := reflect.New(expectedType.Elem()) - - actual, ok := actualValuePtr.Interface().(proto.Message) - Ω(ok).Should(BeTrue(), "Message value is not a proto.Message") - - err = proto.Unmarshal(body, actual) - Ω(err).ShouldNot(HaveOccurred(), "Failed to unmarshal protobuf") - - Ω(actual).Should(Equal(expected), "ProtoBuf Mismatch") - }, - ) -} - -func copyHeader(src http.Header, dst http.Header) { - for key, value := range src { - dst[key] = value - } -} - -/* -RespondWith returns a handler that responds to a request with the specified status code and body - -Body may be a string or []byte - -Also, RespondWith can be given an optional http.Header. The headers defined therein will be added to the response headers. -*/ -func RespondWith(statusCode int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - if len(optionalHeader) == 1 { - copyHeader(optionalHeader[0], w.Header()) - } - w.WriteHeader(statusCode) - switch x := body.(type) { - case string: - w.Write([]byte(x)) - case []byte: - w.Write(x) - default: - Ω(body).Should(BeNil(), "Invalid type for body. Should be string or []byte.") - } - } -} - -/* -RespondWithPtr returns a handler that responds to a request with the specified status code and body - -Unlike RespondWith, you pass RepondWithPtr a pointer to the status code and body allowing different tests -to share the same setup but specify different status codes and bodies. - -Also, RespondWithPtr can be given an optional http.Header. The headers defined therein will be added to the response headers. -Since the http.Header can be mutated after the fact you don't need to pass in a pointer. -*/ -func RespondWithPtr(statusCode *int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - if len(optionalHeader) == 1 { - copyHeader(optionalHeader[0], w.Header()) - } - w.WriteHeader(*statusCode) - if body != nil { - switch x := (body).(type) { - case *string: - w.Write([]byte(*x)) - case *[]byte: - w.Write(*x) - default: - Ω(body).Should(BeNil(), "Invalid type for body. Should be string or []byte.") - } - } - } -} - -/* -RespondWithJSONEncoded returns a handler that responds to a request with the specified status code and a body -containing the JSON-encoding of the passed in object - -Also, RespondWithJSONEncoded can be given an optional http.Header. The headers defined therein will be added to the response headers. -*/ -func RespondWithJSONEncoded(statusCode int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc { - data, err := json.Marshal(object) - Ω(err).ShouldNot(HaveOccurred()) - - var headers http.Header - if len(optionalHeader) == 1 { - headers = optionalHeader[0] - } else { - headers = make(http.Header) - } - if _, found := headers["Content-Type"]; !found { - headers["Content-Type"] = []string{"application/json"} - } - return RespondWith(statusCode, string(data), headers) -} - -/* -RespondWithJSONEncodedPtr behaves like RespondWithJSONEncoded but takes a pointer -to a status code and object. - -This allows different tests to share the same setup but specify different status codes and JSON-encoded -objects. - -Also, RespondWithJSONEncodedPtr can be given an optional http.Header. The headers defined therein will be added to the response headers. -Since the http.Header can be mutated after the fact you don't need to pass in a pointer. -*/ -func RespondWithJSONEncodedPtr(statusCode *int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - data, err := json.Marshal(object) - Ω(err).ShouldNot(HaveOccurred()) - var headers http.Header - if len(optionalHeader) == 1 { - headers = optionalHeader[0] - } else { - headers = make(http.Header) - } - if _, found := headers["Content-Type"]; !found { - headers["Content-Type"] = []string{"application/json"} - } - copyHeader(headers, w.Header()) - w.WriteHeader(*statusCode) - w.Write(data) - } -} - -//RespondWithProto returns a handler that responds to a request with the specified status code and a body -//containing the protobuf serialization of the provided message. -// -//Also, RespondWithProto can be given an optional http.Header. The headers defined therein will be added to the response headers. -func RespondWithProto(statusCode int, message proto.Message, optionalHeader ...http.Header) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - data, err := proto.Marshal(message) - Ω(err).ShouldNot(HaveOccurred()) - - var headers http.Header - if len(optionalHeader) == 1 { - headers = optionalHeader[0] - } else { - headers = make(http.Header) - } - if _, found := headers["Content-Type"]; !found { - headers["Content-Type"] = []string{"application/x-protobuf"} - } - copyHeader(headers, w.Header()) - - w.WriteHeader(statusCode) - w.Write(data) - } -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/protobuf.go b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/protobuf.go deleted file mode 100644 index b2972bc..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/protobuf.go +++ /dev/null @@ -1,3 +0,0 @@ -package protobuf - -//go:generate protoc --go_out=. simple_message.proto diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.pb.go b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.pb.go deleted file mode 100644 index c55a484..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.pb.go +++ /dev/null @@ -1,55 +0,0 @@ -// Code generated by protoc-gen-go. -// source: simple_message.proto -// DO NOT EDIT! - -/* -Package protobuf is a generated protocol buffer package. - -It is generated from these files: - simple_message.proto - -It has these top-level messages: - SimpleMessage -*/ -package protobuf - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -type SimpleMessage struct { - Description *string `protobuf:"bytes,1,req,name=description" json:"description,omitempty"` - Id *int32 `protobuf:"varint,2,req,name=id" json:"id,omitempty"` - Metadata *string `protobuf:"bytes,3,opt,name=metadata" json:"metadata,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *SimpleMessage) Reset() { *m = SimpleMessage{} } -func (m *SimpleMessage) String() string { return proto.CompactTextString(m) } -func (*SimpleMessage) ProtoMessage() {} - -func (m *SimpleMessage) GetDescription() string { - if m != nil && m.Description != nil { - return *m.Description - } - return "" -} - -func (m *SimpleMessage) GetId() int32 { - if m != nil && m.Id != nil { - return *m.Id - } - return 0 -} - -func (m *SimpleMessage) GetMetadata() string { - if m != nil && m.Metadata != nil { - return *m.Metadata - } - return "" -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.proto b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.proto deleted file mode 100644 index 35b7145..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; - -package protobuf; - -message SimpleMessage { - required string description = 1; - required int32 id = 2; - optional string metadata = 3; -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/test_server.go b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/test_server.go deleted file mode 100644 index fde65be..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/test_server.go +++ /dev/null @@ -1,368 +0,0 @@ -/* -Package ghttp supports testing HTTP clients by providing a test server (simply a thin wrapper around httptest's server) that supports -registering multiple handlers. Incoming requests are not routed between the different handlers -- rather it is merely the order of the handlers that matters. The first request is handled by the first -registered handler, the second request by the second handler, etc. - -The intent here is to have each handler *verify* that the incoming request is valid. To accomplish, ghttp -also provides a collection of bite-size handlers that each perform one aspect of request verification. These can -be composed together and registered with a ghttp server. The result is an expressive language for describing -the requests generated by the client under test. - -Here's a simple example, note that the server handler is only defined in one BeforeEach and then modified, as required, by the nested BeforeEaches. -A more comprehensive example is available at https://onsi.github.io/gomega/#_testing_http_clients - - var _ = Describe("A Sprockets Client", func() { - var server *ghttp.Server - var client *SprocketClient - BeforeEach(func() { - server = ghttp.NewServer() - client = NewSprocketClient(server.URL(), "skywalker", "tk427") - }) - - AfterEach(func() { - server.Close() - }) - - Describe("fetching sprockets", func() { - var statusCode int - var sprockets []Sprocket - BeforeEach(func() { - statusCode = http.StatusOK - sprockets = []Sprocket{} - server.AppendHandlers(ghttp.CombineHandlers( - ghttp.VerifyRequest("GET", "/sprockets"), - ghttp.VerifyBasicAuth("skywalker", "tk427"), - ghttp.RespondWithJSONEncodedPtr(&statusCode, &sprockets), - )) - }) - - Context("when requesting all sprockets", func() { - Context("when the response is succesful", func() { - BeforeEach(func() { - sprockets = []Sprocket{ - NewSprocket("Alfalfa"), - NewSprocket("Banana"), - } - }) - - It("should return the returned sprockets", func() { - Ω(client.Sprockets()).Should(Equal(sprockets)) - }) - }) - - Context("when the response is missing", func() { - BeforeEach(func() { - statusCode = http.StatusNotFound - }) - - It("should return an empty list of sprockets", func() { - Ω(client.Sprockets()).Should(BeEmpty()) - }) - }) - - Context("when the response fails to authenticate", func() { - BeforeEach(func() { - statusCode = http.StatusUnauthorized - }) - - It("should return an AuthenticationError error", func() { - sprockets, err := client.Sprockets() - Ω(sprockets).Should(BeEmpty()) - Ω(err).Should(MatchError(AuthenticationError)) - }) - }) - - Context("when the response is a server failure", func() { - BeforeEach(func() { - statusCode = http.StatusInternalServerError - }) - - It("should return an InternalError error", func() { - sprockets, err := client.Sprockets() - Ω(sprockets).Should(BeEmpty()) - Ω(err).Should(MatchError(InternalError)) - }) - }) - }) - - Context("when requesting some sprockets", func() { - BeforeEach(func() { - sprockets = []Sprocket{ - NewSprocket("Alfalfa"), - NewSprocket("Banana"), - } - - server.WrapHandler(0, ghttp.VerifyRequest("GET", "/sprockets", "filter=FOOD")) - }) - - It("should make the request with a filter", func() { - Ω(client.Sprockets("food")).Should(Equal(sprockets)) - }) - }) - }) - }) -*/ -package ghttp - -import ( - "fmt" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "reflect" - "regexp" - "strings" - "sync" - - . "github.com/onsi/gomega" -) - -func new() *Server { - return &Server{ - AllowUnhandledRequests: false, - UnhandledRequestStatusCode: http.StatusInternalServerError, - writeLock: &sync.Mutex{}, - } -} - -type routedHandler struct { - method string - pathRegexp *regexp.Regexp - path string - handler http.HandlerFunc -} - -// NewServer returns a new `*ghttp.Server` that wraps an `httptest` server. The server is started automatically. -func NewServer() *Server { - s := new() - s.HTTPTestServer = httptest.NewServer(s) - return s -} - -// NewUnstartedServer return a new, unstarted, `*ghttp.Server`. Useful for specifying a custom listener on `server.HTTPTestServer`. -func NewUnstartedServer() *Server { - s := new() - s.HTTPTestServer = httptest.NewUnstartedServer(s) - return s -} - -// NewTLSServer returns a new `*ghttp.Server` that wraps an `httptest` TLS server. The server is started automatically. -func NewTLSServer() *Server { - s := new() - s.HTTPTestServer = httptest.NewTLSServer(s) - return s -} - -type Server struct { - //The underlying httptest server - HTTPTestServer *httptest.Server - - //Defaults to false. If set to true, the Server will allow more requests than there are registered handlers. - AllowUnhandledRequests bool - - //The status code returned when receiving an unhandled request. - //Defaults to http.StatusInternalServerError. - //Only applies if AllowUnhandledRequests is true - UnhandledRequestStatusCode int - - //If provided, ghttp will log about each request received to the provided io.Writer - //Defaults to nil - //If you're using Ginkgo, set this to GinkgoWriter to get improved output during failures - Writer io.Writer - - receivedRequests []*http.Request - requestHandlers []http.HandlerFunc - routedHandlers []routedHandler - - writeLock *sync.Mutex - calls int -} - -//Start() starts an unstarted ghttp server. It is a catastrophic error to call Start more than once (thanks, httptest). -func (s *Server) Start() { - s.HTTPTestServer.Start() -} - -//URL() returns a url that will hit the server -func (s *Server) URL() string { - return s.HTTPTestServer.URL -} - -//Addr() returns the address on which the server is listening. -func (s *Server) Addr() string { - return s.HTTPTestServer.Listener.Addr().String() -} - -//Close() should be called at the end of each test. It spins down and cleans up the test server. -func (s *Server) Close() { - s.writeLock.Lock() - defer s.writeLock.Unlock() - - server := s.HTTPTestServer - s.HTTPTestServer = nil - server.Close() -} - -//ServeHTTP() makes Server an http.Handler -//When the server receives a request it handles the request in the following order: -// -//1. If the request matches a handler registered with RouteToHandler, that handler is called. -//2. Otherwise, if there are handlers registered via AppendHandlers, those handlers are called in order. -//3. If all registered handlers have been called then: -// a) If AllowUnhandledRequests is true, the request will be handled with response code of UnhandledRequestStatusCode -// b) If AllowUnhandledRequests is false, the request will not be handled and the current test will be marked as failed. -func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { - s.writeLock.Lock() - defer func() { - e := recover() - if e != nil { - w.WriteHeader(http.StatusInternalServerError) - } - - //If the handler panics GHTTP will silently succeed. This is bad™. - //To catch this case we need to fail the test if the handler has panicked. - //However, if the handler is panicking because Ginkgo's causing it to panic (i.e. an asswertion failed) - //then we shouldn't double-report the error as this will confuse people. - - //So: step 1, if this is a Ginkgo panic - do nothing, Ginkgo's aware of the failure - eAsString, ok := e.(string) - if ok && strings.Contains(eAsString, "defer GinkgoRecover()") { - return - } - - //If we're here, we have to do step 2: assert that the error is nil. This assertion will - //allow us to fail the test suite (note: we can't call Fail since Gomega is not allowed to import Ginkgo). - //Since a failed assertion throws a panic, and we are likely in a goroutine, we need to defer within our defer! - defer func() { - recover() - }() - Ω(e).Should(BeNil(), "Handler Panicked") - }() - - if s.Writer != nil { - s.Writer.Write([]byte(fmt.Sprintf("GHTTP Received Request: %s - %s\n", req.Method, req.URL))) - } - - s.receivedRequests = append(s.receivedRequests, req) - if routedHandler, ok := s.handlerForRoute(req.Method, req.URL.Path); ok { - s.writeLock.Unlock() - routedHandler(w, req) - } else if s.calls < len(s.requestHandlers) { - h := s.requestHandlers[s.calls] - s.calls++ - s.writeLock.Unlock() - h(w, req) - } else { - s.writeLock.Unlock() - if s.AllowUnhandledRequests { - ioutil.ReadAll(req.Body) - req.Body.Close() - w.WriteHeader(s.UnhandledRequestStatusCode) - } else { - Ω(req).Should(BeNil(), "Received Unhandled Request") - } - } -} - -//ReceivedRequests is an array containing all requests received by the server (both handled and unhandled requests) -func (s *Server) ReceivedRequests() []*http.Request { - s.writeLock.Lock() - defer s.writeLock.Unlock() - - return s.receivedRequests -} - -//RouteToHandler can be used to register handlers that will always handle requests that match -//the passed in method and path. -// -//The path may be either a string object or a *regexp.Regexp. -func (s *Server) RouteToHandler(method string, path interface{}, handler http.HandlerFunc) { - s.writeLock.Lock() - defer s.writeLock.Unlock() - - rh := routedHandler{ - method: method, - handler: handler, - } - - switch p := path.(type) { - case *regexp.Regexp: - rh.pathRegexp = p - case string: - rh.path = p - default: - panic("path must be a string or a regular expression") - } - - for i, existingRH := range s.routedHandlers { - if existingRH.method == method && - reflect.DeepEqual(existingRH.pathRegexp, rh.pathRegexp) && - existingRH.path == rh.path { - s.routedHandlers[i] = rh - return - } - } - s.routedHandlers = append(s.routedHandlers, rh) -} - -func (s *Server) handlerForRoute(method string, path string) (http.HandlerFunc, bool) { - for _, rh := range s.routedHandlers { - if rh.method == method { - if rh.pathRegexp != nil { - if rh.pathRegexp.Match([]byte(path)) { - return rh.handler, true - } - } else if rh.path == path { - return rh.handler, true - } - } - } - - return nil, false -} - -//AppendHandlers will appends http.HandlerFuncs to the server's list of registered handlers. The first incoming request is handled by the first handler, the second by the second, etc... -func (s *Server) AppendHandlers(handlers ...http.HandlerFunc) { - s.writeLock.Lock() - defer s.writeLock.Unlock() - - s.requestHandlers = append(s.requestHandlers, handlers...) -} - -//SetHandler overrides the registered handler at the passed in index with the passed in handler -//This is useful, for example, when a server has been set up in a shared context, but must be tweaked -//for a particular test. -func (s *Server) SetHandler(index int, handler http.HandlerFunc) { - s.writeLock.Lock() - defer s.writeLock.Unlock() - - s.requestHandlers[index] = handler -} - -//GetHandler returns the handler registered at the passed in index. -func (s *Server) GetHandler(index int) http.HandlerFunc { - s.writeLock.Lock() - defer s.writeLock.Unlock() - - return s.requestHandlers[index] -} - -//WrapHandler combines the passed in handler with the handler registered at the passed in index. -//This is useful, for example, when a server has been set up in a shared context but must be tweaked -//for a particular test. -// -//If the currently registered handler is A, and the new passed in handler is B then -//WrapHandler will generate a new handler that first calls A, then calls B, and assign it to index -func (s *Server) WrapHandler(index int, handler http.HandlerFunc) { - existingHandler := s.GetHandler(index) - s.SetHandler(index, CombineHandlers(existingHandler, handler)) -} - -func (s *Server) CloseClientConnections() { - s.writeLock.Lock() - defer s.writeLock.Unlock() - - s.HTTPTestServer.CloseClientConnections() -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/gomega_dsl.go b/Godeps/_workspace/src/github.com/onsi/gomega/gomega_dsl.go deleted file mode 100644 index 78bd188..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/gomega_dsl.go +++ /dev/null @@ -1,335 +0,0 @@ -/* -Gomega is the Ginkgo BDD-style testing framework's preferred matcher library. - -The godoc documentation describes Gomega's API. More comprehensive documentation (with examples!) is available at http://onsi.github.io/gomega/ - -Gomega on Github: http://github.com/onsi/gomega - -Learn more about Ginkgo online: http://onsi.github.io/ginkgo - -Ginkgo on Github: http://github.com/onsi/ginkgo - -Gomega is MIT-Licensed -*/ -package gomega - -import ( - "fmt" - "reflect" - "time" - - "github.com/onsi/gomega/internal/assertion" - "github.com/onsi/gomega/internal/asyncassertion" - "github.com/onsi/gomega/internal/testingtsupport" - "github.com/onsi/gomega/types" -) - -const GOMEGA_VERSION = "1.0" - -const nilFailHandlerPanic = `You are trying to make an assertion, but Gomega's fail handler is nil. -If you're using Ginkgo then you probably forgot to put your assertion in an It(). -Alternatively, you may have forgotten to register a fail handler with RegisterFailHandler() or RegisterTestingT(). -` - -var globalFailHandler types.GomegaFailHandler - -var defaultEventuallyTimeout = time.Second -var defaultEventuallyPollingInterval = 10 * time.Millisecond -var defaultConsistentlyDuration = 100 * time.Millisecond -var defaultConsistentlyPollingInterval = 10 * time.Millisecond - -//RegisterFailHandler connects Ginkgo to Gomega. When a matcher fails -//the fail handler passed into RegisterFailHandler is called. -func RegisterFailHandler(handler types.GomegaFailHandler) { - globalFailHandler = handler -} - -//RegisterTestingT connects Gomega to Golang's XUnit style -//Testing.T tests. You'll need to call this at the top of each XUnit style test: -// -// func TestFarmHasCow(t *testing.T) { -// RegisterTestingT(t) -// -// f := farm.New([]string{"Cow", "Horse"}) -// Expect(f.HasCow()).To(BeTrue(), "Farm should have cow") -// } -// -// Note that this *testing.T is registered *globally* by Gomega (this is why you don't have to -// pass `t` down to the matcher itself). This means that you cannot run the XUnit style tests -// in parallel as the global fail handler cannot point to more than one testing.T at a time. -// -// (As an aside: Ginkgo gets around this limitation by running parallel tests in different *processes*). -func RegisterTestingT(t types.GomegaTestingT) { - RegisterFailHandler(testingtsupport.BuildTestingTGomegaFailHandler(t)) -} - -//InterceptGomegaHandlers runs a given callback and returns an array of -//failure messages generated by any Gomega assertions within the callback. -// -//This is accomplished by temporarily replacing the *global* fail handler -//with a fail handler that simply annotates failures. The original fail handler -//is reset when InterceptGomegaFailures returns. -// -//This is most useful when testing custom matchers, but can also be used to check -//on a value using a Gomega assertion without causing a test failure. -func InterceptGomegaFailures(f func()) []string { - originalHandler := globalFailHandler - failures := []string{} - RegisterFailHandler(func(message string, callerSkip ...int) { - failures = append(failures, message) - }) - f() - RegisterFailHandler(originalHandler) - return failures -} - -//Ω wraps an actual value allowing assertions to be made on it: -// Ω("foo").Should(Equal("foo")) -// -//If Ω is passed more than one argument it will pass the *first* argument to the matcher. -//All subsequent arguments will be required to be nil/zero. -// -//This is convenient if you want to make an assertion on a method/function that returns -//a value and an error - a common patter in Go. -// -//For example, given a function with signature: -// func MyAmazingThing() (int, error) -// -//Then: -// Ω(MyAmazingThing()).Should(Equal(3)) -//Will succeed only if `MyAmazingThing()` returns `(3, nil)` -// -//Ω and Expect are identical -func Ω(actual interface{}, extra ...interface{}) GomegaAssertion { - return ExpectWithOffset(0, actual, extra...) -} - -//Expect wraps an actual value allowing assertions to be made on it: -// Expect("foo").To(Equal("foo")) -// -//If Expect is passed more than one argument it will pass the *first* argument to the matcher. -//All subsequent arguments will be required to be nil/zero. -// -//This is convenient if you want to make an assertion on a method/function that returns -//a value and an error - a common patter in Go. -// -//For example, given a function with signature: -// func MyAmazingThing() (int, error) -// -//Then: -// Expect(MyAmazingThing()).Should(Equal(3)) -//Will succeed only if `MyAmazingThing()` returns `(3, nil)` -// -//Expect and Ω are identical -func Expect(actual interface{}, extra ...interface{}) GomegaAssertion { - return ExpectWithOffset(0, actual, extra...) -} - -//ExpectWithOffset wraps an actual value allowing assertions to be made on it: -// ExpectWithOffset(1, "foo").To(Equal("foo")) -// -//Unlike `Expect` and `Ω`, `ExpectWithOffset` takes an additional integer argument -//this is used to modify the call-stack offset when computing line numbers. -// -//This is most useful in helper functions that make assertions. If you want Gomega's -//error message to refer to the calling line in the test (as opposed to the line in the helper function) -//set the first argument of `ExpectWithOffset` appropriately. -func ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) GomegaAssertion { - if globalFailHandler == nil { - panic(nilFailHandlerPanic) - } - return assertion.New(actual, globalFailHandler, offset, extra...) -} - -//Eventually wraps an actual value allowing assertions to be made on it. -//The assertion is tried periodically until it passes or a timeout occurs. -// -//Both the timeout and polling interval are configurable as optional arguments: -//The first optional argument is the timeout -//The second optional argument is the polling interval -// -//Both intervals can either be specified as time.Duration, parsable duration strings or as floats/integers. In the -//last case they are interpreted as seconds. -// -//If Eventually is passed an actual that is a function taking no arguments and returning at least one value, -//then Eventually will call the function periodically and try the matcher against the function's first return value. -// -//Example: -// -// Eventually(func() int { -// return thingImPolling.Count() -// }).Should(BeNumerically(">=", 17)) -// -//Note that this example could be rewritten: -// -// Eventually(thingImPolling.Count).Should(BeNumerically(">=", 17)) -// -//If the function returns more than one value, then Eventually will pass the first value to the matcher and -//assert that all other values are nil/zero. -//This allows you to pass Eventually a function that returns a value and an error - a common pattern in Go. -// -//For example, consider a method that returns a value and an error: -// func FetchFromDB() (string, error) -// -//Then -// Eventually(FetchFromDB).Should(Equal("hasselhoff")) -// -//Will pass only if the the returned error is nil and the returned string passes the matcher. -// -//Eventually's default timeout is 1 second, and its default polling interval is 10ms -func Eventually(actual interface{}, intervals ...interface{}) GomegaAsyncAssertion { - return EventuallyWithOffset(0, actual, intervals...) -} - -//EventuallyWithOffset operates like Eventually but takes an additional -//initial argument to indicate an offset in the call stack. This is useful when building helper -//functions that contain matchers. To learn more, read about `ExpectWithOffset`. -func EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) GomegaAsyncAssertion { - if globalFailHandler == nil { - panic(nilFailHandlerPanic) - } - timeoutInterval := defaultEventuallyTimeout - pollingInterval := defaultEventuallyPollingInterval - if len(intervals) > 0 { - timeoutInterval = toDuration(intervals[0]) - } - if len(intervals) > 1 { - pollingInterval = toDuration(intervals[1]) - } - return asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, actual, globalFailHandler, timeoutInterval, pollingInterval, offset) -} - -//Consistently wraps an actual value allowing assertions to be made on it. -//The assertion is tried periodically and is required to pass for a period of time. -// -//Both the total time and polling interval are configurable as optional arguments: -//The first optional argument is the duration that Consistently will run for -//The second optional argument is the polling interval -// -//Both intervals can either be specified as time.Duration, parsable duration strings or as floats/integers. In the -//last case they are interpreted as seconds. -// -//If Consistently is passed an actual that is a function taking no arguments and returning at least one value, -//then Consistently will call the function periodically and try the matcher against the function's first return value. -// -//If the function returns more than one value, then Consistently will pass the first value to the matcher and -//assert that all other values are nil/zero. -//This allows you to pass Consistently a function that returns a value and an error - a common pattern in Go. -// -//Consistently is useful in cases where you want to assert that something *does not happen* over a period of tiem. -//For example, you want to assert that a goroutine does *not* send data down a channel. In this case, you could: -// -// Consistently(channel).ShouldNot(Receive()) -// -//Consistently's default duration is 100ms, and its default polling interval is 10ms -func Consistently(actual interface{}, intervals ...interface{}) GomegaAsyncAssertion { - return ConsistentlyWithOffset(0, actual, intervals...) -} - -//ConsistentlyWithOffset operates like Consistnetly but takes an additional -//initial argument to indicate an offset in the call stack. This is useful when building helper -//functions that contain matchers. To learn more, read about `ExpectWithOffset`. -func ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) GomegaAsyncAssertion { - if globalFailHandler == nil { - panic(nilFailHandlerPanic) - } - timeoutInterval := defaultConsistentlyDuration - pollingInterval := defaultConsistentlyPollingInterval - if len(intervals) > 0 { - timeoutInterval = toDuration(intervals[0]) - } - if len(intervals) > 1 { - pollingInterval = toDuration(intervals[1]) - } - return asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, actual, globalFailHandler, timeoutInterval, pollingInterval, offset) -} - -//Set the default timeout duration for Eventually. Eventually will repeatedly poll your condition until it succeeds, or until this timeout elapses. -func SetDefaultEventuallyTimeout(t time.Duration) { - defaultEventuallyTimeout = t -} - -//Set the default polling interval for Eventually. -func SetDefaultEventuallyPollingInterval(t time.Duration) { - defaultEventuallyPollingInterval = t -} - -//Set the default duration for Consistently. Consistently will verify that your condition is satsified for this long. -func SetDefaultConsistentlyDuration(t time.Duration) { - defaultConsistentlyDuration = t -} - -//Set the default polling interval for Consistently. -func SetDefaultConsistentlyPollingInterval(t time.Duration) { - defaultConsistentlyPollingInterval = t -} - -//GomegaAsyncAssertion is returned by Eventually and Consistently and polls the actual value passed into Eventually against -//the matcher passed to the Should and ShouldNot methods. -// -//Both Should and ShouldNot take a variadic optionalDescription argument. This is passed on to -//fmt.Sprintf() and is used to annotate failure messages. This allows you to make your failure messages more -//descriptive -// -//Both Should and ShouldNot return a boolean that is true if the assertion passed and false if it failed. -// -//Example: -// -// Eventually(myChannel).Should(Receive(), "Something should have come down the pipe.") -// Consistently(myChannel).ShouldNot(Receive(), "Nothing should have come down the pipe.") -type GomegaAsyncAssertion interface { - Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool - ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool -} - -//GomegaAssertion is returned by Ω and Expect and compares the actual value to the matcher -//passed to the Should/ShouldNot and To/ToNot/NotTo methods. -// -//Typically Should/ShouldNot are used with Ω and To/ToNot/NotTo are used with Expect -//though this is not enforced. -// -//All methods take a variadic optionalDescription argument. This is passed on to fmt.Sprintf() -//and is used to annotate failure messages. -// -//All methods return a bool that is true if hte assertion passed and false if it failed. -// -//Example: -// -// Ω(farm.HasCow()).Should(BeTrue(), "Farm %v should have a cow", farm) -type GomegaAssertion interface { - Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool - ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool - - To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool - ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool - NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool -} - -//OmegaMatcher is deprecated in favor of the better-named and better-organized types.GomegaMatcher but sticks around to support existing code that uses it -type OmegaMatcher types.GomegaMatcher - -func toDuration(input interface{}) time.Duration { - duration, ok := input.(time.Duration) - if ok { - return duration - } - - value := reflect.ValueOf(input) - kind := reflect.TypeOf(input).Kind() - - if reflect.Int <= kind && kind <= reflect.Int64 { - return time.Duration(value.Int()) * time.Second - } else if reflect.Uint <= kind && kind <= reflect.Uint64 { - return time.Duration(value.Uint()) * time.Second - } else if reflect.Float32 <= kind && kind <= reflect.Float64 { - return time.Duration(value.Float() * float64(time.Second)) - } else if reflect.String == kind { - duration, err := time.ParseDuration(value.String()) - if err != nil { - panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input)) - } - return duration - } - - panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input)) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/internal/assertion/assertion.go b/Godeps/_workspace/src/github.com/onsi/gomega/internal/assertion/assertion.go deleted file mode 100644 index b73673f..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/internal/assertion/assertion.go +++ /dev/null @@ -1,98 +0,0 @@ -package assertion - -import ( - "fmt" - "reflect" - - "github.com/onsi/gomega/types" -) - -type Assertion struct { - actualInput interface{} - fail types.GomegaFailHandler - offset int - extra []interface{} -} - -func New(actualInput interface{}, fail types.GomegaFailHandler, offset int, extra ...interface{}) *Assertion { - return &Assertion{ - actualInput: actualInput, - fail: fail, - offset: offset, - extra: extra, - } -} - -func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...) -} - -func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) -} - -func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...) -} - -func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) -} - -func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) -} - -func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string { - switch len(optionalDescription) { - case 0: - return "" - default: - return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" - } -} - -func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { - matches, err := matcher.Match(assertion.actualInput) - description := assertion.buildDescription(optionalDescription...) - if err != nil { - assertion.fail(description+err.Error(), 2+assertion.offset) - return false - } - if matches != desiredMatch { - var message string - if desiredMatch { - message = matcher.FailureMessage(assertion.actualInput) - } else { - message = matcher.NegatedFailureMessage(assertion.actualInput) - } - assertion.fail(description+message, 2+assertion.offset) - return false - } - - return true -} - -func (assertion *Assertion) vetExtras(optionalDescription ...interface{}) bool { - success, message := vetExtras(assertion.extra) - if success { - return true - } - - description := assertion.buildDescription(optionalDescription...) - assertion.fail(description+message, 2+assertion.offset) - return false -} - -func vetExtras(extras []interface{}) (bool, string) { - for i, extra := range extras { - if extra != nil { - zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface() - if !reflect.DeepEqual(zeroValue, extra) { - message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra) - return false, message - } - } - } - return true, "" -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go b/Godeps/_workspace/src/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go deleted file mode 100644 index bce0853..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go +++ /dev/null @@ -1,189 +0,0 @@ -package asyncassertion - -import ( - "errors" - "fmt" - "reflect" - "time" - - "github.com/onsi/gomega/internal/oraclematcher" - "github.com/onsi/gomega/types" -) - -type AsyncAssertionType uint - -const ( - AsyncAssertionTypeEventually AsyncAssertionType = iota - AsyncAssertionTypeConsistently -) - -type AsyncAssertion struct { - asyncType AsyncAssertionType - actualInput interface{} - timeoutInterval time.Duration - pollingInterval time.Duration - fail types.GomegaFailHandler - offset int -} - -func New(asyncType AsyncAssertionType, actualInput interface{}, fail types.GomegaFailHandler, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion { - actualType := reflect.TypeOf(actualInput) - if actualType.Kind() == reflect.Func { - if actualType.NumIn() != 0 || actualType.NumOut() == 0 { - panic("Expected a function with no arguments and one or more return values.") - } - } - - return &AsyncAssertion{ - asyncType: asyncType, - actualInput: actualInput, - fail: fail, - timeoutInterval: timeoutInterval, - pollingInterval: pollingInterval, - offset: offset, - } -} - -func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - return assertion.match(matcher, true, optionalDescription...) -} - -func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - return assertion.match(matcher, false, optionalDescription...) -} - -func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string { - switch len(optionalDescription) { - case 0: - return "" - default: - return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" - } -} - -func (assertion *AsyncAssertion) actualInputIsAFunction() bool { - actualType := reflect.TypeOf(assertion.actualInput) - return actualType.Kind() == reflect.Func && actualType.NumIn() == 0 && actualType.NumOut() > 0 -} - -func (assertion *AsyncAssertion) pollActual() (interface{}, error) { - if assertion.actualInputIsAFunction() { - values := reflect.ValueOf(assertion.actualInput).Call([]reflect.Value{}) - - extras := []interface{}{} - for _, value := range values[1:] { - extras = append(extras, value.Interface()) - } - - success, message := vetExtras(extras) - - if !success { - return nil, errors.New(message) - } - - return values[0].Interface(), nil - } - - return assertion.actualInput, nil -} - -func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool { - if assertion.actualInputIsAFunction() { - return true - } - - return oraclematcher.MatchMayChangeInTheFuture(matcher, value) -} - -func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { - timer := time.Now() - timeout := time.After(assertion.timeoutInterval) - - description := assertion.buildDescription(optionalDescription...) - - var matches bool - var err error - mayChange := true - value, err := assertion.pollActual() - if err == nil { - mayChange = assertion.matcherMayChange(matcher, value) - matches, err = matcher.Match(value) - } - - fail := func(preamble string) { - errMsg := "" - message := "" - if err != nil { - errMsg = "Error: " + err.Error() - } else { - if desiredMatch { - message = matcher.FailureMessage(value) - } else { - message = matcher.NegatedFailureMessage(value) - } - } - assertion.fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset) - } - - if assertion.asyncType == AsyncAssertionTypeEventually { - for { - if err == nil && matches == desiredMatch { - return true - } - - if !mayChange { - fail("No future change is possible. Bailing out early") - return false - } - - select { - case <-time.After(assertion.pollingInterval): - value, err = assertion.pollActual() - if err == nil { - mayChange = assertion.matcherMayChange(matcher, value) - matches, err = matcher.Match(value) - } - case <-timeout: - fail("Timed out") - return false - } - } - } else if assertion.asyncType == AsyncAssertionTypeConsistently { - for { - if !(err == nil && matches == desiredMatch) { - fail("Failed") - return false - } - - if !mayChange { - return true - } - - select { - case <-time.After(assertion.pollingInterval): - value, err = assertion.pollActual() - if err == nil { - mayChange = assertion.matcherMayChange(matcher, value) - matches, err = matcher.Match(value) - } - case <-timeout: - return true - } - } - } - - return false -} - -func vetExtras(extras []interface{}) (bool, string) { - for i, extra := range extras { - if extra != nil { - zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface() - if !reflect.DeepEqual(zeroValue, extra) { - message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra) - return false, message - } - } - } - return true, "" -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/internal/fakematcher/fake_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/internal/fakematcher/fake_matcher.go deleted file mode 100644 index 6e351a7..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/internal/fakematcher/fake_matcher.go +++ /dev/null @@ -1,23 +0,0 @@ -package fakematcher - -import "fmt" - -type FakeMatcher struct { - ReceivedActual interface{} - MatchesToReturn bool - ErrToReturn error -} - -func (matcher *FakeMatcher) Match(actual interface{}) (bool, error) { - matcher.ReceivedActual = actual - - return matcher.MatchesToReturn, matcher.ErrToReturn -} - -func (matcher *FakeMatcher) FailureMessage(actual interface{}) string { - return fmt.Sprintf("positive: %v", actual) -} - -func (matcher *FakeMatcher) NegatedFailureMessage(actual interface{}) string { - return fmt.Sprintf("negative: %v", actual) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/internal/oraclematcher/oracle_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/internal/oraclematcher/oracle_matcher.go deleted file mode 100644 index 66cad88..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/internal/oraclematcher/oracle_matcher.go +++ /dev/null @@ -1,25 +0,0 @@ -package oraclematcher - -import "github.com/onsi/gomega/types" - -/* -GomegaMatchers that also match the OracleMatcher interface can convey information about -whether or not their result will change upon future attempts. - -This allows `Eventually` and `Consistently` to short circuit if success becomes impossible. - -For example, a process' exit code can never change. So, gexec's Exit matcher returns `true` -for `MatchMayChangeInTheFuture` until the process exits, at which point it returns `false` forevermore. -*/ -type OracleMatcher interface { - MatchMayChangeInTheFuture(actual interface{}) bool -} - -func MatchMayChangeInTheFuture(matcher types.GomegaMatcher, value interface{}) bool { - oracleMatcher, ok := matcher.(OracleMatcher) - if !ok { - return true - } - - return oracleMatcher.MatchMayChangeInTheFuture(value) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go b/Godeps/_workspace/src/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go deleted file mode 100644 index 7871fd4..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go +++ /dev/null @@ -1,40 +0,0 @@ -package testingtsupport - -import ( - "regexp" - "runtime/debug" - "strings" - - "github.com/onsi/gomega/types" -) - -type gomegaTestingT interface { - Errorf(format string, args ...interface{}) -} - -func BuildTestingTGomegaFailHandler(t gomegaTestingT) types.GomegaFailHandler { - return func(message string, callerSkip ...int) { - skip := 1 - if len(callerSkip) > 0 { - skip = callerSkip[0] - } - stackTrace := pruneStack(string(debug.Stack()), skip) - t.Errorf("\n%s\n%s", stackTrace, message) - } -} - -func pruneStack(fullStackTrace string, skip int) string { - stack := strings.Split(fullStackTrace, "\n") - if len(stack) > 2*(skip+1) { - stack = stack[2*(skip+1):] - } - prunedStack := []string{} - re := regexp.MustCompile(`\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`) - for i := 0; i < len(stack)/2; i++ { - if !re.Match([]byte(stack[i*2])) { - prunedStack = append(prunedStack, stack[i*2]) - prunedStack = append(prunedStack, stack[i*2+1]) - } - } - return strings.Join(prunedStack, "\n") -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers.go deleted file mode 100644 index b6110c4..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers.go +++ /dev/null @@ -1,393 +0,0 @@ -package gomega - -import ( - "time" - - "github.com/onsi/gomega/matchers" - "github.com/onsi/gomega/types" -) - -//Equal uses reflect.DeepEqual to compare actual with expected. Equal is strict about -//types when performing comparisons. -//It is an error for both actual and expected to be nil. Use BeNil() instead. -func Equal(expected interface{}) types.GomegaMatcher { - return &matchers.EqualMatcher{ - Expected: expected, - } -} - -//BeEquivalentTo is more lax than Equal, allowing equality between different types. -//This is done by converting actual to have the type of expected before -//attempting equality with reflect.DeepEqual. -//It is an error for actual and expected to be nil. Use BeNil() instead. -func BeEquivalentTo(expected interface{}) types.GomegaMatcher { - return &matchers.BeEquivalentToMatcher{ - Expected: expected, - } -} - -//BeNil succeeds if actual is nil -func BeNil() types.GomegaMatcher { - return &matchers.BeNilMatcher{} -} - -//BeTrue succeeds if actual is true -func BeTrue() types.GomegaMatcher { - return &matchers.BeTrueMatcher{} -} - -//BeFalse succeeds if actual is false -func BeFalse() types.GomegaMatcher { - return &matchers.BeFalseMatcher{} -} - -//HaveOccurred succeeds if actual is a non-nil error -//The typical Go error checking pattern looks like: -// err := SomethingThatMightFail() -// Ω(err).ShouldNot(HaveOccurred()) -func HaveOccurred() types.GomegaMatcher { - return &matchers.HaveOccurredMatcher{} -} - -//Succeed passes if actual is a nil error -//Succeed is intended to be used with functions that return a single error value. Instead of -// err := SomethingThatMightFail() -// Ω(err).ShouldNot(HaveOccurred()) -// -//You can write: -// Ω(SomethingThatMightFail()).Should(Succeed()) -// -//It is a mistake to use Succeed with a function that has multiple return values. Gomega's Ω and Expect -//functions automatically trigger failure if any return values after the first return value are non-zero/non-nil. -//This means that Ω(MultiReturnFunc()).ShouldNot(Succeed()) can never pass. -func Succeed() types.GomegaMatcher { - return &matchers.SucceedMatcher{} -} - -//MatchError succeeds if actual is a non-nil error that matches the passed in string/error. -// -//These are valid use-cases: -// Ω(err).Should(MatchError("an error")) //asserts that err.Error() == "an error" -// Ω(err).Should(MatchError(SomeError)) //asserts that err == SomeError (via reflect.DeepEqual) -// -//It is an error for err to be nil or an object that does not implement the Error interface -func MatchError(expected interface{}) types.GomegaMatcher { - return &matchers.MatchErrorMatcher{ - Expected: expected, - } -} - -//BeClosed succeeds if actual is a closed channel. -//It is an error to pass a non-channel to BeClosed, it is also an error to pass nil -// -//In order to check whether or not the channel is closed, Gomega must try to read from the channel -//(even in the `ShouldNot(BeClosed())` case). You should keep this in mind if you wish to make subsequent assertions about -//values coming down the channel. -// -//Also, if you are testing that a *buffered* channel is closed you must first read all values out of the channel before -//asserting that it is closed (it is not possible to detect that a buffered-channel has been closed until all its buffered values are read). -// -//Finally, as a corollary: it is an error to check whether or not a send-only channel is closed. -func BeClosed() types.GomegaMatcher { - return &matchers.BeClosedMatcher{} -} - -//Receive succeeds if there is a value to be received on actual. -//Actual must be a channel (and cannot be a send-only channel) -- anything else is an error. -// -//Receive returns immediately and never blocks: -// -//- If there is nothing on the channel `c` then Ω(c).Should(Receive()) will fail and Ω(c).ShouldNot(Receive()) will pass. -// -//- If the channel `c` is closed then Ω(c).Should(Receive()) will fail and Ω(c).ShouldNot(Receive()) will pass. -// -//- If there is something on the channel `c` ready to be read, then Ω(c).Should(Receive()) will pass and Ω(c).ShouldNot(Receive()) will fail. -// -//If you have a go-routine running in the background that will write to channel `c` you can: -// Eventually(c).Should(Receive()) -// -//This will timeout if nothing gets sent to `c` (you can modify the timeout interval as you normally do with `Eventually`) -// -//A similar use-case is to assert that no go-routine writes to a channel (for a period of time). You can do this with `Consistently`: -// Consistently(c).ShouldNot(Receive()) -// -//You can pass `Receive` a matcher. If you do so, it will match the received object against the matcher. For example: -// Ω(c).Should(Receive(Equal("foo"))) -// -//When given a matcher, `Receive` will always fail if there is nothing to be received on the channel. -// -//Passing Receive a matcher is especially useful when paired with Eventually: -// -// Eventually(c).Should(Receive(ContainSubstring("bar"))) -// -//will repeatedly attempt to pull values out of `c` until a value matching "bar" is received. -// -//Finally, if you want to have a reference to the value *sent* to the channel you can pass the `Receive` matcher a pointer to a variable of the appropriate type: -// var myThing thing -// Eventually(thingChan).Should(Receive(&myThing)) -// Ω(myThing.Sprocket).Should(Equal("foo")) -// Ω(myThing.IsValid()).Should(BeTrue()) -func Receive(args ...interface{}) types.GomegaMatcher { - var arg interface{} - if len(args) > 0 { - arg = args[0] - } - - return &matchers.ReceiveMatcher{ - Arg: arg, - } -} - -//BeSent succeeds if a value can be sent to actual. -//Actual must be a channel (and cannot be a receive-only channel) that can sent the type of the value passed into BeSent -- anything else is an error. -//In addition, actual must not be closed. -// -//BeSent never blocks: -// -//- If the channel `c` is not ready to receive then Ω(c).Should(BeSent("foo")) will fail immediately -//- If the channel `c` is eventually ready to receive then Eventually(c).Should(BeSent("foo")) will succeed.. presuming the channel becomes ready to receive before Eventually's timeout -//- If the channel `c` is closed then Ω(c).Should(BeSent("foo")) and Ω(c).ShouldNot(BeSent("foo")) will both fail immediately -// -//Of course, the value is actually sent to the channel. The point of `BeSent` is less to make an assertion about the availability of the channel (which is typically an implementation detail that your test should not be concerned with). -//Rather, the point of `BeSent` is to make it possible to easily and expressively write tests that can timeout on blocked channel sends. -func BeSent(arg interface{}) types.GomegaMatcher { - return &matchers.BeSentMatcher{ - Arg: arg, - } -} - -//MatchRegexp succeeds if actual is a string or stringer that matches the -//passed-in regexp. Optional arguments can be provided to construct a regexp -//via fmt.Sprintf(). -func MatchRegexp(regexp string, args ...interface{}) types.GomegaMatcher { - return &matchers.MatchRegexpMatcher{ - Regexp: regexp, - Args: args, - } -} - -//ContainSubstring succeeds if actual is a string or stringer that contains the -//passed-in regexp. Optional arguments can be provided to construct the substring -//via fmt.Sprintf(). -func ContainSubstring(substr string, args ...interface{}) types.GomegaMatcher { - return &matchers.ContainSubstringMatcher{ - Substr: substr, - Args: args, - } -} - -//HavePrefix succeeds if actual is a string or stringer that contains the -//passed-in string as a prefix. Optional arguments can be provided to construct -//via fmt.Sprintf(). -func HavePrefix(prefix string, args ...interface{}) types.GomegaMatcher { - return &matchers.HavePrefixMatcher{ - Prefix: prefix, - Args: args, - } -} - -//HaveSuffix succeeds if actual is a string or stringer that contains the -//passed-in string as a suffix. Optional arguments can be provided to construct -//via fmt.Sprintf(). -func HaveSuffix(suffix string, args ...interface{}) types.GomegaMatcher { - return &matchers.HaveSuffixMatcher{ - Suffix: suffix, - Args: args, - } -} - -//MatchJSON succeeds if actual is a string or stringer of JSON that matches -//the expected JSON. The JSONs are decoded and the resulting objects are compared via -//reflect.DeepEqual so things like key-ordering and whitespace shouldn't matter. -func MatchJSON(json interface{}) types.GomegaMatcher { - return &matchers.MatchJSONMatcher{ - JSONToMatch: json, - } -} - -//BeEmpty succeeds if actual is empty. Actual must be of type string, array, map, chan, or slice. -func BeEmpty() types.GomegaMatcher { - return &matchers.BeEmptyMatcher{} -} - -//HaveLen succeeds if actual has the passed-in length. Actual must be of type string, array, map, chan, or slice. -func HaveLen(count int) types.GomegaMatcher { - return &matchers.HaveLenMatcher{ - Count: count, - } -} - -//BeZero succeeds if actual is the zero value for its type or if actual is nil. -func BeZero() types.GomegaMatcher { - return &matchers.BeZeroMatcher{} -} - -//ContainElement succeeds if actual contains the passed in element. -//By default ContainElement() uses Equal() to perform the match, however a -//matcher can be passed in instead: -// Ω([]string{"Foo", "FooBar"}).Should(ContainElement(ContainSubstring("Bar"))) -// -//Actual must be an array, slice or map. -//For maps, ContainElement searches through the map's values. -func ContainElement(element interface{}) types.GomegaMatcher { - return &matchers.ContainElementMatcher{ - Element: element, - } -} - -//ConsistOf succeeds if actual contains preciely the elements passed into the matcher. The ordering of the elements does not matter. -//By default ConsistOf() uses Equal() to match the elements, however custom matchers can be passed in instead. Here are some examples: -// -// Ω([]string{"Foo", "FooBar"}).Should(ConsistOf("FooBar", "Foo")) -// Ω([]string{"Foo", "FooBar"}).Should(ConsistOf(ContainSubstring("Bar"), "Foo")) -// Ω([]string{"Foo", "FooBar"}).Should(ConsistOf(ContainSubstring("Foo"), ContainSubstring("Foo"))) -// -//Actual must be an array, slice or map. For maps, ConsistOf matches against the map's values. -// -//You typically pass variadic arguments to ConsistOf (as in the examples above). However, if you need to pass in a slice you can provided that it -//is the only element passed in to ConsistOf: -// -// Ω([]string{"Foo", "FooBar"}).Should(ConsistOf([]string{"FooBar", "Foo"})) -// -//Note that Go's type system does not allow you to write this as ConsistOf([]string{"FooBar", "Foo"}...) as []string and []interface{} are different types - hence the need for this special rule. -func ConsistOf(elements ...interface{}) types.GomegaMatcher { - return &matchers.ConsistOfMatcher{ - Elements: elements, - } -} - -//HaveKey succeeds if actual is a map with the passed in key. -//By default HaveKey uses Equal() to perform the match, however a -//matcher can be passed in instead: -// Ω(map[string]string{"Foo": "Bar", "BazFoo": "Duck"}).Should(HaveKey(MatchRegexp(`.+Foo$`))) -func HaveKey(key interface{}) types.GomegaMatcher { - return &matchers.HaveKeyMatcher{ - Key: key, - } -} - -//HaveKeyWithValue succeeds if actual is a map with the passed in key and value. -//By default HaveKeyWithValue uses Equal() to perform the match, however a -//matcher can be passed in instead: -// Ω(map[string]string{"Foo": "Bar", "BazFoo": "Duck"}).Should(HaveKeyWithValue("Foo", "Bar")) -// Ω(map[string]string{"Foo": "Bar", "BazFoo": "Duck"}).Should(HaveKeyWithValue(MatchRegexp(`.+Foo$`), "Bar")) -func HaveKeyWithValue(key interface{}, value interface{}) types.GomegaMatcher { - return &matchers.HaveKeyWithValueMatcher{ - Key: key, - Value: value, - } -} - -//BeNumerically performs numerical assertions in a type-agnostic way. -//Actual and expected should be numbers, though the specific type of -//number is irrelevant (floa32, float64, uint8, etc...). -// -//There are six, self-explanatory, supported comparators: -// Ω(1.0).Should(BeNumerically("==", 1)) -// Ω(1.0).Should(BeNumerically("~", 0.999, 0.01)) -// Ω(1.0).Should(BeNumerically(">", 0.9)) -// Ω(1.0).Should(BeNumerically(">=", 1.0)) -// Ω(1.0).Should(BeNumerically("<", 3)) -// Ω(1.0).Should(BeNumerically("<=", 1.0)) -func BeNumerically(comparator string, compareTo ...interface{}) types.GomegaMatcher { - return &matchers.BeNumericallyMatcher{ - Comparator: comparator, - CompareTo: compareTo, - } -} - -//BeTemporally compares time.Time's like BeNumerically -//Actual and expected must be time.Time. The comparators are the same as for BeNumerically -// Ω(time.Now()).Should(BeTemporally(">", time.Time{})) -// Ω(time.Now()).Should(BeTemporally("~", time.Now(), time.Second)) -func BeTemporally(comparator string, compareTo time.Time, threshold ...time.Duration) types.GomegaMatcher { - return &matchers.BeTemporallyMatcher{ - Comparator: comparator, - CompareTo: compareTo, - Threshold: threshold, - } -} - -//BeAssignableToTypeOf succeeds if actual is assignable to the type of expected. -//It will return an error when one of the values is nil. -// Ω(0).Should(BeAssignableToTypeOf(0)) // Same values -// Ω(5).Should(BeAssignableToTypeOf(-1)) // different values same type -// Ω("foo").Should(BeAssignableToTypeOf("bar")) // different values same type -// Ω(struct{ Foo string }{}).Should(BeAssignableToTypeOf(struct{ Foo string }{})) -func BeAssignableToTypeOf(expected interface{}) types.GomegaMatcher { - return &matchers.AssignableToTypeOfMatcher{ - Expected: expected, - } -} - -//Panic succeeds if actual is a function that, when invoked, panics. -//Actual must be a function that takes no arguments and returns no results. -func Panic() types.GomegaMatcher { - return &matchers.PanicMatcher{} -} - -//BeAnExistingFile succeeds if a file exists. -//Actual must be a string representing the abs path to the file being checked. -func BeAnExistingFile() types.GomegaMatcher { - return &matchers.BeAnExistingFileMatcher{} -} - -//BeARegularFile succeeds iff a file exists and is a regular file. -//Actual must be a string representing the abs path to the file being checked. -func BeARegularFile() types.GomegaMatcher { - return &matchers.BeARegularFileMatcher{} -} - -//BeADirectory succeeds iff a file exists and is a directory. -//Actual must be a string representing the abs path to the file being checked. -func BeADirectory() types.GomegaMatcher { - return &matchers.BeADirectoryMatcher{} -} - -//And succeeds only if all of the given matchers succeed. -//The matchers are tried in order, and will fail-fast if one doesn't succeed. -// Expect("hi").To(And(HaveLen(2), Equal("hi")) -// -//And(), Or(), Not() and WithTransform() allow matchers to be composed into complex expressions. -func And(ms ...types.GomegaMatcher) types.GomegaMatcher { - return &matchers.AndMatcher{Matchers: ms} -} - -//SatisfyAll is an alias for And(). -// Ω("hi").Should(SatisfyAll(HaveLen(2), Equal("hi"))) -func SatisfyAll(matchers ...types.GomegaMatcher) types.GomegaMatcher { - return And(matchers...) -} - -//Or succeeds if any of the given matchers succeed. -//The matchers are tried in order and will return immediately upon the first successful match. -// Expect("hi").To(Or(HaveLen(3), HaveLen(2)) -// -//And(), Or(), Not() and WithTransform() allow matchers to be composed into complex expressions. -func Or(ms ...types.GomegaMatcher) types.GomegaMatcher { - return &matchers.OrMatcher{Matchers: ms} -} - -//SatisfyAny is an alias for Or(). -// Expect("hi").SatisfyAny(Or(HaveLen(3), HaveLen(2)) -func SatisfyAny(matchers ...types.GomegaMatcher) types.GomegaMatcher { - return Or(matchers...) -} - -//Not negates the given matcher; it succeeds if the given matcher fails. -// Expect(1).To(Not(Equal(2)) -// -//And(), Or(), Not() and WithTransform() allow matchers to be composed into complex expressions. -func Not(matcher types.GomegaMatcher) types.GomegaMatcher { - return &matchers.NotMatcher{Matcher: matcher} -} - -//WithTransform applies the `transform` to the actual value and matches it against `matcher`. -//The given transform must be a function of one parameter that returns one value. -// var plus1 = func(i int) int { return i + 1 } -// Expect(1).To(WithTransform(plus1, Equal(2)) -// -//And(), Or(), Not() and WithTransform() allow matchers to be composed into complex expressions. -func WithTransform(transform interface{}, matcher types.GomegaMatcher) types.GomegaMatcher { - return matchers.NewWithTransformMatcher(transform, matcher) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/and.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/and.go deleted file mode 100644 index 94c42a7..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/and.go +++ /dev/null @@ -1,64 +0,0 @@ -package matchers - -import ( - "fmt" - - "github.com/onsi/gomega/format" - "github.com/onsi/gomega/internal/oraclematcher" - "github.com/onsi/gomega/types" -) - -type AndMatcher struct { - Matchers []types.GomegaMatcher - - // state - firstFailedMatcher types.GomegaMatcher -} - -func (m *AndMatcher) Match(actual interface{}) (success bool, err error) { - m.firstFailedMatcher = nil - for _, matcher := range m.Matchers { - success, err := matcher.Match(actual) - if !success || err != nil { - m.firstFailedMatcher = matcher - return false, err - } - } - return true, nil -} - -func (m *AndMatcher) FailureMessage(actual interface{}) (message string) { - return m.firstFailedMatcher.FailureMessage(actual) -} - -func (m *AndMatcher) NegatedFailureMessage(actual interface{}) (message string) { - // not the most beautiful list of matchers, but not bad either... - return format.Message(actual, fmt.Sprintf("To not satisfy all of these matchers: %s", m.Matchers)) -} - -func (m *AndMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { - /* - Example with 3 matchers: A, B, C - - Match evaluates them: T, F, => F - So match is currently F, what should MatchMayChangeInTheFuture() return? - Seems like it only depends on B, since currently B MUST change to allow the result to become T - - Match eval: T, T, T => T - So match is currently T, what should MatchMayChangeInTheFuture() return? - Seems to depend on ANY of them being able to change to F. - */ - - if m.firstFailedMatcher == nil { - // so all matchers succeeded.. Any one of them changing would change the result. - for _, matcher := range m.Matchers { - if oraclematcher.MatchMayChangeInTheFuture(matcher, actual) { - return true - } - } - return false // none of were going to change - } else { - // one of the matchers failed.. it must be able to change in order to affect the result - return oraclematcher.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual) - } -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher.go deleted file mode 100644 index 89a1fc2..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher.go +++ /dev/null @@ -1,31 +0,0 @@ -package matchers - -import ( - "fmt" - "reflect" - - "github.com/onsi/gomega/format" -) - -type AssignableToTypeOfMatcher struct { - Expected interface{} -} - -func (matcher *AssignableToTypeOfMatcher) Match(actual interface{}) (success bool, err error) { - if actual == nil || matcher.Expected == nil { - return false, fmt.Errorf("Refusing to compare to .\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.") - } - - actualType := reflect.TypeOf(actual) - expectedType := reflect.TypeOf(matcher.Expected) - - return actualType.AssignableTo(expectedType), nil -} - -func (matcher *AssignableToTypeOfMatcher) FailureMessage(actual interface{}) string { - return format.Message(actual, fmt.Sprintf("to be assignable to the type: %T", matcher.Expected)) -} - -func (matcher *AssignableToTypeOfMatcher) NegatedFailureMessage(actual interface{}) string { - return format.Message(actual, fmt.Sprintf("not to be assignable to the type: %T", matcher.Expected)) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_directory.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_directory.go deleted file mode 100644 index 7b6975e..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_directory.go +++ /dev/null @@ -1,54 +0,0 @@ -package matchers - -import ( - "fmt" - "os" - - "github.com/onsi/gomega/format" -) - -type notADirectoryError struct { - os.FileInfo -} - -func (t notADirectoryError) Error() string { - fileInfo := os.FileInfo(t) - switch { - case fileInfo.Mode().IsRegular(): - return "file is a regular file" - default: - return fmt.Sprintf("file mode is: %s", fileInfo.Mode().String()) - } -} - -type BeADirectoryMatcher struct { - expected interface{} - err error -} - -func (matcher *BeADirectoryMatcher) Match(actual interface{}) (success bool, err error) { - actualFilename, ok := actual.(string) - if !ok { - return false, fmt.Errorf("BeADirectoryMatcher matcher expects a file path") - } - - fileInfo, err := os.Stat(actualFilename) - if err != nil { - matcher.err = err - return false, nil - } - - if !fileInfo.Mode().IsDir() { - matcher.err = notADirectoryError{fileInfo} - return false, nil - } - return true, nil -} - -func (matcher *BeADirectoryMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("to be a directory: %s", matcher.err)) -} - -func (matcher *BeADirectoryMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("not be a directory")) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_regular_file.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_regular_file.go deleted file mode 100644 index e239131..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_regular_file.go +++ /dev/null @@ -1,54 +0,0 @@ -package matchers - -import ( - "fmt" - "os" - - "github.com/onsi/gomega/format" -) - -type notARegularFileError struct { - os.FileInfo -} - -func (t notARegularFileError) Error() string { - fileInfo := os.FileInfo(t) - switch { - case fileInfo.IsDir(): - return "file is a directory" - default: - return fmt.Sprintf("file mode is: %s", fileInfo.Mode().String()) - } -} - -type BeARegularFileMatcher struct { - expected interface{} - err error -} - -func (matcher *BeARegularFileMatcher) Match(actual interface{}) (success bool, err error) { - actualFilename, ok := actual.(string) - if !ok { - return false, fmt.Errorf("BeARegularFileMatcher matcher expects a file path") - } - - fileInfo, err := os.Stat(actualFilename) - if err != nil { - matcher.err = err - return false, nil - } - - if !fileInfo.Mode().IsRegular() { - matcher.err = notARegularFileError{fileInfo} - return false, nil - } - return true, nil -} - -func (matcher *BeARegularFileMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("to be a regular file: %s", matcher.err)) -} - -func (matcher *BeARegularFileMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("not be a regular file")) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_an_existing_file.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_an_existing_file.go deleted file mode 100644 index d42eba2..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_an_existing_file.go +++ /dev/null @@ -1,38 +0,0 @@ -package matchers - -import ( - "fmt" - "os" - - "github.com/onsi/gomega/format" -) - -type BeAnExistingFileMatcher struct { - expected interface{} -} - -func (matcher *BeAnExistingFileMatcher) Match(actual interface{}) (success bool, err error) { - actualFilename, ok := actual.(string) - if !ok { - return false, fmt.Errorf("BeAnExistingFileMatcher matcher expects a file path") - } - - if _, err = os.Stat(actualFilename); err != nil { - switch { - case os.IsNotExist(err): - return false, nil - default: - return false, err - } - } - - return true, nil -} - -func (matcher *BeAnExistingFileMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("to exist")) -} - -func (matcher *BeAnExistingFileMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("not to exist")) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_closed_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_closed_matcher.go deleted file mode 100644 index c1b4995..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_closed_matcher.go +++ /dev/null @@ -1,45 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" - "reflect" -) - -type BeClosedMatcher struct { -} - -func (matcher *BeClosedMatcher) Match(actual interface{}) (success bool, err error) { - if !isChan(actual) { - return false, fmt.Errorf("BeClosed matcher expects a channel. Got:\n%s", format.Object(actual, 1)) - } - - channelType := reflect.TypeOf(actual) - channelValue := reflect.ValueOf(actual) - - if channelType.ChanDir() == reflect.SendDir { - return false, fmt.Errorf("BeClosed matcher cannot determine if a send-only channel is closed or open. Got:\n%s", format.Object(actual, 1)) - } - - winnerIndex, _, open := reflect.Select([]reflect.SelectCase{ - reflect.SelectCase{Dir: reflect.SelectRecv, Chan: channelValue}, - reflect.SelectCase{Dir: reflect.SelectDefault}, - }) - - var closed bool - if winnerIndex == 0 { - closed = !open - } else if winnerIndex == 1 { - closed = false - } - - return closed, nil -} - -func (matcher *BeClosedMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to be closed") -} - -func (matcher *BeClosedMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to be open") -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_empty_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_empty_matcher.go deleted file mode 100644 index 55bdd7d..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_empty_matcher.go +++ /dev/null @@ -1,26 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" -) - -type BeEmptyMatcher struct { -} - -func (matcher *BeEmptyMatcher) Match(actual interface{}) (success bool, err error) { - length, ok := lengthOf(actual) - if !ok { - return false, fmt.Errorf("BeEmpty matcher expects a string/array/map/channel/slice. Got:\n%s", format.Object(actual, 1)) - } - - return length == 0, nil -} - -func (matcher *BeEmptyMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to be empty") -} - -func (matcher *BeEmptyMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to be empty") -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_equivalent_to_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_equivalent_to_matcher.go deleted file mode 100644 index 32a0c31..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_equivalent_to_matcher.go +++ /dev/null @@ -1,33 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" - "reflect" -) - -type BeEquivalentToMatcher struct { - Expected interface{} -} - -func (matcher *BeEquivalentToMatcher) Match(actual interface{}) (success bool, err error) { - if actual == nil && matcher.Expected == nil { - return false, fmt.Errorf("Both actual and expected must not be nil.") - } - - convertedActual := actual - - if actual != nil && matcher.Expected != nil && reflect.TypeOf(actual).ConvertibleTo(reflect.TypeOf(matcher.Expected)) { - convertedActual = reflect.ValueOf(actual).Convert(reflect.TypeOf(matcher.Expected)).Interface() - } - - return reflect.DeepEqual(convertedActual, matcher.Expected), nil -} - -func (matcher *BeEquivalentToMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to be equivalent to", matcher.Expected) -} - -func (matcher *BeEquivalentToMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to be equivalent to", matcher.Expected) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_false_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_false_matcher.go deleted file mode 100644 index 0b224cb..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_false_matcher.go +++ /dev/null @@ -1,25 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" -) - -type BeFalseMatcher struct { -} - -func (matcher *BeFalseMatcher) Match(actual interface{}) (success bool, err error) { - if !isBool(actual) { - return false, fmt.Errorf("Expected a boolean. Got:\n%s", format.Object(actual, 1)) - } - - return actual == false, nil -} - -func (matcher *BeFalseMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to be false") -} - -func (matcher *BeFalseMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to be false") -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_nil_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_nil_matcher.go deleted file mode 100644 index 7ee84fe..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_nil_matcher.go +++ /dev/null @@ -1,18 +0,0 @@ -package matchers - -import "github.com/onsi/gomega/format" - -type BeNilMatcher struct { -} - -func (matcher *BeNilMatcher) Match(actual interface{}) (success bool, err error) { - return isNil(actual), nil -} - -func (matcher *BeNilMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to be nil") -} - -func (matcher *BeNilMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to be nil") -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_numerically_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_numerically_matcher.go deleted file mode 100644 index 52f83fe..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_numerically_matcher.go +++ /dev/null @@ -1,119 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" - "math" -) - -type BeNumericallyMatcher struct { - Comparator string - CompareTo []interface{} -} - -func (matcher *BeNumericallyMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("to be %s", matcher.Comparator), matcher.CompareTo[0]) -} - -func (matcher *BeNumericallyMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("not to be %s", matcher.Comparator), matcher.CompareTo[0]) -} - -func (matcher *BeNumericallyMatcher) Match(actual interface{}) (success bool, err error) { - if len(matcher.CompareTo) == 0 || len(matcher.CompareTo) > 2 { - return false, fmt.Errorf("BeNumerically requires 1 or 2 CompareTo arguments. Got:\n%s", format.Object(matcher.CompareTo, 1)) - } - if !isNumber(actual) { - return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(actual, 1)) - } - if !isNumber(matcher.CompareTo[0]) { - return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[0], 1)) - } - if len(matcher.CompareTo) == 2 && !isNumber(matcher.CompareTo[1]) { - return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[0], 1)) - } - - switch matcher.Comparator { - case "==", "~", ">", ">=", "<", "<=": - default: - return false, fmt.Errorf("Unknown comparator: %s", matcher.Comparator) - } - - if isFloat(actual) || isFloat(matcher.CompareTo[0]) { - var secondOperand float64 = 1e-8 - if len(matcher.CompareTo) == 2 { - secondOperand = toFloat(matcher.CompareTo[1]) - } - success = matcher.matchFloats(toFloat(actual), toFloat(matcher.CompareTo[0]), secondOperand) - } else if isInteger(actual) { - var secondOperand int64 = 0 - if len(matcher.CompareTo) == 2 { - secondOperand = toInteger(matcher.CompareTo[1]) - } - success = matcher.matchIntegers(toInteger(actual), toInteger(matcher.CompareTo[0]), secondOperand) - } else if isUnsignedInteger(actual) { - var secondOperand uint64 = 0 - if len(matcher.CompareTo) == 2 { - secondOperand = toUnsignedInteger(matcher.CompareTo[1]) - } - success = matcher.matchUnsignedIntegers(toUnsignedInteger(actual), toUnsignedInteger(matcher.CompareTo[0]), secondOperand) - } else { - return false, fmt.Errorf("Failed to compare:\n%s\n%s:\n%s", format.Object(actual, 1), matcher.Comparator, format.Object(matcher.CompareTo[0], 1)) - } - - return success, nil -} - -func (matcher *BeNumericallyMatcher) matchIntegers(actual, compareTo, threshold int64) (success bool) { - switch matcher.Comparator { - case "==", "~": - diff := actual - compareTo - return -threshold <= diff && diff <= threshold - case ">": - return (actual > compareTo) - case ">=": - return (actual >= compareTo) - case "<": - return (actual < compareTo) - case "<=": - return (actual <= compareTo) - } - return false -} - -func (matcher *BeNumericallyMatcher) matchUnsignedIntegers(actual, compareTo, threshold uint64) (success bool) { - switch matcher.Comparator { - case "==", "~": - if actual < compareTo { - actual, compareTo = compareTo, actual - } - return actual-compareTo <= threshold - case ">": - return (actual > compareTo) - case ">=": - return (actual >= compareTo) - case "<": - return (actual < compareTo) - case "<=": - return (actual <= compareTo) - } - return false -} - -func (matcher *BeNumericallyMatcher) matchFloats(actual, compareTo, threshold float64) (success bool) { - switch matcher.Comparator { - case "~": - return math.Abs(actual-compareTo) <= threshold - case "==": - return (actual == compareTo) - case ">": - return (actual > compareTo) - case ">=": - return (actual >= compareTo) - case "<": - return (actual < compareTo) - case "<=": - return (actual <= compareTo) - } - return false -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_sent_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_sent_matcher.go deleted file mode 100644 index d7c3223..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_sent_matcher.go +++ /dev/null @@ -1,71 +0,0 @@ -package matchers - -import ( - "fmt" - "reflect" - - "github.com/onsi/gomega/format" -) - -type BeSentMatcher struct { - Arg interface{} - channelClosed bool -} - -func (matcher *BeSentMatcher) Match(actual interface{}) (success bool, err error) { - if !isChan(actual) { - return false, fmt.Errorf("BeSent expects a channel. Got:\n%s", format.Object(actual, 1)) - } - - channelType := reflect.TypeOf(actual) - channelValue := reflect.ValueOf(actual) - - if channelType.ChanDir() == reflect.RecvDir { - return false, fmt.Errorf("BeSent matcher cannot be passed a receive-only channel. Got:\n%s", format.Object(actual, 1)) - } - - argType := reflect.TypeOf(matcher.Arg) - assignable := argType.AssignableTo(channelType.Elem()) - - if !assignable { - return false, fmt.Errorf("Cannot pass:\n%s to the channel:\n%s\nThe types don't match.", format.Object(matcher.Arg, 1), format.Object(actual, 1)) - } - - argValue := reflect.ValueOf(matcher.Arg) - - defer func() { - if e := recover(); e != nil { - success = false - err = fmt.Errorf("Cannot send to a closed channel") - matcher.channelClosed = true - } - }() - - winnerIndex, _, _ := reflect.Select([]reflect.SelectCase{ - reflect.SelectCase{Dir: reflect.SelectSend, Chan: channelValue, Send: argValue}, - reflect.SelectCase{Dir: reflect.SelectDefault}, - }) - - var didSend bool - if winnerIndex == 0 { - didSend = true - } - - return didSend, nil -} - -func (matcher *BeSentMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to send:", matcher.Arg) -} - -func (matcher *BeSentMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to send:", matcher.Arg) -} - -func (matcher *BeSentMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { - if !isChan(actual) { - return false - } - - return !matcher.channelClosed -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_temporally_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_temporally_matcher.go deleted file mode 100644 index abda4eb..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_temporally_matcher.go +++ /dev/null @@ -1,65 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" - "time" -) - -type BeTemporallyMatcher struct { - Comparator string - CompareTo time.Time - Threshold []time.Duration -} - -func (matcher *BeTemporallyMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("to be %s", matcher.Comparator), matcher.CompareTo) -} - -func (matcher *BeTemporallyMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("not to be %s", matcher.Comparator), matcher.CompareTo) -} - -func (matcher *BeTemporallyMatcher) Match(actual interface{}) (bool, error) { - // predicate to test for time.Time type - isTime := func(t interface{}) bool { - _, ok := t.(time.Time) - return ok - } - - if !isTime(actual) { - return false, fmt.Errorf("Expected a time.Time. Got:\n%s", format.Object(actual, 1)) - } - - switch matcher.Comparator { - case "==", "~", ">", ">=", "<", "<=": - default: - return false, fmt.Errorf("Unknown comparator: %s", matcher.Comparator) - } - - var threshold = time.Millisecond - if len(matcher.Threshold) == 1 { - threshold = matcher.Threshold[0] - } - - return matcher.matchTimes(actual.(time.Time), matcher.CompareTo, threshold), nil -} - -func (matcher *BeTemporallyMatcher) matchTimes(actual, compareTo time.Time, threshold time.Duration) (success bool) { - switch matcher.Comparator { - case "==": - return actual.Equal(compareTo) - case "~": - diff := actual.Sub(compareTo) - return -threshold <= diff && diff <= threshold - case ">": - return actual.After(compareTo) - case ">=": - return !actual.Before(compareTo) - case "<": - return actual.Before(compareTo) - case "<=": - return !actual.After(compareTo) - } - return false -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_true_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_true_matcher.go deleted file mode 100644 index 1275e5f..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_true_matcher.go +++ /dev/null @@ -1,25 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" -) - -type BeTrueMatcher struct { -} - -func (matcher *BeTrueMatcher) Match(actual interface{}) (success bool, err error) { - if !isBool(actual) { - return false, fmt.Errorf("Expected a boolean. Got:\n%s", format.Object(actual, 1)) - } - - return actual.(bool), nil -} - -func (matcher *BeTrueMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to be true") -} - -func (matcher *BeTrueMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to be true") -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_zero_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_zero_matcher.go deleted file mode 100644 index b39c914..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_zero_matcher.go +++ /dev/null @@ -1,27 +0,0 @@ -package matchers - -import ( - "github.com/onsi/gomega/format" - "reflect" -) - -type BeZeroMatcher struct { -} - -func (matcher *BeZeroMatcher) Match(actual interface{}) (success bool, err error) { - if actual == nil { - return true, nil - } - zeroValue := reflect.Zero(reflect.TypeOf(actual)).Interface() - - return reflect.DeepEqual(zeroValue, actual), nil - -} - -func (matcher *BeZeroMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to be zero-valued") -} - -func (matcher *BeZeroMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to be zero-valued") -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/consist_of.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/consist_of.go deleted file mode 100644 index 7b0e088..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/consist_of.go +++ /dev/null @@ -1,80 +0,0 @@ -package matchers - -import ( - "fmt" - "reflect" - - "github.com/onsi/gomega/format" - "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph" -) - -type ConsistOfMatcher struct { - Elements []interface{} -} - -func (matcher *ConsistOfMatcher) Match(actual interface{}) (success bool, err error) { - if !isArrayOrSlice(actual) && !isMap(actual) { - return false, fmt.Errorf("ConsistOf matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1)) - } - - elements := matcher.Elements - if len(matcher.Elements) == 1 && isArrayOrSlice(matcher.Elements[0]) { - elements = []interface{}{} - value := reflect.ValueOf(matcher.Elements[0]) - for i := 0; i < value.Len(); i++ { - elements = append(elements, value.Index(i).Interface()) - } - } - - matchers := []interface{}{} - for _, element := range elements { - matcher, isMatcher := element.(omegaMatcher) - if !isMatcher { - matcher = &EqualMatcher{Expected: element} - } - matchers = append(matchers, matcher) - } - - values := matcher.valuesOf(actual) - - if len(values) != len(matchers) { - return false, nil - } - - neighbours := func(v, m interface{}) (bool, error) { - match, err := m.(omegaMatcher).Match(v) - return match && err == nil, nil - } - - bipartiteGraph, err := bipartitegraph.NewBipartiteGraph(values, matchers, neighbours) - if err != nil { - return false, err - } - - return len(bipartiteGraph.LargestMatching()) == len(values), nil -} - -func (matcher *ConsistOfMatcher) valuesOf(actual interface{}) []interface{} { - value := reflect.ValueOf(actual) - values := []interface{}{} - if isMap(actual) { - keys := value.MapKeys() - for i := 0; i < value.Len(); i++ { - values = append(values, value.MapIndex(keys[i]).Interface()) - } - } else { - for i := 0; i < value.Len(); i++ { - values = append(values, value.Index(i).Interface()) - } - } - - return values -} - -func (matcher *ConsistOfMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to consist of", matcher.Elements) -} - -func (matcher *ConsistOfMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to consist of", matcher.Elements) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/contain_element_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/contain_element_matcher.go deleted file mode 100644 index 4159335..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/contain_element_matcher.go +++ /dev/null @@ -1,56 +0,0 @@ -package matchers - -import ( - "fmt" - "reflect" - - "github.com/onsi/gomega/format" -) - -type ContainElementMatcher struct { - Element interface{} -} - -func (matcher *ContainElementMatcher) Match(actual interface{}) (success bool, err error) { - if !isArrayOrSlice(actual) && !isMap(actual) { - return false, fmt.Errorf("ContainElement matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1)) - } - - elemMatcher, elementIsMatcher := matcher.Element.(omegaMatcher) - if !elementIsMatcher { - elemMatcher = &EqualMatcher{Expected: matcher.Element} - } - - value := reflect.ValueOf(actual) - var keys []reflect.Value - if isMap(actual) { - keys = value.MapKeys() - } - var lastError error - for i := 0; i < value.Len(); i++ { - var success bool - var err error - if isMap(actual) { - success, err = elemMatcher.Match(value.MapIndex(keys[i]).Interface()) - } else { - success, err = elemMatcher.Match(value.Index(i).Interface()) - } - if err != nil { - lastError = err - continue - } - if success { - return true, nil - } - } - - return false, lastError -} - -func (matcher *ContainElementMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to contain element matching", matcher.Element) -} - -func (matcher *ContainElementMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to contain element matching", matcher.Element) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/contain_substring_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/contain_substring_matcher.go deleted file mode 100644 index 2e76089..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/contain_substring_matcher.go +++ /dev/null @@ -1,37 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" - "strings" -) - -type ContainSubstringMatcher struct { - Substr string - Args []interface{} -} - -func (matcher *ContainSubstringMatcher) Match(actual interface{}) (success bool, err error) { - actualString, ok := toString(actual) - if !ok { - return false, fmt.Errorf("ContainSubstring matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1)) - } - - return strings.Contains(actualString, matcher.stringToMatch()), nil -} - -func (matcher *ContainSubstringMatcher) stringToMatch() string { - stringToMatch := matcher.Substr - if len(matcher.Args) > 0 { - stringToMatch = fmt.Sprintf(matcher.Substr, matcher.Args...) - } - return stringToMatch -} - -func (matcher *ContainSubstringMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to contain substring", matcher.stringToMatch()) -} - -func (matcher *ContainSubstringMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to contain substring", matcher.stringToMatch()) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/equal_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/equal_matcher.go deleted file mode 100644 index d186597..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/equal_matcher.go +++ /dev/null @@ -1,27 +0,0 @@ -package matchers - -import ( - "fmt" - "reflect" - - "github.com/onsi/gomega/format" -) - -type EqualMatcher struct { - Expected interface{} -} - -func (matcher *EqualMatcher) Match(actual interface{}) (success bool, err error) { - if actual == nil && matcher.Expected == nil { - return false, fmt.Errorf("Refusing to compare to .\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.") - } - return reflect.DeepEqual(actual, matcher.Expected), nil -} - -func (matcher *EqualMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to equal", matcher.Expected) -} - -func (matcher *EqualMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to equal", matcher.Expected) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_key_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_key_matcher.go deleted file mode 100644 index 5701ba6..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_key_matcher.go +++ /dev/null @@ -1,53 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" - "reflect" -) - -type HaveKeyMatcher struct { - Key interface{} -} - -func (matcher *HaveKeyMatcher) Match(actual interface{}) (success bool, err error) { - if !isMap(actual) { - return false, fmt.Errorf("HaveKey matcher expects a map. Got:%s", format.Object(actual, 1)) - } - - keyMatcher, keyIsMatcher := matcher.Key.(omegaMatcher) - if !keyIsMatcher { - keyMatcher = &EqualMatcher{Expected: matcher.Key} - } - - keys := reflect.ValueOf(actual).MapKeys() - for i := 0; i < len(keys); i++ { - success, err := keyMatcher.Match(keys[i].Interface()) - if err != nil { - return false, fmt.Errorf("HaveKey's key matcher failed with:\n%s%s", format.Indent, err.Error()) - } - if success { - return true, nil - } - } - - return false, nil -} - -func (matcher *HaveKeyMatcher) FailureMessage(actual interface{}) (message string) { - switch matcher.Key.(type) { - case omegaMatcher: - return format.Message(actual, "to have key matching", matcher.Key) - default: - return format.Message(actual, "to have key", matcher.Key) - } -} - -func (matcher *HaveKeyMatcher) NegatedFailureMessage(actual interface{}) (message string) { - switch matcher.Key.(type) { - case omegaMatcher: - return format.Message(actual, "not to have key matching", matcher.Key) - default: - return format.Message(actual, "not to have key", matcher.Key) - } -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go deleted file mode 100644 index 464ac18..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go +++ /dev/null @@ -1,73 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" - "reflect" -) - -type HaveKeyWithValueMatcher struct { - Key interface{} - Value interface{} -} - -func (matcher *HaveKeyWithValueMatcher) Match(actual interface{}) (success bool, err error) { - if !isMap(actual) { - return false, fmt.Errorf("HaveKeyWithValue matcher expects a map. Got:%s", format.Object(actual, 1)) - } - - keyMatcher, keyIsMatcher := matcher.Key.(omegaMatcher) - if !keyIsMatcher { - keyMatcher = &EqualMatcher{Expected: matcher.Key} - } - - valueMatcher, valueIsMatcher := matcher.Value.(omegaMatcher) - if !valueIsMatcher { - valueMatcher = &EqualMatcher{Expected: matcher.Value} - } - - keys := reflect.ValueOf(actual).MapKeys() - for i := 0; i < len(keys); i++ { - success, err := keyMatcher.Match(keys[i].Interface()) - if err != nil { - return false, fmt.Errorf("HaveKeyWithValue's key matcher failed with:\n%s%s", format.Indent, err.Error()) - } - if success { - actualValue := reflect.ValueOf(actual).MapIndex(keys[i]) - success, err := valueMatcher.Match(actualValue.Interface()) - if err != nil { - return false, fmt.Errorf("HaveKeyWithValue's value matcher failed with:\n%s%s", format.Indent, err.Error()) - } - return success, nil - } - } - - return false, nil -} - -func (matcher *HaveKeyWithValueMatcher) FailureMessage(actual interface{}) (message string) { - str := "to have {key: value}" - if _, ok := matcher.Key.(omegaMatcher); ok { - str += " matching" - } else if _, ok := matcher.Value.(omegaMatcher); ok { - str += " matching" - } - - expect := make(map[interface{}]interface{}, 1) - expect[matcher.Key] = matcher.Value - return format.Message(actual, str, expect) -} - -func (matcher *HaveKeyWithValueMatcher) NegatedFailureMessage(actual interface{}) (message string) { - kStr := "not to have key" - if _, ok := matcher.Key.(omegaMatcher); ok { - kStr = "not to have key matching" - } - - vStr := "or that key's value not be" - if _, ok := matcher.Value.(omegaMatcher); ok { - vStr = "or to have that key's value not matching" - } - - return format.Message(actual, kStr, matcher.Key, vStr, matcher.Value) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_len_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_len_matcher.go deleted file mode 100644 index a183775..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_len_matcher.go +++ /dev/null @@ -1,27 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" -) - -type HaveLenMatcher struct { - Count int -} - -func (matcher *HaveLenMatcher) Match(actual interface{}) (success bool, err error) { - length, ok := lengthOf(actual) - if !ok { - return false, fmt.Errorf("HaveLen matcher expects a string/array/map/channel/slice. Got:\n%s", format.Object(actual, 1)) - } - - return length == matcher.Count, nil -} - -func (matcher *HaveLenMatcher) FailureMessage(actual interface{}) (message string) { - return fmt.Sprintf("Expected\n%s\nto have length %d", format.Object(actual, 1), matcher.Count) -} - -func (matcher *HaveLenMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return fmt.Sprintf("Expected\n%s\nnot to have length %d", format.Object(actual, 1), matcher.Count) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_occurred_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_occurred_matcher.go deleted file mode 100644 index cdc1d54..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_occurred_matcher.go +++ /dev/null @@ -1,30 +0,0 @@ -package matchers - -import ( - "fmt" - - "github.com/onsi/gomega/format" -) - -type HaveOccurredMatcher struct { -} - -func (matcher *HaveOccurredMatcher) Match(actual interface{}) (success bool, err error) { - if isNil(actual) { - return false, nil - } - - if isError(actual) { - return true, nil - } - - return false, fmt.Errorf("Expected an error. Got:\n%s", format.Object(actual, 1)) -} - -func (matcher *HaveOccurredMatcher) FailureMessage(actual interface{}) (message string) { - return fmt.Sprintf("Expected an error to have occurred. Got:\n%s", format.Object(actual, 1)) -} - -func (matcher *HaveOccurredMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return fmt.Sprintf("Expected error:\n%s\n%s\n%s", format.Object(actual, 1), format.IndentString(actual.(error).Error(), 1), "not to have occurred") -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_prefix_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_prefix_matcher.go deleted file mode 100644 index 8b63a89..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_prefix_matcher.go +++ /dev/null @@ -1,35 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" -) - -type HavePrefixMatcher struct { - Prefix string - Args []interface{} -} - -func (matcher *HavePrefixMatcher) Match(actual interface{}) (success bool, err error) { - actualString, ok := toString(actual) - if !ok { - return false, fmt.Errorf("HavePrefix matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1)) - } - prefix := matcher.prefix() - return len(actualString) >= len(prefix) && actualString[0:len(prefix)] == prefix, nil -} - -func (matcher *HavePrefixMatcher) prefix() string { - if len(matcher.Args) > 0 { - return fmt.Sprintf(matcher.Prefix, matcher.Args...) - } - return matcher.Prefix -} - -func (matcher *HavePrefixMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to have prefix", matcher.prefix()) -} - -func (matcher *HavePrefixMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to have prefix", matcher.prefix()) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_suffix_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_suffix_matcher.go deleted file mode 100644 index eb1b284..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_suffix_matcher.go +++ /dev/null @@ -1,35 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" -) - -type HaveSuffixMatcher struct { - Suffix string - Args []interface{} -} - -func (matcher *HaveSuffixMatcher) Match(actual interface{}) (success bool, err error) { - actualString, ok := toString(actual) - if !ok { - return false, fmt.Errorf("HaveSuffix matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1)) - } - suffix := matcher.suffix() - return len(actualString) >= len(suffix) && actualString[len(actualString) - len(suffix):] == suffix, nil -} - -func (matcher *HaveSuffixMatcher) suffix() string { - if len(matcher.Args) > 0 { - return fmt.Sprintf(matcher.Suffix, matcher.Args...) - } - return matcher.Suffix -} - -func (matcher *HaveSuffixMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to have suffix", matcher.suffix()) -} - -func (matcher *HaveSuffixMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to have suffix", matcher.suffix()) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_error_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_error_matcher.go deleted file mode 100644 index 03cdf04..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_error_matcher.go +++ /dev/null @@ -1,50 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" - "reflect" -) - -type MatchErrorMatcher struct { - Expected interface{} -} - -func (matcher *MatchErrorMatcher) Match(actual interface{}) (success bool, err error) { - if isNil(actual) { - return false, fmt.Errorf("Expected an error, got nil") - } - - if !isError(actual) { - return false, fmt.Errorf("Expected an error. Got:\n%s", format.Object(actual, 1)) - } - - actualErr := actual.(error) - - if isString(matcher.Expected) { - return reflect.DeepEqual(actualErr.Error(), matcher.Expected), nil - } - - if isError(matcher.Expected) { - return reflect.DeepEqual(actualErr, matcher.Expected), nil - } - - var subMatcher omegaMatcher - var hasSubMatcher bool - if matcher.Expected != nil { - subMatcher, hasSubMatcher = (matcher.Expected).(omegaMatcher) - if hasSubMatcher { - return subMatcher.Match(actualErr.Error()) - } - } - - return false, fmt.Errorf("MatchError must be passed an error, string, or Matcher that can match on strings. Got:\n%s", format.Object(matcher.Expected, 1)) -} - -func (matcher *MatchErrorMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to match error", matcher.Expected) -} - -func (matcher *MatchErrorMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to match error", matcher.Expected) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_json_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_json_matcher.go deleted file mode 100644 index efc5e15..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_json_matcher.go +++ /dev/null @@ -1,61 +0,0 @@ -package matchers - -import ( - "bytes" - "encoding/json" - "fmt" - "github.com/onsi/gomega/format" - "reflect" -) - -type MatchJSONMatcher struct { - JSONToMatch interface{} -} - -func (matcher *MatchJSONMatcher) Match(actual interface{}) (success bool, err error) { - actualString, expectedString, err := matcher.prettyPrint(actual) - if err != nil { - return false, err - } - - var aval interface{} - var eval interface{} - - // this is guarded by prettyPrint - json.Unmarshal([]byte(actualString), &aval) - json.Unmarshal([]byte(expectedString), &eval) - - return reflect.DeepEqual(aval, eval), nil -} - -func (matcher *MatchJSONMatcher) FailureMessage(actual interface{}) (message string) { - actualString, expectedString, _ := matcher.prettyPrint(actual) - return format.Message(actualString, "to match JSON of", expectedString) -} - -func (matcher *MatchJSONMatcher) NegatedFailureMessage(actual interface{}) (message string) { - actualString, expectedString, _ := matcher.prettyPrint(actual) - return format.Message(actualString, "not to match JSON of", expectedString) -} - -func (matcher *MatchJSONMatcher) prettyPrint(actual interface{}) (actualFormatted, expectedFormatted string, err error) { - actualString, aok := toString(actual) - expectedString, eok := toString(matcher.JSONToMatch) - - if !(aok && eok) { - return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1)) - } - - abuf := new(bytes.Buffer) - ebuf := new(bytes.Buffer) - - if err := json.Indent(abuf, []byte(actualString), "", " "); err != nil { - return "", "", err - } - - if err := json.Indent(ebuf, []byte(expectedString), "", " "); err != nil { - return "", "", err - } - - return abuf.String(), ebuf.String(), nil -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_regexp_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_regexp_matcher.go deleted file mode 100644 index 7ca79a1..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/match_regexp_matcher.go +++ /dev/null @@ -1,42 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" - "regexp" -) - -type MatchRegexpMatcher struct { - Regexp string - Args []interface{} -} - -func (matcher *MatchRegexpMatcher) Match(actual interface{}) (success bool, err error) { - actualString, ok := toString(actual) - if !ok { - return false, fmt.Errorf("RegExp matcher requires a string or stringer.\nGot:%s", format.Object(actual, 1)) - } - - match, err := regexp.Match(matcher.regexp(), []byte(actualString)) - if err != nil { - return false, fmt.Errorf("RegExp match failed to compile with error:\n\t%s", err.Error()) - } - - return match, nil -} - -func (matcher *MatchRegexpMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to match regular expression", matcher.regexp()) -} - -func (matcher *MatchRegexpMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to match regular expression", matcher.regexp()) -} - -func (matcher *MatchRegexpMatcher) regexp() string { - re := matcher.Regexp - if len(matcher.Args) > 0 { - re = fmt.Sprintf(matcher.Regexp, matcher.Args...) - } - return re -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/not.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/not.go deleted file mode 100644 index 2c91670..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/not.go +++ /dev/null @@ -1,30 +0,0 @@ -package matchers - -import ( - "github.com/onsi/gomega/internal/oraclematcher" - "github.com/onsi/gomega/types" -) - -type NotMatcher struct { - Matcher types.GomegaMatcher -} - -func (m *NotMatcher) Match(actual interface{}) (bool, error) { - success, err := m.Matcher.Match(actual) - if err != nil { - return false, err - } - return !success, nil -} - -func (m *NotMatcher) FailureMessage(actual interface{}) (message string) { - return m.Matcher.NegatedFailureMessage(actual) // works beautifully -} - -func (m *NotMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return m.Matcher.FailureMessage(actual) // works beautifully -} - -func (m *NotMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { - return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/or.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/or.go deleted file mode 100644 index 3bf7998..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/or.go +++ /dev/null @@ -1,67 +0,0 @@ -package matchers - -import ( - "fmt" - - "github.com/onsi/gomega/format" - "github.com/onsi/gomega/internal/oraclematcher" - "github.com/onsi/gomega/types" -) - -type OrMatcher struct { - Matchers []types.GomegaMatcher - - // state - firstSuccessfulMatcher types.GomegaMatcher -} - -func (m *OrMatcher) Match(actual interface{}) (success bool, err error) { - m.firstSuccessfulMatcher = nil - for _, matcher := range m.Matchers { - success, err := matcher.Match(actual) - if err != nil { - return false, err - } - if success { - m.firstSuccessfulMatcher = matcher - return true, nil - } - } - return false, nil -} - -func (m *OrMatcher) FailureMessage(actual interface{}) (message string) { - // not the most beautiful list of matchers, but not bad either... - return format.Message(actual, fmt.Sprintf("To satisfy at least one of these matchers: %s", m.Matchers)) -} - -func (m *OrMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return m.firstSuccessfulMatcher.NegatedFailureMessage(actual) -} - -func (m *OrMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { - /* - Example with 3 matchers: A, B, C - - Match evaluates them: F, T, => T - So match is currently T, what should MatchMayChangeInTheFuture() return? - Seems like it only depends on B, since currently B MUST change to allow the result to become F - - Match eval: F, F, F => F - So match is currently F, what should MatchMayChangeInTheFuture() return? - Seems to depend on ANY of them being able to change to T. - */ - - if m.firstSuccessfulMatcher != nil { - // one of the matchers succeeded.. it must be able to change in order to affect the result - return oraclematcher.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual) - } else { - // so all matchers failed.. Any one of them changing would change the result. - for _, matcher := range m.Matchers { - if oraclematcher.MatchMayChangeInTheFuture(matcher, actual) { - return true - } - } - return false // none of were going to change - } -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/panic_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/panic_matcher.go deleted file mode 100644 index 75ab251..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/panic_matcher.go +++ /dev/null @@ -1,42 +0,0 @@ -package matchers - -import ( - "fmt" - "github.com/onsi/gomega/format" - "reflect" -) - -type PanicMatcher struct{} - -func (matcher *PanicMatcher) Match(actual interface{}) (success bool, err error) { - if actual == nil { - return false, fmt.Errorf("PanicMatcher expects a non-nil actual.") - } - - actualType := reflect.TypeOf(actual) - if actualType.Kind() != reflect.Func { - return false, fmt.Errorf("PanicMatcher expects a function. Got:\n%s", format.Object(actual, 1)) - } - if !(actualType.NumIn() == 0 && actualType.NumOut() == 0) { - return false, fmt.Errorf("PanicMatcher expects a function with no arguments and no return value. Got:\n%s", format.Object(actual, 1)) - } - - success = false - defer func() { - if e := recover(); e != nil { - success = true - } - }() - - reflect.ValueOf(actual).Call([]reflect.Value{}) - - return -} - -func (matcher *PanicMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to panic") -} - -func (matcher *PanicMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to panic") -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/receive_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/receive_matcher.go deleted file mode 100644 index 7a8c2cd..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/receive_matcher.go +++ /dev/null @@ -1,126 +0,0 @@ -package matchers - -import ( - "fmt" - "reflect" - - "github.com/onsi/gomega/format" -) - -type ReceiveMatcher struct { - Arg interface{} - receivedValue reflect.Value - channelClosed bool -} - -func (matcher *ReceiveMatcher) Match(actual interface{}) (success bool, err error) { - if !isChan(actual) { - return false, fmt.Errorf("ReceiveMatcher expects a channel. Got:\n%s", format.Object(actual, 1)) - } - - channelType := reflect.TypeOf(actual) - channelValue := reflect.ValueOf(actual) - - if channelType.ChanDir() == reflect.SendDir { - return false, fmt.Errorf("ReceiveMatcher matcher cannot be passed a send-only channel. Got:\n%s", format.Object(actual, 1)) - } - - var subMatcher omegaMatcher - var hasSubMatcher bool - - if matcher.Arg != nil { - subMatcher, hasSubMatcher = (matcher.Arg).(omegaMatcher) - if !hasSubMatcher { - argType := reflect.TypeOf(matcher.Arg) - if argType.Kind() != reflect.Ptr { - return false, fmt.Errorf("Cannot assign a value from the channel:\n%s\nTo:\n%s\nYou need to pass a pointer!", format.Object(actual, 1), format.Object(matcher.Arg, 1)) - } - - assignable := channelType.Elem().AssignableTo(argType.Elem()) - if !assignable { - return false, fmt.Errorf("Cannot assign a value from the channel:\n%s\nTo:\n%s", format.Object(actual, 1), format.Object(matcher.Arg, 1)) - } - } - } - - winnerIndex, value, open := reflect.Select([]reflect.SelectCase{ - reflect.SelectCase{Dir: reflect.SelectRecv, Chan: channelValue}, - reflect.SelectCase{Dir: reflect.SelectDefault}, - }) - - var closed bool - var didReceive bool - if winnerIndex == 0 { - closed = !open - didReceive = open - } - matcher.channelClosed = closed - - if closed { - return false, nil - } - - if hasSubMatcher { - if didReceive { - matcher.receivedValue = value - return subMatcher.Match(matcher.receivedValue.Interface()) - } else { - return false, nil - } - } - - if didReceive { - if matcher.Arg != nil { - outValue := reflect.ValueOf(matcher.Arg) - reflect.Indirect(outValue).Set(value) - } - - return true, nil - } else { - return false, nil - } -} - -func (matcher *ReceiveMatcher) FailureMessage(actual interface{}) (message string) { - subMatcher, hasSubMatcher := (matcher.Arg).(omegaMatcher) - - closedAddendum := "" - if matcher.channelClosed { - closedAddendum = " The channel is closed." - } - - if hasSubMatcher { - if matcher.receivedValue.IsValid() { - return subMatcher.FailureMessage(matcher.receivedValue.Interface()) - } - return "When passed a matcher, ReceiveMatcher's channel *must* receive something." - } else { - return format.Message(actual, "to receive something."+closedAddendum) - } -} - -func (matcher *ReceiveMatcher) NegatedFailureMessage(actual interface{}) (message string) { - subMatcher, hasSubMatcher := (matcher.Arg).(omegaMatcher) - - closedAddendum := "" - if matcher.channelClosed { - closedAddendum = " The channel is closed." - } - - if hasSubMatcher { - if matcher.receivedValue.IsValid() { - return subMatcher.NegatedFailureMessage(matcher.receivedValue.Interface()) - } - return "When passed a matcher, ReceiveMatcher's channel *must* receive something." - } else { - return format.Message(actual, "not to receive anything."+closedAddendum) - } -} - -func (matcher *ReceiveMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { - if !isChan(actual) { - return false - } - - return !matcher.channelClosed -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go deleted file mode 100644 index f7dd853..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go +++ /dev/null @@ -1,30 +0,0 @@ -package matchers - -import ( - "fmt" - - "github.com/onsi/gomega/format" -) - -type SucceedMatcher struct { -} - -func (matcher *SucceedMatcher) Match(actual interface{}) (success bool, err error) { - if actual == nil { - return true, nil - } - - if isError(actual) { - return false, nil - } - - return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1)) -} - -func (matcher *SucceedMatcher) FailureMessage(actual interface{}) (message string) { - return fmt.Sprintf("Expected success, but got an error:\n%s\n%s", format.Object(actual, 1), format.IndentString(actual.(error).Error(), 1)) -} - -func (matcher *SucceedMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return "Expected failure, but got no error." -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/MIT.LICENSE b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/MIT.LICENSE deleted file mode 100644 index 8edd817..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/MIT.LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2014 Amit Kumar Gupta - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraph.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraph.go deleted file mode 100644 index 119d21e..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraph.go +++ /dev/null @@ -1,41 +0,0 @@ -package bipartitegraph - -import "errors" -import "fmt" - -import . "github.com/onsi/gomega/matchers/support/goraph/node" -import . "github.com/onsi/gomega/matchers/support/goraph/edge" - -type BipartiteGraph struct { - Left NodeOrderedSet - Right NodeOrderedSet - Edges EdgeSet -} - -func NewBipartiteGraph(leftValues, rightValues []interface{}, neighbours func(interface{}, interface{}) (bool, error)) (*BipartiteGraph, error) { - left := NodeOrderedSet{} - for i, _ := range leftValues { - left = append(left, Node{i}) - } - - right := NodeOrderedSet{} - for j, _ := range rightValues { - right = append(right, Node{j + len(left)}) - } - - edges := EdgeSet{} - for i, leftValue := range leftValues { - for j, rightValue := range rightValues { - neighbours, err := neighbours(leftValue, rightValue) - if err != nil { - return nil, errors.New(fmt.Sprintf("error determining adjacency for %v and %v: %s", leftValue, rightValue, err.Error())) - } - - if neighbours { - edges = append(edges, Edge{left[i], right[j]}) - } - } - } - - return &BipartiteGraph{left, right, edges}, nil -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go deleted file mode 100644 index 32529c5..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go +++ /dev/null @@ -1,161 +0,0 @@ -package bipartitegraph - -import . "github.com/onsi/gomega/matchers/support/goraph/node" -import . "github.com/onsi/gomega/matchers/support/goraph/edge" -import "github.com/onsi/gomega/matchers/support/goraph/util" - -func (bg *BipartiteGraph) LargestMatching() (matching EdgeSet) { - paths := bg.maximalDisjointSLAPCollection(matching) - - for len(paths) > 0 { - for _, path := range paths { - matching = matching.SymmetricDifference(path) - } - paths = bg.maximalDisjointSLAPCollection(matching) - } - - return -} - -func (bg *BipartiteGraph) maximalDisjointSLAPCollection(matching EdgeSet) (result []EdgeSet) { - guideLayers := bg.createSLAPGuideLayers(matching) - if len(guideLayers) == 0 { - return - } - - used := make(map[Node]bool) - - for _, u := range guideLayers[len(guideLayers)-1] { - slap, found := bg.findDisjointSLAP(u, matching, guideLayers, used) - if found { - for _, edge := range slap { - used[edge.Node1] = true - used[edge.Node2] = true - } - result = append(result, slap) - } - } - - return -} - -func (bg *BipartiteGraph) findDisjointSLAP( - start Node, - matching EdgeSet, - guideLayers []NodeOrderedSet, - used map[Node]bool, -) ([]Edge, bool) { - return bg.findDisjointSLAPHelper(start, EdgeSet{}, len(guideLayers)-1, matching, guideLayers, used) -} - -func (bg *BipartiteGraph) findDisjointSLAPHelper( - currentNode Node, - currentSLAP EdgeSet, - currentLevel int, - matching EdgeSet, - guideLayers []NodeOrderedSet, - used map[Node]bool, -) (EdgeSet, bool) { - used[currentNode] = true - - if currentLevel == 0 { - return currentSLAP, true - } - - for _, nextNode := range guideLayers[currentLevel-1] { - if used[nextNode] { - continue - } - - edge, found := bg.Edges.FindByNodes(currentNode, nextNode) - if !found { - continue - } - - if matching.Contains(edge) == util.Odd(currentLevel) { - continue - } - - currentSLAP = append(currentSLAP, edge) - slap, found := bg.findDisjointSLAPHelper(nextNode, currentSLAP, currentLevel-1, matching, guideLayers, used) - if found { - return slap, true - } - currentSLAP = currentSLAP[:len(currentSLAP)-1] - } - - used[currentNode] = false - return nil, false -} - -func (bg *BipartiteGraph) createSLAPGuideLayers(matching EdgeSet) (guideLayers []NodeOrderedSet) { - used := make(map[Node]bool) - currentLayer := NodeOrderedSet{} - - for _, node := range bg.Left { - if matching.Free(node) { - used[node] = true - currentLayer = append(currentLayer, node) - } - } - - if len(currentLayer) == 0 { - return []NodeOrderedSet{} - } else { - guideLayers = append(guideLayers, currentLayer) - } - - done := false - - for !done { - lastLayer := currentLayer - currentLayer = NodeOrderedSet{} - - if util.Odd(len(guideLayers)) { - for _, leftNode := range lastLayer { - for _, rightNode := range bg.Right { - if used[rightNode] { - continue - } - - edge, found := bg.Edges.FindByNodes(leftNode, rightNode) - if !found || matching.Contains(edge) { - continue - } - - currentLayer = append(currentLayer, rightNode) - used[rightNode] = true - - if matching.Free(rightNode) { - done = true - } - } - } - } else { - for _, rightNode := range lastLayer { - for _, leftNode := range bg.Left { - if used[leftNode] { - continue - } - - edge, found := bg.Edges.FindByNodes(leftNode, rightNode) - if !found || !matching.Contains(edge) { - continue - } - - currentLayer = append(currentLayer, leftNode) - used[leftNode] = true - } - } - - } - - if len(currentLayer) == 0 { - return []NodeOrderedSet{} - } else { - guideLayers = append(guideLayers, currentLayer) - } - } - - return -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go deleted file mode 100644 index 4fd15cc..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go +++ /dev/null @@ -1,61 +0,0 @@ -package edge - -import . "github.com/onsi/gomega/matchers/support/goraph/node" - -type Edge struct { - Node1 Node - Node2 Node -} - -type EdgeSet []Edge - -func (ec EdgeSet) Free(node Node) bool { - for _, e := range ec { - if e.Node1 == node || e.Node2 == node { - return false - } - } - - return true -} - -func (ec EdgeSet) Contains(edge Edge) bool { - for _, e := range ec { - if e == edge { - return true - } - } - - return false -} - -func (ec EdgeSet) FindByNodes(node1, node2 Node) (Edge, bool) { - for _, e := range ec { - if (e.Node1 == node1 && e.Node2 == node2) || (e.Node1 == node2 && e.Node2 == node1) { - return e, true - } - } - - return Edge{}, false -} - -func (ec EdgeSet) SymmetricDifference(ec2 EdgeSet) EdgeSet { - edgesToInclude := make(map[Edge]bool) - - for _, e := range ec { - edgesToInclude[e] = true - } - - for _, e := range ec2 { - edgesToInclude[e] = !edgesToInclude[e] - } - - result := EdgeSet{} - for e, include := range edgesToInclude { - if include { - result = append(result, e) - } - } - - return result -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/node/node.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/node/node.go deleted file mode 100644 index 800c2ea..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/node/node.go +++ /dev/null @@ -1,7 +0,0 @@ -package node - -type Node struct { - Id int -} - -type NodeOrderedSet []Node diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/util/util.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/util/util.go deleted file mode 100644 index a24cd27..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/util/util.go +++ /dev/null @@ -1,7 +0,0 @@ -package util - -import "math" - -func Odd(n int) bool { - return math.Mod(float64(n), 2.0) == 1.0 -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/type_support.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/type_support.go deleted file mode 100644 index ef9b448..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/type_support.go +++ /dev/null @@ -1,165 +0,0 @@ -/* -Gomega matchers - -This package implements the Gomega matchers and does not typically need to be imported. -See the docs for Gomega for documentation on the matchers - -http://onsi.github.io/gomega/ -*/ -package matchers - -import ( - "fmt" - "reflect" -) - -type omegaMatcher interface { - Match(actual interface{}) (success bool, err error) - FailureMessage(actual interface{}) (message string) - NegatedFailureMessage(actual interface{}) (message string) -} - -func isBool(a interface{}) bool { - return reflect.TypeOf(a).Kind() == reflect.Bool -} - -func isNumber(a interface{}) bool { - if a == nil { - return false - } - kind := reflect.TypeOf(a).Kind() - return reflect.Int <= kind && kind <= reflect.Float64 -} - -func isInteger(a interface{}) bool { - kind := reflect.TypeOf(a).Kind() - return reflect.Int <= kind && kind <= reflect.Int64 -} - -func isUnsignedInteger(a interface{}) bool { - kind := reflect.TypeOf(a).Kind() - return reflect.Uint <= kind && kind <= reflect.Uint64 -} - -func isFloat(a interface{}) bool { - kind := reflect.TypeOf(a).Kind() - return reflect.Float32 <= kind && kind <= reflect.Float64 -} - -func toInteger(a interface{}) int64 { - if isInteger(a) { - return reflect.ValueOf(a).Int() - } else if isUnsignedInteger(a) { - return int64(reflect.ValueOf(a).Uint()) - } else if isFloat(a) { - return int64(reflect.ValueOf(a).Float()) - } else { - panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a)) - } -} - -func toUnsignedInteger(a interface{}) uint64 { - if isInteger(a) { - return uint64(reflect.ValueOf(a).Int()) - } else if isUnsignedInteger(a) { - return reflect.ValueOf(a).Uint() - } else if isFloat(a) { - return uint64(reflect.ValueOf(a).Float()) - } else { - panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a)) - } -} - -func toFloat(a interface{}) float64 { - if isInteger(a) { - return float64(reflect.ValueOf(a).Int()) - } else if isUnsignedInteger(a) { - return float64(reflect.ValueOf(a).Uint()) - } else if isFloat(a) { - return reflect.ValueOf(a).Float() - } else { - panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a)) - } -} - -func isError(a interface{}) bool { - _, ok := a.(error) - return ok -} - -func isChan(a interface{}) bool { - if isNil(a) { - return false - } - return reflect.TypeOf(a).Kind() == reflect.Chan -} - -func isMap(a interface{}) bool { - if a == nil { - return false - } - return reflect.TypeOf(a).Kind() == reflect.Map -} - -func isArrayOrSlice(a interface{}) bool { - if a == nil { - return false - } - switch reflect.TypeOf(a).Kind() { - case reflect.Array, reflect.Slice: - return true - default: - return false - } -} - -func isString(a interface{}) bool { - if a == nil { - return false - } - return reflect.TypeOf(a).Kind() == reflect.String -} - -func toString(a interface{}) (string, bool) { - aString, isString := a.(string) - if isString { - return aString, true - } - - aBytes, isBytes := a.([]byte) - if isBytes { - return string(aBytes), true - } - - aStringer, isStringer := a.(fmt.Stringer) - if isStringer { - return aStringer.String(), true - } - - return "", false -} - -func lengthOf(a interface{}) (int, bool) { - if a == nil { - return 0, false - } - switch reflect.TypeOf(a).Kind() { - case reflect.Map, reflect.Array, reflect.String, reflect.Chan, reflect.Slice: - return reflect.ValueOf(a).Len(), true - default: - return 0, false - } -} - -func isNil(a interface{}) bool { - if a == nil { - return true - } - - switch reflect.TypeOf(a).Kind() { - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: - return reflect.ValueOf(a).IsNil() - } - - return false -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/with_transform.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/with_transform.go deleted file mode 100644 index 8e58d8a..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/with_transform.go +++ /dev/null @@ -1,72 +0,0 @@ -package matchers - -import ( - "fmt" - "reflect" - - "github.com/onsi/gomega/internal/oraclematcher" - "github.com/onsi/gomega/types" -) - -type WithTransformMatcher struct { - // input - Transform interface{} // must be a function of one parameter that returns one value - Matcher types.GomegaMatcher - - // cached value - transformArgType reflect.Type - - // state - transformedValue interface{} -} - -func NewWithTransformMatcher(transform interface{}, matcher types.GomegaMatcher) *WithTransformMatcher { - if transform == nil { - panic("transform function cannot be nil") - } - txType := reflect.TypeOf(transform) - if txType.NumIn() != 1 { - panic("transform function must have 1 argument") - } - if txType.NumOut() != 1 { - panic("transform function must have 1 return value") - } - - return &WithTransformMatcher{ - Transform: transform, - Matcher: matcher, - transformArgType: reflect.TypeOf(transform).In(0), - } -} - -func (m *WithTransformMatcher) Match(actual interface{}) (bool, error) { - // return error if actual's type is incompatible with Transform function's argument type - actualType := reflect.TypeOf(actual) - if !actualType.AssignableTo(m.transformArgType) { - return false, fmt.Errorf("Transform function expects '%s' but we have '%s'", m.transformArgType, actualType) - } - - // call the Transform function with `actual` - fn := reflect.ValueOf(m.Transform) - result := fn.Call([]reflect.Value{reflect.ValueOf(actual)}) - m.transformedValue = result[0].Interface() // expect exactly one value - - return m.Matcher.Match(m.transformedValue) -} - -func (m *WithTransformMatcher) FailureMessage(_ interface{}) (message string) { - return m.Matcher.FailureMessage(m.transformedValue) -} - -func (m *WithTransformMatcher) NegatedFailureMessage(_ interface{}) (message string) { - return m.Matcher.NegatedFailureMessage(m.transformedValue) -} - -func (m *WithTransformMatcher) MatchMayChangeInTheFuture(_ interface{}) bool { - // TODO: Maybe this should always just return true? (Only an issue for non-deterministic transformers.) - // - // Querying the next matcher is fine if the transformer always will return the same value. - // But if the transformer is non-deterministic and returns a different value each time, then there - // is no point in querying the next matcher, since it can only comment on the last transformed value. - return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue) -} diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/types/types.go b/Godeps/_workspace/src/github.com/onsi/gomega/types/types.go deleted file mode 100644 index 1c632ad..0000000 --- a/Godeps/_workspace/src/github.com/onsi/gomega/types/types.go +++ /dev/null @@ -1,17 +0,0 @@ -package types - -type GomegaFailHandler func(message string, callerSkip ...int) - -//A simple *testing.T interface wrapper -type GomegaTestingT interface { - Errorf(format string, args ...interface{}) -} - -//All Gomega matchers must implement the GomegaMatcher interface -// -//For details on writing custom matchers, check out: http://onsi.github.io/gomega/#adding_your_own_matchers -type GomegaMatcher interface { - Match(actual interface{}) (success bool, err error) - FailureMessage(actual interface{}) (message string) - NegatedFailureMessage(actual interface{}) (message string) -} From dd9de8bdfd9da494a2f1ff68d89cd726ffed39b3 Mon Sep 17 00:00:00 2001 From: Daniel Hess Date: Mon, 24 Oct 2016 21:49:58 +0000 Subject: [PATCH 2/4] adding glide files and vendor dir --- glide.lock | 23 + glide.yaml | 9 + vendor/github.com/franela/goblin/.gitignore | 22 + vendor/github.com/franela/goblin/.travis.yml | 1 + vendor/github.com/franela/goblin/LICENSE | 19 + vendor/github.com/franela/goblin/Makefile | 3 + vendor/github.com/franela/goblin/README.md | 111 ++ .../github.com/franela/goblin/assertions.go | 32 + .../franela/goblin/assertions_test.go | 30 + .../franela/goblin/describe_test.go | 181 ++ vendor/github.com/franela/goblin/goblin.go | 219 +++ .../github.com/franela/goblin/goblin_logo.jpg | Bin 0 -> 36893 bytes .../franela/goblin/goblin_output.png | Bin 0 -> 18985 bytes .../github.com/franela/goblin/goblin_test.go | 173 ++ vendor/github.com/franela/goblin/it_test.go | 181 ++ vendor/github.com/franela/goblin/reporting.go | 106 ++ .../franela/goblin/reporting_test.go | 166 ++ vendor/github.com/franela/goblin/resolver.go | 16 + .../franela/goblin/resolver_test.go | 28 + vendor/github.com/gorilla/context/.travis.yml | 19 + vendor/github.com/gorilla/context/LICENSE | 27 + vendor/github.com/gorilla/context/README.md | 10 + vendor/github.com/gorilla/context/context.go | 143 ++ .../gorilla/context/context_test.go | 161 ++ vendor/github.com/gorilla/context/doc.go | 88 + vendor/github.com/gorilla/mux/.travis.yml | 20 + vendor/github.com/gorilla/mux/LICENSE | 27 + vendor/github.com/gorilla/mux/README.md | 242 +++ vendor/github.com/gorilla/mux/bench_test.go | 49 + vendor/github.com/gorilla/mux/doc.go | 206 +++ vendor/github.com/gorilla/mux/mux.go | 481 ++++++ vendor/github.com/gorilla/mux/mux_test.go | 1453 +++++++++++++++++ vendor/github.com/gorilla/mux/old_test.go | 710 ++++++++ vendor/github.com/gorilla/mux/regexp.go | 312 ++++ vendor/github.com/gorilla/mux/route.go | 627 +++++++ vendor/github.com/onsi/gomega/.gitignore | 3 + vendor/github.com/onsi/gomega/.travis.yml | 10 + vendor/github.com/onsi/gomega/CHANGELOG.md | 49 + vendor/github.com/onsi/gomega/MIT.LICENSE | 20 + vendor/github.com/onsi/gomega/README.md | 17 + .../github.com/onsi/gomega/format/format.go | 276 ++++ .../onsi/gomega/format/format_suite_test.go | 13 + .../onsi/gomega/format/format_test.go | 449 +++++ .../github.com/onsi/gomega/gbytes/buffer.go | 204 +++ .../onsi/gomega/gbytes/buffer_test.go | 121 ++ .../onsi/gomega/gbytes/gbuffer_suite_test.go | 13 + .../onsi/gomega/gbytes/say_matcher.go | 105 ++ .../onsi/gomega/gbytes/say_matcher_test.go | 163 ++ .../gomega/gexec/_fixture/firefly/main.go | 36 + vendor/github.com/onsi/gomega/gexec/build.go | 78 + .../onsi/gomega/gexec/exit_matcher.go | 88 + .../onsi/gomega/gexec/exit_matcher_test.go | 113 ++ .../onsi/gomega/gexec/gexec_suite_test.go | 26 + .../onsi/gomega/gexec/prefixed_writer.go | 80 + .../onsi/gomega/gexec/prefixed_writer_test.go | 41 + .../github.com/onsi/gomega/gexec/session.go | 214 +++ .../onsi/gomega/gexec/session_test.go | 177 ++ .../github.com/onsi/gomega/ghttp/handlers.go | 202 +++ .../onsi/gomega/ghttp/test_server.go | 303 ++++ .../gomega/ghttp/test_server_suite_test.go | 13 + .../onsi/gomega/ghttp/test_server_test.go | 555 +++++++ vendor/github.com/onsi/gomega/gomega_dsl.go | 321 ++++ .../gomega/internal/assertion/assertion.go | 98 ++ .../assertion/assertion_suite_test.go | 13 + .../internal/assertion/assertion_test.go | 236 +++ .../asyncassertion/async_assertion.go | 197 +++ .../async_assertion_suite_test.go | 13 + .../asyncassertion/async_assertion_test.go | 315 ++++ .../internal/fakematcher/fake_matcher.go | 23 + .../testingtsupport/testing_t_support.go | 40 + .../testingtsupport/testing_t_support_test.go | 12 + vendor/github.com/onsi/gomega/matchers.go | 293 ++++ .../matchers/assignable_to_type_of_matcher.go | 30 + .../assignable_to_type_of_matcher_test.go | 30 + .../onsi/gomega/matchers/be_closed_matcher.go | 45 + .../gomega/matchers/be_closed_matcher_test.go | 70 + .../onsi/gomega/matchers/be_empty_matcher.go | 26 + .../gomega/matchers/be_empty_matcher_test.go | 52 + .../matchers/be_equivalent_to_matcher.go | 33 + .../matchers/be_equivalent_to_matcher_test.go | 50 + .../onsi/gomega/matchers/be_false_matcher.go | 25 + .../gomega/matchers/be_false_matcher_test.go | 20 + .../onsi/gomega/matchers/be_nil_matcher.go | 18 + .../gomega/matchers/be_nil_matcher_test.go | 28 + .../gomega/matchers/be_numerically_matcher.go | 119 ++ .../matchers/be_numerically_matcher_test.go | 148 ++ .../onsi/gomega/matchers/be_sent_matcher.go | 71 + .../gomega/matchers/be_sent_matcher_test.go | 106 ++ .../gomega/matchers/be_temporally_matcher.go | 65 + .../matchers/be_temporally_matcher_test.go | 98 ++ .../onsi/gomega/matchers/be_true_matcher.go | 25 + .../gomega/matchers/be_true_matcher_test.go | 20 + .../onsi/gomega/matchers/be_zero_matcher.go | 27 + .../gomega/matchers/be_zero_matcher_test.go | 30 + .../onsi/gomega/matchers/consist_of.go | 80 + .../onsi/gomega/matchers/consist_of_test.go | 75 + .../matchers/contain_element_matcher.go | 53 + .../matchers/contain_element_matcher_test.go | 72 + .../matchers/contain_substring_matcher.go | 37 + .../contain_substring_matcher_test.go | 36 + .../onsi/gomega/matchers/equal_matcher.go | 26 + .../gomega/matchers/equal_matcher_test.go | 44 + .../onsi/gomega/matchers/have_key_matcher.go | 53 + .../gomega/matchers/have_key_matcher_test.go | 73 + .../matchers/have_key_with_value_matcher.go | 73 + .../have_key_with_value_matcher_test.go | 82 + .../onsi/gomega/matchers/have_len_matcher.go | 27 + .../gomega/matchers/have_len_matcher_test.go | 53 + .../gomega/matchers/have_occurred_matcher.go | 29 + .../matchers/have_occurred_matcher_test.go | 28 + .../gomega/matchers/match_error_matcher.go | 41 + .../matchers/match_error_matcher_test.go | 80 + .../gomega/matchers/match_json_matcher.go | 61 + .../matchers/match_json_matcher_test.go | 59 + .../gomega/matchers/match_regexp_matcher.go | 42 + .../matchers/match_regexp_matcher_test.go | 44 + .../matchers/matcher_tests_suite_test.go | 29 + .../onsi/gomega/matchers/panic_matcher.go | 42 + .../gomega/matchers/panic_matcher_test.go | 36 + .../onsi/gomega/matchers/receive_matcher.go | 116 ++ .../gomega/matchers/receive_matcher_test.go | 284 ++++ .../matchers/support/goraph/MIT.LICENSE | 20 + .../goraph/bipartitegraph/bipartitegraph.go | 41 + .../bipartitegraph/bipartitegraphmatching.go | 161 ++ .../matchers/support/goraph/edge/edge.go | 61 + .../matchers/support/goraph/node/node.go | 7 + .../matchers/support/goraph/util/util.go | 7 + .../onsi/gomega/matchers/type_support.go | 165 ++ vendor/github.com/onsi/gomega/types/types.go | 17 + 129 files changed, 14115 insertions(+) create mode 100644 glide.lock create mode 100644 glide.yaml create mode 100644 vendor/github.com/franela/goblin/.gitignore create mode 100644 vendor/github.com/franela/goblin/.travis.yml create mode 100644 vendor/github.com/franela/goblin/LICENSE create mode 100644 vendor/github.com/franela/goblin/Makefile create mode 100644 vendor/github.com/franela/goblin/README.md create mode 100644 vendor/github.com/franela/goblin/assertions.go create mode 100644 vendor/github.com/franela/goblin/assertions_test.go create mode 100644 vendor/github.com/franela/goblin/describe_test.go create mode 100644 vendor/github.com/franela/goblin/goblin.go create mode 100644 vendor/github.com/franela/goblin/goblin_logo.jpg create mode 100644 vendor/github.com/franela/goblin/goblin_output.png create mode 100644 vendor/github.com/franela/goblin/goblin_test.go create mode 100644 vendor/github.com/franela/goblin/it_test.go create mode 100644 vendor/github.com/franela/goblin/reporting.go create mode 100644 vendor/github.com/franela/goblin/reporting_test.go create mode 100644 vendor/github.com/franela/goblin/resolver.go create mode 100644 vendor/github.com/franela/goblin/resolver_test.go create mode 100644 vendor/github.com/gorilla/context/.travis.yml create mode 100644 vendor/github.com/gorilla/context/LICENSE create mode 100644 vendor/github.com/gorilla/context/README.md create mode 100644 vendor/github.com/gorilla/context/context.go create mode 100644 vendor/github.com/gorilla/context/context_test.go create mode 100644 vendor/github.com/gorilla/context/doc.go create mode 100644 vendor/github.com/gorilla/mux/.travis.yml create mode 100644 vendor/github.com/gorilla/mux/LICENSE create mode 100644 vendor/github.com/gorilla/mux/README.md create mode 100644 vendor/github.com/gorilla/mux/bench_test.go create mode 100644 vendor/github.com/gorilla/mux/doc.go create mode 100644 vendor/github.com/gorilla/mux/mux.go create mode 100644 vendor/github.com/gorilla/mux/mux_test.go create mode 100644 vendor/github.com/gorilla/mux/old_test.go create mode 100644 vendor/github.com/gorilla/mux/regexp.go create mode 100644 vendor/github.com/gorilla/mux/route.go create mode 100644 vendor/github.com/onsi/gomega/.gitignore create mode 100644 vendor/github.com/onsi/gomega/.travis.yml create mode 100644 vendor/github.com/onsi/gomega/CHANGELOG.md create mode 100644 vendor/github.com/onsi/gomega/MIT.LICENSE create mode 100644 vendor/github.com/onsi/gomega/README.md create mode 100644 vendor/github.com/onsi/gomega/format/format.go create mode 100644 vendor/github.com/onsi/gomega/format/format_suite_test.go create mode 100644 vendor/github.com/onsi/gomega/format/format_test.go create mode 100644 vendor/github.com/onsi/gomega/gbytes/buffer.go create mode 100644 vendor/github.com/onsi/gomega/gbytes/buffer_test.go create mode 100644 vendor/github.com/onsi/gomega/gbytes/gbuffer_suite_test.go create mode 100644 vendor/github.com/onsi/gomega/gbytes/say_matcher.go create mode 100644 vendor/github.com/onsi/gomega/gbytes/say_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/gexec/_fixture/firefly/main.go create mode 100644 vendor/github.com/onsi/gomega/gexec/build.go create mode 100644 vendor/github.com/onsi/gomega/gexec/exit_matcher.go create mode 100644 vendor/github.com/onsi/gomega/gexec/exit_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/gexec/gexec_suite_test.go create mode 100644 vendor/github.com/onsi/gomega/gexec/prefixed_writer.go create mode 100644 vendor/github.com/onsi/gomega/gexec/prefixed_writer_test.go create mode 100644 vendor/github.com/onsi/gomega/gexec/session.go create mode 100644 vendor/github.com/onsi/gomega/gexec/session_test.go create mode 100644 vendor/github.com/onsi/gomega/ghttp/handlers.go create mode 100644 vendor/github.com/onsi/gomega/ghttp/test_server.go create mode 100644 vendor/github.com/onsi/gomega/ghttp/test_server_suite_test.go create mode 100644 vendor/github.com/onsi/gomega/ghttp/test_server_test.go create mode 100644 vendor/github.com/onsi/gomega/gomega_dsl.go create mode 100644 vendor/github.com/onsi/gomega/internal/assertion/assertion.go create mode 100644 vendor/github.com/onsi/gomega/internal/assertion/assertion_suite_test.go create mode 100644 vendor/github.com/onsi/gomega/internal/assertion/assertion_test.go create mode 100644 vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go create mode 100644 vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion_suite_test.go create mode 100644 vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion_test.go create mode 100644 vendor/github.com/onsi/gomega/internal/fakematcher/fake_matcher.go create mode 100644 vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go create mode 100644 vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers.go create mode 100644 vendor/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_closed_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_closed_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_empty_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_empty_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_equivalent_to_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_equivalent_to_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_false_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_false_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_nil_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_nil_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_numerically_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_numerically_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_sent_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_sent_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_temporally_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_temporally_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_true_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_true_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_zero_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/be_zero_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/consist_of.go create mode 100644 vendor/github.com/onsi/gomega/matchers/consist_of_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/contain_element_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/contain_element_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/contain_substring_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/contain_substring_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/equal_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/equal_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/have_key_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/have_key_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/have_len_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/have_len_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/have_occurred_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/have_occurred_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/match_error_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/match_error_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/match_json_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/match_json_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/match_regexp_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/match_regexp_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/matcher_tests_suite_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/panic_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/panic_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/receive_matcher.go create mode 100644 vendor/github.com/onsi/gomega/matchers/receive_matcher_test.go create mode 100644 vendor/github.com/onsi/gomega/matchers/support/goraph/MIT.LICENSE create mode 100644 vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraph.go create mode 100644 vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go create mode 100644 vendor/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go create mode 100644 vendor/github.com/onsi/gomega/matchers/support/goraph/node/node.go create mode 100644 vendor/github.com/onsi/gomega/matchers/support/goraph/util/util.go create mode 100644 vendor/github.com/onsi/gomega/matchers/type_support.go create mode 100644 vendor/github.com/onsi/gomega/types/types.go diff --git a/glide.lock b/glide.lock new file mode 100644 index 0000000..e486b82 --- /dev/null +++ b/glide.lock @@ -0,0 +1,23 @@ +hash: 729e94f26cc65c79258bb8d8b337b8c6ff729983725e74b3e5ac0fd5105bcee2 +updated: 2016-10-24T21:49:30.771065249Z +imports: +- name: github.com/gorilla/context + version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 +- name: github.com/gorilla/mux + version: 0eeaf8392f5b04950925b8a69fe70f110fa7cbfc +testImports: +- name: github.com/franela/goblin + version: 74c9fe110d4bfd04c222a089a309e0a97e258534 +- name: github.com/onsi/gomega + version: a78ae492d53aad5a7a232d0d0462c14c400e3ee7 + subpackages: + - format + - internal/assertion + - internal/asyncassertion + - internal/testingtsupport + - matchers + - matchers/support/goraph/bipartitegraph + - matchers/support/goraph/edge + - matchers/support/goraph/node + - matchers/support/goraph/util + - types diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..7e7524b --- /dev/null +++ b/glide.yaml @@ -0,0 +1,9 @@ +package: github.com/gomicro/steward +import: +- package: github.com/gorilla/mux + version: v1.1 +testImport: +- package: github.com/franela/goblin + version: 0.0.1 +- package: github.com/onsi/gomega + version: v1.0 diff --git a/vendor/github.com/franela/goblin/.gitignore b/vendor/github.com/franela/goblin/.gitignore new file mode 100644 index 0000000..0026861 --- /dev/null +++ b/vendor/github.com/franela/goblin/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/vendor/github.com/franela/goblin/.travis.yml b/vendor/github.com/franela/goblin/.travis.yml new file mode 100644 index 0000000..4f2ee4d --- /dev/null +++ b/vendor/github.com/franela/goblin/.travis.yml @@ -0,0 +1 @@ +language: go diff --git a/vendor/github.com/franela/goblin/LICENSE b/vendor/github.com/franela/goblin/LICENSE new file mode 100644 index 0000000..b2d6522 --- /dev/null +++ b/vendor/github.com/franela/goblin/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013 Marcos Lilljedahl and Jonathan Leibiusky + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/franela/goblin/Makefile b/vendor/github.com/franela/goblin/Makefile new file mode 100644 index 0000000..66763dc --- /dev/null +++ b/vendor/github.com/franela/goblin/Makefile @@ -0,0 +1,3 @@ +export GOPATH=$(shell pwd) +test: + go test -v diff --git a/vendor/github.com/franela/goblin/README.md b/vendor/github.com/franela/goblin/README.md new file mode 100644 index 0000000..72ea681 --- /dev/null +++ b/vendor/github.com/franela/goblin/README.md @@ -0,0 +1,111 @@ +[![Build Status](https://travis-ci.org/franela/goblin.png?branch=master)](https://travis-ci.org/franela/goblin) +Goblin +====== + +![](https://github.com/marcosnils/goblin/blob/master/goblin_logo.jpg?raw=true) + +A [Mocha](http://visionmedia.github.io/mocha/) like BDD testing framework for Go + +No extensive documentation nor complicated steps to get it running + +Run tests as usual with `go test` + +Colorful reports and beautiful syntax + + +Why Goblin? +----------- + +Inspired by the flexibility and simplicity of Node BDD and frustrated by the +rigorousness of Go way of testing, we wanted to bring a new tool to +write self-describing and comprehensive code. + + + +What do I get with it? +---------------------- + +- Preserve the exact same syntax and behaviour as Node's Mocha +- Nest as many `Describe` and `It` blocks as you want +- Use `Before`, `BeforeEach`, `After` and `AfterEach` for setup and teardown your tests +- No need to remember confusing parameters in `Describe` and `It` blocks +- Use a declarative and expressive language to write your tests +- Plug different assertion libraries ([Gomega](https://github.com/onsi/gomega) supported so far) +- Skip your tests the same way as you would do in Mocha +- Two line setup is all you need to get up running + + + +How do I use it? +---------------- + +Since ```go test``` is not currently extensive, you will have to hook Goblin to it. You do that by +adding a single test method in your test file. All your goblin tests will be implemented inside this function. + +```go +package foobar + +import ( + "testing" + . "goblin" +) + +func Test(t *testing.T) { + g := Goblin(t) + g.Describe("Numbers", func() { + g.It("Should add two numbers ", func() { + g.Assert(1+1).Equal(2) + }) + g.It("Should match equal numbers", func() { + g.Assert(2).Equal(4) + }) + g.It("Should substract two numbers") + }) +} +``` + +Ouput will be something like: + +![](https://github.com/marcosnils/goblin/blob/master/goblin_output.png?raw=true) + +Nice and easy, right? + +How do I use it with Gomega? +---------------------------- + +Gomega is a nice assertion framework. But it doesn't provide a nice way to hook it to testing frameworks. It should just panic instead of requiring a fail function. There is an issue about that [here](https://github.com/onsi/gomega/issues/5). +While this is being discussed and hopefully fixed, the way to use Gomega with Goblin is: + +```go +package foobar + +import ( + "testing" + . "goblin" + . "github.com/onsi/gomega" +) + +func Test(t *testing.T) { + g := Goblin(t) + + //special hook for gomega + RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) + + g.Describe("lala", func() { + g.It("lslslslsls", func() { + Expect(1).To(Equal(10)) + }) + }) +} +``` + +TODO: +----- + +We do have a couple of [issues](https://github.com/franela/goblin/issues) pending we'll be addressing soon. But feel free to +contribute and send us PRs (with tests please :smile:). + +Contributions: +------------ + +Special thanks to [Leandro Reox](https://github.com/leandroreox) (Leitan) for the goblin logo. diff --git a/vendor/github.com/franela/goblin/assertions.go b/vendor/github.com/franela/goblin/assertions.go new file mode 100644 index 0000000..055d491 --- /dev/null +++ b/vendor/github.com/franela/goblin/assertions.go @@ -0,0 +1,32 @@ +package goblin + +import ( + "reflect" + "fmt" +) + +type Assertion struct { + src interface{} +} + +func objectsAreEqual(a, b interface{}) bool { + if reflect.DeepEqual(a, b) { + return true + } + + if reflect.ValueOf(a) == reflect.ValueOf(b) { + return true + } + + if fmt.Sprintf("%#v", a) == fmt.Sprintf("%#v", b) { + return true + } + + return false +} + +func (a *Assertion) Equal(dst interface{}) { + if !objectsAreEqual(a.src, dst) { + panic(fmt.Sprintf("%v", a.src)+" does not equal "+fmt.Sprintf("%v", dst)) + } +} diff --git a/vendor/github.com/franela/goblin/assertions_test.go b/vendor/github.com/franela/goblin/assertions_test.go new file mode 100644 index 0000000..3de1579 --- /dev/null +++ b/vendor/github.com/franela/goblin/assertions_test.go @@ -0,0 +1,30 @@ +package goblin + +import ( + "testing" +) + +var failed = false + +func TestEqual(t *testing.T) { + a := Assertion{src: 1} + a.Equal(1) + + if failed { + t.FailNow() + } + + a = Assertion{src: "foo"} + a.Equal("foo") + + if failed { + t.FailNow() + } + + a = Assertion{src: map[string]string{"foo": "bar"}} + a.Equal(map[string]string{"foo": "bar"}) + + if failed { + t.FailNow() + } +} diff --git a/vendor/github.com/franela/goblin/describe_test.go b/vendor/github.com/franela/goblin/describe_test.go new file mode 100644 index 0000000..fcd4163 --- /dev/null +++ b/vendor/github.com/franela/goblin/describe_test.go @@ -0,0 +1,181 @@ +package goblin + +import ( + "testing" +) + +func TestBefore(t *testing.T) { + fakeTest := testing.T{} + + g := Goblin(&fakeTest) + + g.Describe("Numbers", func() { + before := 0 + + g.Before(func() { + before++ + }) + + g.It("Should have called before", func() { + g.Assert(before).Equal(1) + }) + + g.It("Should have called before only once", func() { + g.Assert(before).Equal(1) + }) + }) + + + if fakeTest.Failed() { + t.Fatal() + } +} + +func TestMultipleBefore(t *testing.T) { + fakeTest := testing.T{} + + + g := Goblin(&fakeTest) + + g.Describe("Numbers", func() { + before := 0 + + g.Before(func() { + before++ + }) + + g.Before(func() { + before++ + }) + + g.It("Should have called all the registered before", func() { + g.Assert(before).Equal(2) + }) + }) + + + if fakeTest.Failed() { + t.Fatal() + } +} + +func TestNestedBefore(t *testing.T) { + fakeTest := testing.T{} + + g := Goblin(&fakeTest) + + g.Describe("Numbers", func() { + before := 0 + + g.Before(func() { + before++ + }) + + g.Describe("Addition", func() { + g.Before(func() { + before++ + }) + + g.It("Should have called all the registered before", func() { + g.Assert(before).Equal(2) + }) + + g.It("Should have called all the registered before only once", func() { + g.Assert(before).Equal(2) + }) + }) + + }) + + + if fakeTest.Failed() { + t.Fatal() + } +} + + +func TestAfter(t *testing.T) { + fakeTest := testing.T{} + + g := Goblin(&fakeTest) + after := 0 + g.Describe("Numbers", func() { + + g.After(func() { + after++ + }) + + g.It("Should call after only once", func() { + g.Assert(after).Equal(0) + }) + + g.It("Should call after only once", func() { + g.Assert(after).Equal(0) + }) + }) + + + if fakeTest.Failed() || after != 1 { + t.Fatal() + } +} + +func TestMultipleAfter(t *testing.T) { + fakeTest := testing.T{} + + g := Goblin(&fakeTest) + + after := 0 + g.Describe("Numbers", func() { + + g.After(func() { + after++ + }) + + g.After(func() { + after++ + }) + + g.It("Should call all the registered after", func() { + g.Assert(after).Equal(0) + }) + }) + + + if fakeTest.Failed() && after != 2 { + t.Fatal() + } +} + +func TestNestedAfter(t *testing.T) { + fakeTest := testing.T{} + + g := Goblin(&fakeTest) + after := 0 + g.Describe("Numbers", func() { + + g.After(func() { + after++ + }) + + g.Describe("Addition", func() { + g.After(func() { + after++ + }) + + g.It("Should call all the registered after", func() { + g.Assert(after).Equal(0) + }) + + g.It("Should have called all the registered after only once", func() { + g.Assert(after).Equal(0) + }) + }) + + }) + + + if fakeTest.Failed() || after != 2 { + t.Fatal() + } +} diff --git a/vendor/github.com/franela/goblin/goblin.go b/vendor/github.com/franela/goblin/goblin.go new file mode 100644 index 0000000..43f7191 --- /dev/null +++ b/vendor/github.com/franela/goblin/goblin.go @@ -0,0 +1,219 @@ +package goblin + +import ( + "testing" + "time" +) + +type Runnable interface { + run(*G) (bool) +} + +func (g *G) Describe(name string, h func()) { + d := &Describe{name:name, h:h, parent:g.parent} + + if d.parent != nil { + d.parent.children = append(d.parent.children, Runnable(d)) + } + + g.parent = d + + h() + + g.parent = d.parent + + if g.parent == nil { + g.reporter.begin() + if d.run(g) { + g.t.Fail() + } + g.reporter.end() + } +} + +type Describe struct { + name string + h func() + children []Runnable + befores []func() + afters []func() + afterEach []func() + beforeEach []func() + hasTests bool + parent *Describe +} + +func (d *Describe) runBeforeEach() { + if d.parent != nil { + d.parent.runBeforeEach() + } + + for _, b := range d.beforeEach { + b() + } +} + +func (d *Describe) runAfterEach() { + + if d.parent != nil { + d.parent.runAfterEach() + } + + for _, a := range d.afterEach { + a() + } +} + + +func (d *Describe) run(g *G) (bool) { + g.reporter.beginDescribe(d.name) + + failed := "" + + if d.hasTests { + for _, b := range d.befores { + b() + } + } + + for _, r := range d.children { + if r.run(g) { + failed = "true" + } + } + + if d.hasTests { + for _, a := range d.afters { + a() + } + } + + g.reporter.endDescribe() + + return failed != "" +} + +type Failure struct { + file string + line int + testName string + message string +} + +type It struct { + h func() + name string + parent *Describe + failure *Failure + reporter Reporter +} + +func (it *It) run(g *G) (bool) { + g.currentIt = it + + if it.h == nil { + g.reporter.itIsPending(it.name) + return false + } + //TODO: should handle errors for beforeEach + it.parent.runBeforeEach() + + runIt(g, it.h) + + it.parent.runAfterEach() + + failed := false + if it.failure != nil { + failed = true + } + + if failed { + g.reporter.itFailed(it.name) + g.reporter.failure(it.failure) + } else { + g.reporter.itPassed(it.name) + } + return failed +} + +func (it *It) failed(msg, file string, line int) { + it.failure = &Failure{file:file, line:line, message:msg, testName: it.parent.name + " " + it.name} +} + +func Goblin (t *testing.T) (*G) { + g := &G{t: t} + g.reporter = Reporter(&DetailedReporter{}) + return g +} + + +func runIt (g *G, h func()) { + defer timeTrack(time.Now(), g) + + // We do this to recover from panic, which is how we know that the test failed. + defer func() { + if r := recover(); r != nil { + file, line := ResolveCaller() + e := r.(string) + g.currentIt.failed(e, file, line) + } + }() + h() +} + + +type G struct { + t *testing.T + parent *Describe + currentIt *It + reporter Reporter +} + +func (g *G) SetReporter(r Reporter) { + g.reporter = r +} + +func (g *G) It(name string, h ...func()) { + it := &It{name:name, parent:g.parent, reporter: g.reporter} + notifyParents(g.parent) + if len(h) > 0 { + it.h = h[0] + } + g.parent.children = append(g.parent.children, Runnable(it)) +} + +func notifyParents(d *Describe) { + d.hasTests = true + if d.parent != nil { + notifyParents(d.parent) + } +} + +func (g *G) Before(h func()) { + g.parent.befores = append(g.parent.befores, h) +} + +func (g *G) BeforeEach(h func()) { + g.parent.beforeEach = append(g.parent.beforeEach, h) +} + +func (g *G) After(h func()) { + g.parent.afters = append(g.parent.afters, h) +} + +func (g *G) AfterEach(h func()) { + g.parent.afterEach = append(g.parent.afterEach, h) +} + +func (g *G) Assert(src interface{}) (*Assertion) { + return &Assertion{src: src} +} + + +func timeTrack(start time.Time, g *G) { + g.reporter.itTook(time.Since(start)) +} + +func (g *G) Fail(message string) { + panic(message) +} diff --git a/vendor/github.com/franela/goblin/goblin_logo.jpg b/vendor/github.com/franela/goblin/goblin_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..44534f297b14af552cc040a32e29a85892d364a2 GIT binary patch literal 36893 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
  • +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 W|I)^Hm^PjO0000 0 { + fmt.Printf(" \033[36m%d test(s) pending\033[0m\n\n", r.pending) + } + + if len(r.failures) > 0 { + fmt.Printf("%s \n\n", red(fmt.Sprintf(" %d tests failed:", len(r.failures)))) + + } + + for i, failure := range r.failures { + fmt.Printf(" %d) %s:\n\n", i+1, failure.testName) + fmt.Printf(" %s %s\n\n", red(failure.message), gray(fmt.Sprintf("(%s:%d)", failure.file, failure.line))) + } +} diff --git a/vendor/github.com/franela/goblin/reporting_test.go b/vendor/github.com/franela/goblin/reporting_test.go new file mode 100644 index 0000000..715106c --- /dev/null +++ b/vendor/github.com/franela/goblin/reporting_test.go @@ -0,0 +1,166 @@ +package goblin + +import ( + "testing" + "reflect" + "time" +) + +type FakeReporter struct { + describes []string + fails []string + passes []string + pending []string + ends int + failures int + executionTime time.Duration + totalExecutionTime time.Duration + beginFlag, endFlag bool +} + +func (r *FakeReporter) beginDescribe(name string) { + r.describes = append(r.describes, name) +} + +func (r *FakeReporter) endDescribe() { + r.ends++ +} + +func (r *FakeReporter) failure(failure *Failure) { + r.failures++; +} + +func (r *FakeReporter) itFailed(name string) { + r.fails = append(r.fails, name) +} + +func (r *FakeReporter) itPassed(name string) { + r.passes = append(r.passes, name) +} + +func (r *FakeReporter) itIsPending(name string) { + r.pending = append(r.pending, name) +} + +func (r *FakeReporter) itTook(duration time.Duration) { + r.executionTime = duration + r.totalExecutionTime += duration +} + +func (r *FakeReporter) begin() { + r.beginFlag = true +} + +func (r *FakeReporter) end() { + r.endFlag = true +} + +func TestReporting(t *testing.T) { + fakeTest := &testing.T{} + reporter := FakeReporter{} + fakeReporter := Reporter(&reporter) + + g := Goblin(fakeTest) + g.SetReporter(fakeReporter) + + g.Describe("One", func() { + g.It("Foo", func() { + g.Assert(0).Equal(1) + }) + g.Describe("Two", func() { + g.It("Bar", func() { + g.Assert(0).Equal(0) + }) + }) + }) + + + if !reflect.DeepEqual(reporter.describes, []string{"One", "Two"}) { + t.FailNow() + } + if !reflect.DeepEqual(reporter.fails, []string{"Foo"}) { + t.FailNow() + } + if !reflect.DeepEqual(reporter.passes, []string{"Bar"}) { + t.FailNow() + } + if reporter.ends != 2 { + t.FailNow() + } + + if !reporter.beginFlag || !reporter.endFlag { + t.FailNow() + } +} + + +func TestReportingTime(t *testing.T) { + fakeTest := &testing.T{} + reporter := FakeReporter{} + fakeReporter := Reporter(&reporter) + + g := Goblin(fakeTest) + g.SetReporter(fakeReporter) + + g.Describe("One", func() { + g.AfterEach(func() { + //TODO: Make this an assertion + if int64(reporter.executionTime / time.Millisecond) < 5 || int64(reporter.executionTime / time.Millisecond) >= 6 { + t.FailNow() + } + }) + g.It("Foo", func() { + time.Sleep(5 * time.Millisecond) + }) + g.Describe("Two", func() { + g.It("Bar", func() { + time.Sleep(5 * time.Millisecond) + }) + }) + }) + + if int64(reporter.totalExecutionTime / time.Millisecond) < 10 { + t.FailNow() + } +} + +func TestReportingPending(t *testing.T) { + fakeTest := &testing.T{} + reporter := FakeReporter{} + fakeReporter := Reporter(&reporter) + + g := Goblin(fakeTest) + g.SetReporter(fakeReporter) + + g.Describe("One", func() { + g.It("One") + g.Describe("Two", func() { + g.It("Two") + }) + }) + + if !reflect.DeepEqual(reporter.pending, []string{"One", "Two"}) { + t.FailNow() + } +} + + +func TestReportingErrors(t *testing.T) { + fakeTest := &testing.T{} + reporter := FakeReporter{} + fakeReporter := Reporter(&reporter) + + g := Goblin(fakeTest) + g.SetReporter(fakeReporter) + + g.Describe("Numbers", func() { + g.It("Should make reporting add two errors ", func() { + g.Assert(0).Equal(1) + g.Assert(0).Equal(1) + }) + }) + + if reporter.failures != 1 { + t.FailNow() + } +} diff --git a/vendor/github.com/franela/goblin/resolver.go b/vendor/github.com/franela/goblin/resolver.go new file mode 100644 index 0000000..3934f78 --- /dev/null +++ b/vendor/github.com/franela/goblin/resolver.go @@ -0,0 +1,16 @@ +package goblin + +import ( + "runtime" + "strings" +) + +func ResolveCaller() (string, int) { + var filename string + var line int + + for depth:=0; !strings.HasSuffix(filename, "_test.go"); depth++ { + _, filename, line, _ = runtime.Caller(depth) + } + return filename, line +} diff --git a/vendor/github.com/franela/goblin/resolver_test.go b/vendor/github.com/franela/goblin/resolver_test.go new file mode 100644 index 0000000..a924339 --- /dev/null +++ b/vendor/github.com/franela/goblin/resolver_test.go @@ -0,0 +1,28 @@ +package goblin + +import ( + "testing" + "os" + "runtime" +) + +func TestResolver(t *testing.T) { + + g := Goblin(t) + + g.Describe("Resolver", func() { + + g.It("Should resolve the caller filename ", func() { + file, _:= ResolveCaller() + cwd, _ := os.Getwd() + g.Assert(file).Equal(cwd+"/resolver_test.go") + }) + + g.It("Should resolve the caller line ", func() { + _, _, currentLine, _ := runtime.Caller(0) + _, line:= ResolveCaller() + g.Assert(line).Equal(currentLine+1) + }) + + }) +} diff --git a/vendor/github.com/gorilla/context/.travis.yml b/vendor/github.com/gorilla/context/.travis.yml new file mode 100644 index 0000000..6f440f1 --- /dev/null +++ b/vendor/github.com/gorilla/context/.travis.yml @@ -0,0 +1,19 @@ +language: go +sudo: false + +matrix: + include: + - go: 1.3 + - go: 1.4 + - go: 1.5 + - go: 1.6 + - go: 1.7 + - go: tip + allow_failures: + - go: tip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go vet $(go list ./... | grep -v /vendor/) + - go test -v -race ./... diff --git a/vendor/github.com/gorilla/context/LICENSE b/vendor/github.com/gorilla/context/LICENSE new file mode 100644 index 0000000..0e5fb87 --- /dev/null +++ b/vendor/github.com/gorilla/context/LICENSE @@ -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. diff --git a/vendor/github.com/gorilla/context/README.md b/vendor/github.com/gorilla/context/README.md new file mode 100644 index 0000000..08f8669 --- /dev/null +++ b/vendor/github.com/gorilla/context/README.md @@ -0,0 +1,10 @@ +context +======= +[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context) + +gorilla/context is a general purpose registry for global request variables. + +> Note: gorilla/context, having been born well before `context.Context` existed, does not play well +> with the shallow copying of the request that [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) (added to net/http Go 1.7 onwards) performs. You should either use *just* gorilla/context, or moving forward, the new `http.Request.Context()`. + +Read the full documentation here: http://www.gorillatoolkit.org/pkg/context diff --git a/vendor/github.com/gorilla/context/context.go b/vendor/github.com/gorilla/context/context.go new file mode 100644 index 0000000..81cb128 --- /dev/null +++ b/vendor/github.com/gorilla/context/context.go @@ -0,0 +1,143 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context + +import ( + "net/http" + "sync" + "time" +) + +var ( + mutex sync.RWMutex + data = make(map[*http.Request]map[interface{}]interface{}) + datat = make(map[*http.Request]int64) +) + +// Set stores a value for a given key in a given request. +func Set(r *http.Request, key, val interface{}) { + mutex.Lock() + if data[r] == nil { + data[r] = make(map[interface{}]interface{}) + datat[r] = time.Now().Unix() + } + data[r][key] = val + mutex.Unlock() +} + +// Get returns a value stored for a given key in a given request. +func Get(r *http.Request, key interface{}) interface{} { + mutex.RLock() + if ctx := data[r]; ctx != nil { + value := ctx[key] + mutex.RUnlock() + return value + } + mutex.RUnlock() + return nil +} + +// GetOk returns stored value and presence state like multi-value return of map access. +func GetOk(r *http.Request, key interface{}) (interface{}, bool) { + mutex.RLock() + if _, ok := data[r]; ok { + value, ok := data[r][key] + mutex.RUnlock() + return value, ok + } + mutex.RUnlock() + return nil, false +} + +// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests. +func GetAll(r *http.Request) map[interface{}]interface{} { + mutex.RLock() + if context, ok := data[r]; ok { + result := make(map[interface{}]interface{}, len(context)) + for k, v := range context { + result[k] = v + } + mutex.RUnlock() + return result + } + mutex.RUnlock() + return nil +} + +// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if +// the request was registered. +func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { + mutex.RLock() + context, ok := data[r] + result := make(map[interface{}]interface{}, len(context)) + for k, v := range context { + result[k] = v + } + mutex.RUnlock() + return result, ok +} + +// Delete removes a value stored for a given key in a given request. +func Delete(r *http.Request, key interface{}) { + mutex.Lock() + if data[r] != nil { + delete(data[r], key) + } + mutex.Unlock() +} + +// Clear removes all values stored for a given request. +// +// This is usually called by a handler wrapper to clean up request +// variables at the end of a request lifetime. See ClearHandler(). +func Clear(r *http.Request) { + mutex.Lock() + clear(r) + mutex.Unlock() +} + +// clear is Clear without the lock. +func clear(r *http.Request) { + delete(data, r) + delete(datat, r) +} + +// Purge removes request data stored for longer than maxAge, in seconds. +// It returns the amount of requests removed. +// +// If maxAge <= 0, all request data is removed. +// +// This is only used for sanity check: in case context cleaning was not +// properly set some request data can be kept forever, consuming an increasing +// amount of memory. In case this is detected, Purge() must be called +// periodically until the problem is fixed. +func Purge(maxAge int) int { + mutex.Lock() + count := 0 + if maxAge <= 0 { + count = len(data) + data = make(map[*http.Request]map[interface{}]interface{}) + datat = make(map[*http.Request]int64) + } else { + min := time.Now().Unix() - int64(maxAge) + for r := range data { + if datat[r] < min { + clear(r) + count++ + } + } + } + mutex.Unlock() + return count +} + +// ClearHandler wraps an http.Handler and clears request values at the end +// of a request lifetime. +func ClearHandler(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer Clear(r) + h.ServeHTTP(w, r) + }) +} diff --git a/vendor/github.com/gorilla/context/context_test.go b/vendor/github.com/gorilla/context/context_test.go new file mode 100644 index 0000000..d70e91a --- /dev/null +++ b/vendor/github.com/gorilla/context/context_test.go @@ -0,0 +1,161 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context + +import ( + "net/http" + "testing" +) + +type keyType int + +const ( + key1 keyType = iota + key2 +) + +func TestContext(t *testing.T) { + assertEqual := func(val interface{}, exp interface{}) { + if val != exp { + t.Errorf("Expected %v, got %v.", exp, val) + } + } + + r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + + // Get() + assertEqual(Get(r, key1), nil) + + // Set() + Set(r, key1, "1") + assertEqual(Get(r, key1), "1") + assertEqual(len(data[r]), 1) + + Set(r, key2, "2") + assertEqual(Get(r, key2), "2") + assertEqual(len(data[r]), 2) + + //GetOk + value, ok := GetOk(r, key1) + assertEqual(value, "1") + assertEqual(ok, true) + + value, ok = GetOk(r, "not exists") + assertEqual(value, nil) + assertEqual(ok, false) + + Set(r, "nil value", nil) + value, ok = GetOk(r, "nil value") + assertEqual(value, nil) + assertEqual(ok, true) + + // GetAll() + values := GetAll(r) + assertEqual(len(values), 3) + + // GetAll() for empty request + values = GetAll(emptyR) + if values != nil { + t.Error("GetAll didn't return nil value for invalid request") + } + + // GetAllOk() + values, ok = GetAllOk(r) + assertEqual(len(values), 3) + assertEqual(ok, true) + + // GetAllOk() for empty request + values, ok = GetAllOk(emptyR) + assertEqual(len(values), 0) + assertEqual(ok, false) + + // Delete() + Delete(r, key1) + assertEqual(Get(r, key1), nil) + assertEqual(len(data[r]), 2) + + Delete(r, key2) + assertEqual(Get(r, key2), nil) + assertEqual(len(data[r]), 1) + + // Clear() + Clear(r) + assertEqual(len(data), 0) +} + +func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) { + <-wait + for i := 0; i < iterations; i++ { + Get(r, key) + } + done <- struct{}{} + +} + +func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) { + <-wait + for i := 0; i < iterations; i++ { + Set(r, key, value) + } + done <- struct{}{} + +} + +func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) { + + b.StopTimer() + r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + done := make(chan struct{}) + b.StartTimer() + + for i := 0; i < b.N; i++ { + wait := make(chan struct{}) + + for i := 0; i < numReaders; i++ { + go parallelReader(r, "test", iterations, wait, done) + } + + for i := 0; i < numWriters; i++ { + go parallelWriter(r, "test", "123", iterations, wait, done) + } + + close(wait) + + for i := 0; i < numReaders+numWriters; i++ { + <-done + } + + } + +} + +func BenchmarkMutexSameReadWrite1(b *testing.B) { + benchmarkMutex(b, 1, 1, 32) +} +func BenchmarkMutexSameReadWrite2(b *testing.B) { + benchmarkMutex(b, 2, 2, 32) +} +func BenchmarkMutexSameReadWrite4(b *testing.B) { + benchmarkMutex(b, 4, 4, 32) +} +func BenchmarkMutex1(b *testing.B) { + benchmarkMutex(b, 2, 8, 32) +} +func BenchmarkMutex2(b *testing.B) { + benchmarkMutex(b, 16, 4, 64) +} +func BenchmarkMutex3(b *testing.B) { + benchmarkMutex(b, 1, 2, 128) +} +func BenchmarkMutex4(b *testing.B) { + benchmarkMutex(b, 128, 32, 256) +} +func BenchmarkMutex5(b *testing.B) { + benchmarkMutex(b, 1024, 2048, 64) +} +func BenchmarkMutex6(b *testing.B) { + benchmarkMutex(b, 2048, 1024, 512) +} diff --git a/vendor/github.com/gorilla/context/doc.go b/vendor/github.com/gorilla/context/doc.go new file mode 100644 index 0000000..448d1bf --- /dev/null +++ b/vendor/github.com/gorilla/context/doc.go @@ -0,0 +1,88 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package context stores values shared during a request lifetime. + +Note: gorilla/context, having been born well before `context.Context` existed, +does not play well > with the shallow copying of the request that +[`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) +(added to net/http Go 1.7 onwards) performs. You should either use *just* +gorilla/context, or moving forward, the new `http.Request.Context()`. + +For example, a router can set variables extracted from the URL and later +application handlers can access those values, or it can be used to store +sessions values to be saved at the end of a request. There are several +others common uses. + +The idea was posted by Brad Fitzpatrick to the go-nuts mailing list: + + http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53 + +Here's the basic usage: first define the keys that you will need. The key +type is interface{} so a key can be of any type that supports equality. +Here we define a key using a custom int type to avoid name collisions: + + package foo + + import ( + "github.com/gorilla/context" + ) + + type key int + + const MyKey key = 0 + +Then set a variable. Variables are bound to an http.Request object, so you +need a request instance to set a value: + + context.Set(r, MyKey, "bar") + +The application can later access the variable using the same key you provided: + + func MyHandler(w http.ResponseWriter, r *http.Request) { + // val is "bar". + val := context.Get(r, foo.MyKey) + + // returns ("bar", true) + val, ok := context.GetOk(r, foo.MyKey) + // ... + } + +And that's all about the basic usage. We discuss some other ideas below. + +Any type can be stored in the context. To enforce a given type, make the key +private and wrap Get() and Set() to accept and return values of a specific +type: + + type key int + + const mykey key = 0 + + // GetMyKey returns a value for this package from the request values. + func GetMyKey(r *http.Request) SomeType { + if rv := context.Get(r, mykey); rv != nil { + return rv.(SomeType) + } + return nil + } + + // SetMyKey sets a value for this package in the request values. + func SetMyKey(r *http.Request, val SomeType) { + context.Set(r, mykey, val) + } + +Variables must be cleared at the end of a request, to remove all values +that were stored. This can be done in an http.Handler, after a request was +served. Just call Clear() passing the request: + + context.Clear(r) + +...or use ClearHandler(), which conveniently wraps an http.Handler to clear +variables at the end of a request lifetime. + +The Routers from the packages gorilla/mux and gorilla/pat call Clear() +so if you are using either of them you don't need to clear the context manually. +*/ +package context diff --git a/vendor/github.com/gorilla/mux/.travis.yml b/vendor/github.com/gorilla/mux/.travis.yml new file mode 100644 index 0000000..4dcdacb --- /dev/null +++ b/vendor/github.com/gorilla/mux/.travis.yml @@ -0,0 +1,20 @@ +language: go +sudo: false + +matrix: + include: + - go: 1.2 + - go: 1.3 + - go: 1.4 + - go: 1.5 + - go: 1.6 + - go: tip + +install: + - go get golang.org/x/tools/cmd/vet + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go tool vet . + - go test -v -race ./... diff --git a/vendor/github.com/gorilla/mux/LICENSE b/vendor/github.com/gorilla/mux/LICENSE new file mode 100644 index 0000000..0e5fb87 --- /dev/null +++ b/vendor/github.com/gorilla/mux/LICENSE @@ -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. diff --git a/vendor/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md new file mode 100644 index 0000000..9516c51 --- /dev/null +++ b/vendor/github.com/gorilla/mux/README.md @@ -0,0 +1,242 @@ +mux +=== +[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) +[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux) + +http://www.gorillatoolkit.org/pkg/mux + +Package `gorilla/mux` implements a request router and dispatcher. + +The name mux stands for "HTTP request multiplexer". Like the standard `http.ServeMux`, `mux.Router` matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are: + +* Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers. +* URL hosts and paths can have variables with an optional regular expression. +* Registered URLs can be built, or "reversed", which helps maintaining references to resources. +* Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching. +* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`. + +Let's start registering a couple of URL paths and handlers: + +```go +func main() { + r := mux.NewRouter() + r.HandleFunc("/", HomeHandler) + r.HandleFunc("/products", ProductsHandler) + r.HandleFunc("/articles", ArticlesHandler) + http.Handle("/", r) +} +``` + +Here we register three routes mapping URL paths to handlers. This is equivalent to how `http.HandleFunc()` works: if an incoming request URL matches one of the paths, the corresponding handler is called passing (`http.ResponseWriter`, `*http.Request`) as parameters. + +Paths can have variables. They are defined using the format `{name}` or `{name:pattern}`. If a regular expression pattern is not defined, the matched variable will be anything until the next slash. For example: + +```go +r := mux.NewRouter() +r.HandleFunc("/products/{key}", ProductHandler) +r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) +r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) +``` + +The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`: + +```go +vars := mux.Vars(request) +category := vars["category"] +``` + +And this is all you need to know about the basic usage. More advanced options are explained below. + +Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables: + +```go +r := mux.NewRouter() +// Only matches if domain is "www.example.com". +r.Host("www.example.com") +// Matches a dynamic subdomain. +r.Host("{subdomain:[a-z]+}.domain.com") +``` + +There are several other matchers that can be added. To match path prefixes: + +```go +r.PathPrefix("/products/") +``` + +...or HTTP methods: + +```go +r.Methods("GET", "POST") +``` + +...or URL schemes: + +```go +r.Schemes("https") +``` + +...or header values: + +```go +r.Headers("X-Requested-With", "XMLHttpRequest") +``` + +...or query values: + +```go +r.Queries("key", "value") +``` + +...or to use a custom matcher function: + +```go +r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { + return r.ProtoMajor == 0 +}) +``` + +...and finally, it is possible to combine several matchers in a single route: + +```go +r.HandleFunc("/products", ProductsHandler). + Host("www.example.com"). + Methods("GET"). + Schemes("http") +``` + +Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting". + +For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it: + +```go +r := mux.NewRouter() +s := r.Host("www.example.com").Subrouter() +``` + +Then register routes in the subrouter: + +```go +s.HandleFunc("/products/", ProductsHandler) +s.HandleFunc("/products/{key}", ProductHandler) +s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) +``` + +The three URL paths we registered above will only be tested if the domain is `www.example.com`, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route. + +Subrouters can be used to create domain or path "namespaces": you define subrouters in a central place and then parts of the app can register its paths relatively to a given subrouter. + +There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths: + +```go +r := mux.NewRouter() +s := r.PathPrefix("/products").Subrouter() +// "/products/" +s.HandleFunc("/", ProductsHandler) +// "/products/{key}/" +s.HandleFunc("/{key}/", ProductHandler) +// "/products/{key}/details" +s.HandleFunc("/{key}/details", ProductDetailsHandler) +``` + +Now let's see how to build registered URLs. + +Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling `Name()` on a route. For example: + +```go +r := mux.NewRouter() +r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). + Name("article") +``` + +To build a URL, get the route and call the `URL()` method, passing a sequence of key/value pairs for the route variables. For the previous route, we would do: + +```go +url, err := r.Get("article").URL("category", "technology", "id", "42") +``` + +...and the result will be a `url.URL` with the following path: + +``` +"/articles/technology/42" +``` + +This also works for host variables: + +```go +r := mux.NewRouter() +r.Host("{subdomain}.domain.com"). + Path("/articles/{category}/{id:[0-9]+}"). + HandlerFunc(ArticleHandler). + Name("article") + +// url.String() will be "http://news.domain.com/articles/technology/42" +url, err := r.Get("article").URL("subdomain", "news", + "category", "technology", + "id", "42") +``` + +All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match. + +Regex support also exists for matching Headers within a route. For example, we could do: + +```go +r.HeadersRegexp("Content-Type", "application/(text|json)") +``` + +...and the route will match both requests with a Content-Type of `application/json` as well as `application/text` + +There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do: + +```go +// "http://news.domain.com/" +host, err := r.Get("article").URLHost("subdomain", "news") + +// "/articles/technology/42" +path, err := r.Get("article").URLPath("category", "technology", "id", "42") +``` + +And if you use subrouters, host and path defined separately can be built as well: + +```go +r := mux.NewRouter() +s := r.Host("{subdomain}.domain.com").Subrouter() +s.Path("/articles/{category}/{id:[0-9]+}"). + HandlerFunc(ArticleHandler). + Name("article") + +// "http://news.domain.com/articles/technology/42" +url, err := r.Get("article").URL("subdomain", "news", + "category", "technology", + "id", "42") +``` + +## Full Example + +Here's a complete, runnable example of a small `mux` based server: + +```go +package main + +import ( + "net/http" + + "github.com/gorilla/mux" +) + +func YourHandler(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Gorilla!\n")) +} + +func main() { + r := mux.NewRouter() + // Routes consist of a path and a handler function. + r.HandleFunc("/", YourHandler) + + // Bind to a port and pass our router in + http.ListenAndServe(":8000", r) +} +``` + +## License + +BSD licensed. See the LICENSE file for details. diff --git a/vendor/github.com/gorilla/mux/bench_test.go b/vendor/github.com/gorilla/mux/bench_test.go new file mode 100644 index 0000000..946289b --- /dev/null +++ b/vendor/github.com/gorilla/mux/bench_test.go @@ -0,0 +1,49 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func BenchmarkMux(b *testing.B) { + router := new(Router) + handler := func(w http.ResponseWriter, r *http.Request) {} + router.HandleFunc("/v1/{v1}", handler) + + request, _ := http.NewRequest("GET", "/v1/anything", nil) + for i := 0; i < b.N; i++ { + router.ServeHTTP(nil, request) + } +} + +func BenchmarkMuxAlternativeInRegexp(b *testing.B) { + router := new(Router) + handler := func(w http.ResponseWriter, r *http.Request) {} + router.HandleFunc("/v1/{v1:(a|b)}", handler) + + requestA, _ := http.NewRequest("GET", "/v1/a", nil) + requestB, _ := http.NewRequest("GET", "/v1/b", nil) + for i := 0; i < b.N; i++ { + router.ServeHTTP(nil, requestA) + router.ServeHTTP(nil, requestB) + } +} + +func BenchmarkManyPathVariables(b *testing.B) { + router := new(Router) + handler := func(w http.ResponseWriter, r *http.Request) {} + router.HandleFunc("/v1/{v1}/{v2}/{v3}/{v4}/{v5}", handler) + + matchingRequest, _ := http.NewRequest("GET", "/v1/1/2/3/4/5", nil) + notMatchingRequest, _ := http.NewRequest("GET", "/v1/1/2/3/4", nil) + recorder := httptest.NewRecorder() + for i := 0; i < b.N; i++ { + router.ServeHTTP(nil, matchingRequest) + router.ServeHTTP(recorder, notMatchingRequest) + } +} diff --git a/vendor/github.com/gorilla/mux/doc.go b/vendor/github.com/gorilla/mux/doc.go new file mode 100644 index 0000000..835f534 --- /dev/null +++ b/vendor/github.com/gorilla/mux/doc.go @@ -0,0 +1,206 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package mux implements a request router and dispatcher. + +The name mux stands for "HTTP request multiplexer". Like the standard +http.ServeMux, mux.Router matches incoming requests against a list of +registered routes and calls a handler for the route that matches the URL +or other conditions. The main features are: + + * Requests can be matched based on URL host, path, path prefix, schemes, + header and query values, HTTP methods or using custom matchers. + * URL hosts and paths can have variables with an optional regular + expression. + * Registered URLs can be built, or "reversed", which helps maintaining + references to resources. + * Routes can be used as subrouters: nested routes are only tested if the + parent route matches. This is useful to define groups of routes that + share common conditions like a host, a path prefix or other repeated + attributes. As a bonus, this optimizes request matching. + * It implements the http.Handler interface so it is compatible with the + standard http.ServeMux. + +Let's start registering a couple of URL paths and handlers: + + func main() { + r := mux.NewRouter() + r.HandleFunc("/", HomeHandler) + r.HandleFunc("/products", ProductsHandler) + r.HandleFunc("/articles", ArticlesHandler) + http.Handle("/", r) + } + +Here we register three routes mapping URL paths to handlers. This is +equivalent to how http.HandleFunc() works: if an incoming request URL matches +one of the paths, the corresponding handler is called passing +(http.ResponseWriter, *http.Request) as parameters. + +Paths can have variables. They are defined using the format {name} or +{name:pattern}. If a regular expression pattern is not defined, the matched +variable will be anything until the next slash. For example: + + r := mux.NewRouter() + r.HandleFunc("/products/{key}", ProductHandler) + r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) + r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) + +The names are used to create a map of route variables which can be retrieved +calling mux.Vars(): + + vars := mux.Vars(request) + category := vars["category"] + +And this is all you need to know about the basic usage. More advanced options +are explained below. + +Routes can also be restricted to a domain or subdomain. Just define a host +pattern to be matched. They can also have variables: + + r := mux.NewRouter() + // Only matches if domain is "www.example.com". + r.Host("www.example.com") + // Matches a dynamic subdomain. + r.Host("{subdomain:[a-z]+}.domain.com") + +There are several other matchers that can be added. To match path prefixes: + + r.PathPrefix("/products/") + +...or HTTP methods: + + r.Methods("GET", "POST") + +...or URL schemes: + + r.Schemes("https") + +...or header values: + + r.Headers("X-Requested-With", "XMLHttpRequest") + +...or query values: + + r.Queries("key", "value") + +...or to use a custom matcher function: + + r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { + return r.ProtoMajor == 0 + }) + +...and finally, it is possible to combine several matchers in a single route: + + r.HandleFunc("/products", ProductsHandler). + Host("www.example.com"). + Methods("GET"). + Schemes("http") + +Setting the same matching conditions again and again can be boring, so we have +a way to group several routes that share the same requirements. +We call it "subrouting". + +For example, let's say we have several URLs that should only match when the +host is "www.example.com". Create a route for that host and get a "subrouter" +from it: + + r := mux.NewRouter() + s := r.Host("www.example.com").Subrouter() + +Then register routes in the subrouter: + + s.HandleFunc("/products/", ProductsHandler) + s.HandleFunc("/products/{key}", ProductHandler) + s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) + +The three URL paths we registered above will only be tested if the domain is +"www.example.com", because the subrouter is tested first. This is not +only convenient, but also optimizes request matching. You can create +subrouters combining any attribute matchers accepted by a route. + +Subrouters can be used to create domain or path "namespaces": you define +subrouters in a central place and then parts of the app can register its +paths relatively to a given subrouter. + +There's one more thing about subroutes. When a subrouter has a path prefix, +the inner routes use it as base for their paths: + + r := mux.NewRouter() + s := r.PathPrefix("/products").Subrouter() + // "/products/" + s.HandleFunc("/", ProductsHandler) + // "/products/{key}/" + s.HandleFunc("/{key}/", ProductHandler) + // "/products/{key}/details" + s.HandleFunc("/{key}/details", ProductDetailsHandler) + +Now let's see how to build registered URLs. + +Routes can be named. All routes that define a name can have their URLs built, +or "reversed". We define a name calling Name() on a route. For example: + + r := mux.NewRouter() + r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). + Name("article") + +To build a URL, get the route and call the URL() method, passing a sequence of +key/value pairs for the route variables. For the previous route, we would do: + + url, err := r.Get("article").URL("category", "technology", "id", "42") + +...and the result will be a url.URL with the following path: + + "/articles/technology/42" + +This also works for host variables: + + r := mux.NewRouter() + r.Host("{subdomain}.domain.com"). + Path("/articles/{category}/{id:[0-9]+}"). + HandlerFunc(ArticleHandler). + Name("article") + + // url.String() will be "http://news.domain.com/articles/technology/42" + url, err := r.Get("article").URL("subdomain", "news", + "category", "technology", + "id", "42") + +All variables defined in the route are required, and their values must +conform to the corresponding patterns. These requirements guarantee that a +generated URL will always match a registered route -- the only exception is +for explicitly defined "build-only" routes which never match. + +Regex support also exists for matching Headers within a route. For example, we could do: + + r.HeadersRegexp("Content-Type", "application/(text|json)") + +...and the route will match both requests with a Content-Type of `application/json` as well as +`application/text` + +There's also a way to build only the URL host or path for a route: +use the methods URLHost() or URLPath() instead. For the previous route, +we would do: + + // "http://news.domain.com/" + host, err := r.Get("article").URLHost("subdomain", "news") + + // "/articles/technology/42" + path, err := r.Get("article").URLPath("category", "technology", "id", "42") + +And if you use subrouters, host and path defined separately can be built +as well: + + r := mux.NewRouter() + s := r.Host("{subdomain}.domain.com").Subrouter() + s.Path("/articles/{category}/{id:[0-9]+}"). + HandlerFunc(ArticleHandler). + Name("article") + + // "http://news.domain.com/articles/technology/42" + url, err := r.Get("article").URL("subdomain", "news", + "category", "technology", + "id", "42") +*/ +package mux diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go new file mode 100644 index 0000000..fbb7f19 --- /dev/null +++ b/vendor/github.com/gorilla/mux/mux.go @@ -0,0 +1,481 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "errors" + "fmt" + "net/http" + "path" + "regexp" + + "github.com/gorilla/context" +) + +// NewRouter returns a new router instance. +func NewRouter() *Router { + return &Router{namedRoutes: make(map[string]*Route), KeepContext: false} +} + +// Router registers routes to be matched and dispatches a handler. +// +// It implements the http.Handler interface, so it can be registered to serve +// requests: +// +// var router = mux.NewRouter() +// +// func main() { +// http.Handle("/", router) +// } +// +// Or, for Google App Engine, register it in a init() function: +// +// func init() { +// http.Handle("/", router) +// } +// +// This will send all incoming requests to the router. +type Router struct { + // Configurable Handler to be used when no route matches. + NotFoundHandler http.Handler + // Parent route, if this is a subrouter. + parent parentRoute + // Routes to be matched, in order. + routes []*Route + // Routes by name for URL building. + namedRoutes map[string]*Route + // See Router.StrictSlash(). This defines the flag for new routes. + strictSlash bool + // If true, do not clear the request context after handling the request + KeepContext bool +} + +// Match matches registered routes against the request. +func (r *Router) Match(req *http.Request, match *RouteMatch) bool { + for _, route := range r.routes { + if route.Match(req, match) { + return true + } + } + + // Closest match for a router (includes sub-routers) + if r.NotFoundHandler != nil { + match.Handler = r.NotFoundHandler + return true + } + return false +} + +// ServeHTTP dispatches the handler registered in the matched route. +// +// When there is a match, the route variables can be retrieved calling +// mux.Vars(request). +func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { + // Clean path to canonical form and redirect. + if p := cleanPath(req.URL.Path); p != req.URL.Path { + + // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. + // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: + // http://code.google.com/p/go/issues/detail?id=5252 + url := *req.URL + url.Path = p + p = url.String() + + w.Header().Set("Location", p) + w.WriteHeader(http.StatusMovedPermanently) + return + } + var match RouteMatch + var handler http.Handler + if r.Match(req, &match) { + handler = match.Handler + setVars(req, match.Vars) + setCurrentRoute(req, match.Route) + } + if handler == nil { + handler = http.NotFoundHandler() + } + if !r.KeepContext { + defer context.Clear(req) + } + handler.ServeHTTP(w, req) +} + +// Get returns a route registered with the given name. +func (r *Router) Get(name string) *Route { + return r.getNamedRoutes()[name] +} + +// GetRoute returns a route registered with the given name. This method +// was renamed to Get() and remains here for backwards compatibility. +func (r *Router) GetRoute(name string) *Route { + return r.getNamedRoutes()[name] +} + +// StrictSlash defines the trailing slash behavior for new routes. The initial +// value is false. +// +// When true, if the route path is "/path/", accessing "/path" will redirect +// to the former and vice versa. In other words, your application will always +// see the path as specified in the route. +// +// When false, if the route path is "/path", accessing "/path/" will not match +// this route and vice versa. +// +// Special case: when a route sets a path prefix using the PathPrefix() method, +// strict slash is ignored for that route because the redirect behavior can't +// be determined from a prefix alone. However, any subrouters created from that +// route inherit the original StrictSlash setting. +func (r *Router) StrictSlash(value bool) *Router { + r.strictSlash = value + return r +} + +// ---------------------------------------------------------------------------- +// parentRoute +// ---------------------------------------------------------------------------- + +// getNamedRoutes returns the map where named routes are registered. +func (r *Router) getNamedRoutes() map[string]*Route { + if r.namedRoutes == nil { + if r.parent != nil { + r.namedRoutes = r.parent.getNamedRoutes() + } else { + r.namedRoutes = make(map[string]*Route) + } + } + return r.namedRoutes +} + +// getRegexpGroup returns regexp definitions from the parent route, if any. +func (r *Router) getRegexpGroup() *routeRegexpGroup { + if r.parent != nil { + return r.parent.getRegexpGroup() + } + return nil +} + +func (r *Router) buildVars(m map[string]string) map[string]string { + if r.parent != nil { + m = r.parent.buildVars(m) + } + return m +} + +// ---------------------------------------------------------------------------- +// Route factories +// ---------------------------------------------------------------------------- + +// NewRoute registers an empty route. +func (r *Router) NewRoute() *Route { + route := &Route{parent: r, strictSlash: r.strictSlash} + r.routes = append(r.routes, route) + return route +} + +// Handle registers a new route with a matcher for the URL path. +// See Route.Path() and Route.Handler(). +func (r *Router) Handle(path string, handler http.Handler) *Route { + return r.NewRoute().Path(path).Handler(handler) +} + +// HandleFunc registers a new route with a matcher for the URL path. +// See Route.Path() and Route.HandlerFunc(). +func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, + *http.Request)) *Route { + return r.NewRoute().Path(path).HandlerFunc(f) +} + +// Headers registers a new route with a matcher for request header values. +// See Route.Headers(). +func (r *Router) Headers(pairs ...string) *Route { + return r.NewRoute().Headers(pairs...) +} + +// Host registers a new route with a matcher for the URL host. +// See Route.Host(). +func (r *Router) Host(tpl string) *Route { + return r.NewRoute().Host(tpl) +} + +// MatcherFunc registers a new route with a custom matcher function. +// See Route.MatcherFunc(). +func (r *Router) MatcherFunc(f MatcherFunc) *Route { + return r.NewRoute().MatcherFunc(f) +} + +// Methods registers a new route with a matcher for HTTP methods. +// See Route.Methods(). +func (r *Router) Methods(methods ...string) *Route { + return r.NewRoute().Methods(methods...) +} + +// Path registers a new route with a matcher for the URL path. +// See Route.Path(). +func (r *Router) Path(tpl string) *Route { + return r.NewRoute().Path(tpl) +} + +// PathPrefix registers a new route with a matcher for the URL path prefix. +// See Route.PathPrefix(). +func (r *Router) PathPrefix(tpl string) *Route { + return r.NewRoute().PathPrefix(tpl) +} + +// Queries registers a new route with a matcher for URL query values. +// See Route.Queries(). +func (r *Router) Queries(pairs ...string) *Route { + return r.NewRoute().Queries(pairs...) +} + +// Schemes registers a new route with a matcher for URL schemes. +// See Route.Schemes(). +func (r *Router) Schemes(schemes ...string) *Route { + return r.NewRoute().Schemes(schemes...) +} + +// BuildVarsFunc registers a new route with a custom function for modifying +// route variables before building a URL. +func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route { + return r.NewRoute().BuildVarsFunc(f) +} + +// Walk walks the router and all its sub-routers, calling walkFn for each route +// in the tree. The routes are walked in the order they were added. Sub-routers +// are explored depth-first. +func (r *Router) Walk(walkFn WalkFunc) error { + return r.walk(walkFn, []*Route{}) +} + +// SkipRouter is used as a return value from WalkFuncs to indicate that the +// router that walk is about to descend down to should be skipped. +var SkipRouter = errors.New("skip this router") + +// WalkFunc is the type of the function called for each route visited by Walk. +// At every invocation, it is given the current route, and the current router, +// and a list of ancestor routes that lead to the current route. +type WalkFunc func(route *Route, router *Router, ancestors []*Route) error + +func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { + for _, t := range r.routes { + if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" { + continue + } + + err := walkFn(t, r, ancestors) + if err == SkipRouter { + continue + } + for _, sr := range t.matchers { + if h, ok := sr.(*Router); ok { + err := h.walk(walkFn, ancestors) + if err != nil { + return err + } + } + } + if h, ok := t.handler.(*Router); ok { + ancestors = append(ancestors, t) + err := h.walk(walkFn, ancestors) + if err != nil { + return err + } + ancestors = ancestors[:len(ancestors)-1] + } + } + return nil +} + +// ---------------------------------------------------------------------------- +// Context +// ---------------------------------------------------------------------------- + +// RouteMatch stores information about a matched route. +type RouteMatch struct { + Route *Route + Handler http.Handler + Vars map[string]string +} + +type contextKey int + +const ( + varsKey contextKey = iota + routeKey +) + +// Vars returns the route variables for the current request, if any. +func Vars(r *http.Request) map[string]string { + if rv := context.Get(r, varsKey); rv != nil { + return rv.(map[string]string) + } + return nil +} + +// CurrentRoute returns the matched route for the current request, if any. +// This only works when called inside the handler of the matched route +// because the matched route is stored in the request context which is cleared +// after the handler returns, unless the KeepContext option is set on the +// Router. +func CurrentRoute(r *http.Request) *Route { + if rv := context.Get(r, routeKey); rv != nil { + return rv.(*Route) + } + return nil +} + +func setVars(r *http.Request, val interface{}) { + if val != nil { + context.Set(r, varsKey, val) + } +} + +func setCurrentRoute(r *http.Request, val interface{}) { + if val != nil { + context.Set(r, routeKey, val) + } +} + +// ---------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------- + +// cleanPath returns the canonical path for p, eliminating . and .. elements. +// Borrowed from the net/http package. +func cleanPath(p string) string { + if p == "" { + return "/" + } + if p[0] != '/' { + p = "/" + p + } + np := path.Clean(p) + // path.Clean removes trailing slash except for root; + // put the trailing slash back if necessary. + if p[len(p)-1] == '/' && np != "/" { + np += "/" + } + return np +} + +// uniqueVars returns an error if two slices contain duplicated strings. +func uniqueVars(s1, s2 []string) error { + for _, v1 := range s1 { + for _, v2 := range s2 { + if v1 == v2 { + return fmt.Errorf("mux: duplicated route variable %q", v2) + } + } + } + return nil +} + +// checkPairs returns the count of strings passed in, and an error if +// the count is not an even number. +func checkPairs(pairs ...string) (int, error) { + length := len(pairs) + if length%2 != 0 { + return length, fmt.Errorf( + "mux: number of parameters must be multiple of 2, got %v", pairs) + } + return length, nil +} + +// mapFromPairsToString converts variadic string parameters to a +// string to string map. +func mapFromPairsToString(pairs ...string) (map[string]string, error) { + length, err := checkPairs(pairs...) + if err != nil { + return nil, err + } + m := make(map[string]string, length/2) + for i := 0; i < length; i += 2 { + m[pairs[i]] = pairs[i+1] + } + return m, nil +} + +// mapFromPairsToRegex converts variadic string paramers to a +// string to regex map. +func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { + length, err := checkPairs(pairs...) + if err != nil { + return nil, err + } + m := make(map[string]*regexp.Regexp, length/2) + for i := 0; i < length; i += 2 { + regex, err := regexp.Compile(pairs[i+1]) + if err != nil { + return nil, err + } + m[pairs[i]] = regex + } + return m, nil +} + +// matchInArray returns true if the given string value is in the array. +func matchInArray(arr []string, value string) bool { + for _, v := range arr { + if v == value { + return true + } + } + return false +} + +// matchMapWithString returns true if the given key/value pairs exist in a given map. +func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool { + for k, v := range toCheck { + // Check if key exists. + if canonicalKey { + k = http.CanonicalHeaderKey(k) + } + if values := toMatch[k]; values == nil { + return false + } else if v != "" { + // If value was defined as an empty string we only check that the + // key exists. Otherwise we also check for equality. + valueExists := false + for _, value := range values { + if v == value { + valueExists = true + break + } + } + if !valueExists { + return false + } + } + } + return true +} + +// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against +// the given regex +func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool { + for k, v := range toCheck { + // Check if key exists. + if canonicalKey { + k = http.CanonicalHeaderKey(k) + } + if values := toMatch[k]; values == nil { + return false + } else if v != nil { + // If value was defined as an empty string we only check that the + // key exists. Otherwise we also check for equality. + valueExists := false + for _, value := range values { + if v.MatchString(value) { + valueExists = true + break + } + } + if !valueExists { + return false + } + } + } + return true +} diff --git a/vendor/github.com/gorilla/mux/mux_test.go b/vendor/github.com/gorilla/mux/mux_test.go new file mode 100644 index 0000000..a44d03f --- /dev/null +++ b/vendor/github.com/gorilla/mux/mux_test.go @@ -0,0 +1,1453 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "fmt" + "net/http" + "strings" + "testing" + + "github.com/gorilla/context" +) + +func (r *Route) GoString() string { + matchers := make([]string, len(r.matchers)) + for i, m := range r.matchers { + matchers[i] = fmt.Sprintf("%#v", m) + } + return fmt.Sprintf("&Route{matchers:[]matcher{%s}}", strings.Join(matchers, ", ")) +} + +func (r *routeRegexp) GoString() string { + return fmt.Sprintf("&routeRegexp{template: %q, matchHost: %t, matchQuery: %t, strictSlash: %t, regexp: regexp.MustCompile(%q), reverse: %q, varsN: %v, varsR: %v", r.template, r.matchHost, r.matchQuery, r.strictSlash, r.regexp.String(), r.reverse, r.varsN, r.varsR) +} + +type routeTest struct { + title string // title of the test + route *Route // the route being tested + request *http.Request // a request to test the route + vars map[string]string // the expected vars of the match + host string // the expected host of the match + path string // the expected path of the match + path_template string // the expected path template to match + host_template string // the expected host template to match + shouldMatch bool // whether the request is expected to match the route at all + shouldRedirect bool // whether the request should result in a redirect +} + +func TestHost(t *testing.T) { + // newRequestHost a new request with a method, url, and host header + newRequestHost := func(method, url, host string) *http.Request { + req, err := http.NewRequest(method, url, nil) + if err != nil { + panic(err) + } + req.Host = host + return req + } + + tests := []routeTest{ + { + title: "Host route match", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: true, + }, + { + title: "Host route, wrong host in request URL", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: false, + }, + { + title: "Host route with port, match", + route: new(Route).Host("aaa.bbb.ccc:1234"), + request: newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc:1234", + path: "", + shouldMatch: true, + }, + { + title: "Host route with port, wrong port in request URL", + route: new(Route).Host("aaa.bbb.ccc:1234"), + request: newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc:1234", + path: "", + shouldMatch: false, + }, + { + title: "Host route, match with host in request header", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: true, + }, + { + title: "Host route, wrong host in request header", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequestHost("GET", "/111/222/333", "aaa.222.ccc"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: false, + }, + // BUG {new(Route).Host("aaa.bbb.ccc:1234"), newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"), map[string]string{}, "aaa.bbb.ccc:1234", "", true}, + { + title: "Host route with port, wrong host in request header", + route: new(Route).Host("aaa.bbb.ccc:1234"), + request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"), + vars: map[string]string{}, + host: "aaa.bbb.ccc:1234", + path: "", + shouldMatch: false, + }, + { + title: "Host route with pattern, match", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with pattern, additional capturing group, match", + route: new(Route).Host("aaa.{v1:[a-z]{2}(b|c)}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `aaa.{v1:[a-z]{2}(b|c)}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with pattern, wrong host in request URL", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: false, + }, + { + title: "Host route with multiple patterns, match", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: true, + }, + { + title: "Host route with multiple patterns, wrong host in request URL", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: false, + }, + { + title: "Host route with hyphenated name and pattern, match", + route: new(Route).Host("aaa.{v-1:[a-z]{3}}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v-1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `aaa.{v-1:[a-z]{3}}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with hyphenated name and pattern, additional capturing group, match", + route: new(Route).Host("aaa.{v-1:[a-z]{2}(b|c)}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v-1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `aaa.{v-1:[a-z]{2}(b|c)}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with multiple hyphenated names and patterns, match", + route: new(Route).Host("{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v-1": "aaa", "v-2": "bbb", "v-3": "ccc"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}`, + shouldMatch: true, + }, + { + title: "Path route with single pattern with pipe, match", + route: new(Route).Path("/{category:a|b/c}"), + request: newRequest("GET", "http://localhost/a"), + vars: map[string]string{"category": "a"}, + host: "", + path: "/a", + path_template: `/{category:a|b/c}`, + shouldMatch: true, + }, + { + title: "Path route with single pattern with pipe, match", + route: new(Route).Path("/{category:a|b/c}"), + request: newRequest("GET", "http://localhost/b/c"), + vars: map[string]string{"category": "b/c"}, + host: "", + path: "/b/c", + path_template: `/{category:a|b/c}`, + shouldMatch: true, + }, + { + title: "Path route with multiple patterns with pipe, match", + route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"), + request: newRequest("GET", "http://localhost/a/product_name/1"), + vars: map[string]string{"category": "a", "product": "product_name", "id": "1"}, + host: "", + path: "/a/product_name/1", + path_template: `/{category:a|b/c}/{product}/{id:[0-9]+}`, + shouldMatch: true, + }, + { + title: "Path route with multiple patterns with pipe, match", + route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"), + request: newRequest("GET", "http://localhost/b/c/product_name/1"), + vars: map[string]string{"category": "b/c", "product": "product_name", "id": "1"}, + host: "", + path: "/b/c/product_name/1", + path_template: `/{category:a|b/c}/{product}/{id:[0-9]+}`, + shouldMatch: true, + }, + } + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestPath(t *testing.T) { + tests := []routeTest{ + { + title: "Path route, match", + route: new(Route).Path("/111/222/333"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{}, + host: "", + path: "/111/222/333", + shouldMatch: true, + }, + { + title: "Path route, match with trailing slash in request and path", + route: new(Route).Path("/111/"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111/", + shouldMatch: true, + }, + { + title: "Path route, do not match with trailing slash in path", + route: new(Route).Path("/111/"), + request: newRequest("GET", "http://localhost/111"), + vars: map[string]string{}, + host: "", + path: "/111", + path_template: `/111/`, + shouldMatch: false, + }, + { + title: "Path route, do not match with trailing slash in request", + route: new(Route).Path("/111"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111/", + path_template: `/111`, + shouldMatch: false, + }, + { + title: "Path route, wrong path in request in request URL", + route: new(Route).Path("/111/222/333"), + request: newRequest("GET", "http://localhost/1/2/3"), + vars: map[string]string{}, + host: "", + path: "/111/222/333", + shouldMatch: false, + }, + { + title: "Path route with pattern, match", + route: new(Route).Path("/111/{v1:[0-9]{3}}/333"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222/333", + path_template: `/111/{v1:[0-9]{3}}/333`, + shouldMatch: true, + }, + { + title: "Path route with pattern, URL in request does not match", + route: new(Route).Path("/111/{v1:[0-9]{3}}/333"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222/333", + path_template: `/111/{v1:[0-9]{3}}/333`, + shouldMatch: false, + }, + { + title: "Path route with multiple patterns, match", + route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"}, + host: "", + path: "/111/222/333", + path_template: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}`, + shouldMatch: true, + }, + { + title: "Path route with multiple patterns, URL in request does not match", + route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"}, + host: "", + path: "/111/222/333", + path_template: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}`, + shouldMatch: false, + }, + { + title: "Path route with multiple patterns with pipe, match", + route: new(Route).Path("/{category:a|(b/c)}/{product}/{id:[0-9]+}"), + request: newRequest("GET", "http://localhost/a/product_name/1"), + vars: map[string]string{"category": "a", "product": "product_name", "id": "1"}, + host: "", + path: "/a/product_name/1", + path_template: `/{category:a|(b/c)}/{product}/{id:[0-9]+}`, + shouldMatch: true, + }, + { + title: "Path route with hyphenated name and pattern, match", + route: new(Route).Path("/111/{v-1:[0-9]{3}}/333"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v-1": "222"}, + host: "", + path: "/111/222/333", + path_template: `/111/{v-1:[0-9]{3}}/333`, + shouldMatch: true, + }, + { + title: "Path route with multiple hyphenated names and patterns, match", + route: new(Route).Path("/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v-1": "111", "v-2": "222", "v-3": "333"}, + host: "", + path: "/111/222/333", + path_template: `/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}`, + shouldMatch: true, + }, + { + title: "Path route with multiple hyphenated names and patterns with pipe, match", + route: new(Route).Path("/{product-category:a|(b/c)}/{product-name}/{product-id:[0-9]+}"), + request: newRequest("GET", "http://localhost/a/product_name/1"), + vars: map[string]string{"product-category": "a", "product-name": "product_name", "product-id": "1"}, + host: "", + path: "/a/product_name/1", + path_template: `/{product-category:a|(b/c)}/{product-name}/{product-id:[0-9]+}`, + shouldMatch: true, + }, + { + title: "Path route with multiple hyphenated names and patterns with pipe and case insensitive, match", + route: new(Route).Path("/{type:(?i:daily|mini|variety)}-{date:\\d{4,4}-\\d{2,2}-\\d{2,2}}"), + request: newRequest("GET", "http://localhost/daily-2016-01-01"), + vars: map[string]string{"type": "daily", "date": "2016-01-01"}, + host: "", + path: "/daily-2016-01-01", + path_template: `/{type:(?i:daily|mini|variety)}-{date:\d{4,4}-\d{2,2}-\d{2,2}}`, + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestPathPrefix(t *testing.T) { + tests := []routeTest{ + { + title: "PathPrefix route, match", + route: new(Route).PathPrefix("/111"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: true, + }, + { + title: "PathPrefix route, match substring", + route: new(Route).PathPrefix("/1"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{}, + host: "", + path: "/1", + shouldMatch: true, + }, + { + title: "PathPrefix route, URL prefix in request does not match", + route: new(Route).PathPrefix("/111"), + request: newRequest("GET", "http://localhost/1/2/3"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: false, + }, + { + title: "PathPrefix route with pattern, match", + route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222", + path_template: `/111/{v1:[0-9]{3}}`, + shouldMatch: true, + }, + { + title: "PathPrefix route with pattern, URL prefix in request does not match", + route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222", + path_template: `/111/{v1:[0-9]{3}}`, + shouldMatch: false, + }, + { + title: "PathPrefix route with multiple patterns, match", + route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "111", "v2": "222"}, + host: "", + path: "/111/222", + path_template: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}`, + shouldMatch: true, + }, + { + title: "PathPrefix route with multiple patterns, URL prefix in request does not match", + route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "111", "v2": "222"}, + host: "", + path: "/111/222", + path_template: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}`, + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestHostPath(t *testing.T) { + tests := []routeTest{ + { + title: "Host and Path route, match", + route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{}, + host: "", + path: "", + path_template: `/111/222/333`, + host_template: `aaa.bbb.ccc`, + shouldMatch: true, + }, + { + title: "Host and Path route, wrong host in request URL", + route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{}, + host: "", + path: "", + path_template: `/111/222/333`, + host_template: `aaa.bbb.ccc`, + shouldMatch: false, + }, + { + title: "Host and Path route with pattern, match", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb", "v2": "222"}, + host: "aaa.bbb.ccc", + path: "/111/222/333", + path_template: `/111/{v2:[0-9]{3}}/333`, + host_template: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: true, + }, + { + title: "Host and Path route with pattern, URL in request does not match", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb", "v2": "222"}, + host: "aaa.bbb.ccc", + path: "/111/222/333", + path_template: `/111/{v2:[0-9]{3}}/333`, + host_template: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: false, + }, + { + title: "Host and Path route with multiple patterns, match", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"}, + host: "aaa.bbb.ccc", + path: "/111/222/333", + path_template: `/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}`, + host_template: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: true, + }, + { + title: "Host and Path route with multiple patterns, URL in request does not match", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"}, + host: "aaa.bbb.ccc", + path: "/111/222/333", + path_template: `/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}`, + host_template: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestHeaders(t *testing.T) { + // newRequestHeaders creates a new request with a method, url, and headers + newRequestHeaders := func(method, url string, headers map[string]string) *http.Request { + req, err := http.NewRequest(method, url, nil) + if err != nil { + panic(err) + } + for k, v := range headers { + req.Header.Add(k, v) + } + return req + } + + tests := []routeTest{ + { + title: "Headers route, match", + route: new(Route).Headers("foo", "bar", "baz", "ding"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Headers route, bad header values", + route: new(Route).Headers("foo", "bar", "baz", "ding"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Headers route, regex header values to match", + route: new(Route).Headers("foo", "ba[zr]"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Headers route, regex header values to match", + route: new(Route).HeadersRegexp("foo", "ba[zr]"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "baz"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } + +} + +func TestMethods(t *testing.T) { + tests := []routeTest{ + { + title: "Methods route, match GET", + route: new(Route).Methods("GET", "POST"), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Methods route, match POST", + route: new(Route).Methods("GET", "POST"), + request: newRequest("POST", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Methods route, bad method", + route: new(Route).Methods("GET", "POST"), + request: newRequest("PUT", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestQueries(t *testing.T) { + tests := []routeTest{ + { + title: "Queries route, match", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route, match with a query string", + route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"), + vars: map[string]string{}, + host: "", + path: "", + path_template: `/api`, + host_template: `www.example.com`, + shouldMatch: true, + }, + { + title: "Queries route, match with a query string out of order", + route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"), + vars: map[string]string{}, + host: "", + path: "", + path_template: `/api`, + host_template: `www.example.com`, + shouldMatch: true, + }, + { + title: "Queries route, bad query", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?foo=bar&baz=dong"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with pattern, match", + route: new(Route).Queries("foo", "{v1}"), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{"v1": "bar"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with multiple patterns, match", + route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{"v1": "bar", "v2": "ding"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern, match", + route: new(Route).Queries("foo", "{v1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=10"), + vars: map[string]string{"v1": "10"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=a"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with regexp pattern with quantifier, match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=1"), + vars: map[string]string{"v1": "1"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, additional variable in query string, match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?bar=2&foo=1"), + vars: map[string]string{"v1": "1"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=12"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with regexp pattern with quantifier, additional capturing group", + route: new(Route).Queries("foo", "{v1:[0-9]{1}(a|b)}"), + request: newRequest("GET", "http://localhost?foo=1a"), + vars: map[string]string{"v1": "1a"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=12"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with hyphenated name, match", + route: new(Route).Queries("foo", "{v-1}"), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{"v-1": "bar"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with multiple hyphenated names, match", + route: new(Route).Queries("foo", "{v-1}", "baz", "{v-2}"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{"v-1": "bar", "v-2": "ding"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with hyphenate name and pattern, match", + route: new(Route).Queries("foo", "{v-1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=10"), + vars: map[string]string{"v-1": "10"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with hyphenated name and pattern with quantifier, additional capturing group", + route: new(Route).Queries("foo", "{v-1:[0-9]{1}(a|b)}"), + request: newRequest("GET", "http://localhost?foo=1a"), + vars: map[string]string{"v-1": "1a"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with empty value, should match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with empty value and no parameter in request, should not match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with empty value and empty parameter in request, should match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost?foo="), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with overlapping value, should not match", + route: new(Route).Queries("foo", "bar"), + request: newRequest("GET", "http://localhost?foo=barfoo"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with no parameter in request, should not match", + route: new(Route).Queries("foo", "{bar}"), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with empty parameter in request, should match", + route: new(Route).Queries("foo", "{bar}"), + request: newRequest("GET", "http://localhost?foo="), + vars: map[string]string{"foo": ""}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route, bad submatch", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestSchemes(t *testing.T) { + tests := []routeTest{ + // Schemes + { + title: "Schemes route, match https", + route: new(Route).Schemes("https", "ftp"), + request: newRequest("GET", "https://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Schemes route, match ftp", + route: new(Route).Schemes("https", "ftp"), + request: newRequest("GET", "ftp://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Schemes route, bad scheme", + route: new(Route).Schemes("https", "ftp"), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + } + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestMatcherFunc(t *testing.T) { + m := func(r *http.Request, m *RouteMatch) bool { + if r.URL.Host == "aaa.bbb.ccc" { + return true + } + return false + } + + tests := []routeTest{ + { + title: "MatchFunc route, match", + route: new(Route).MatcherFunc(m), + request: newRequest("GET", "http://aaa.bbb.ccc"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "MatchFunc route, non-match", + route: new(Route).MatcherFunc(m), + request: newRequest("GET", "http://aaa.222.ccc"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestBuildVarsFunc(t *testing.T) { + tests := []routeTest{ + { + title: "BuildVarsFunc set on route", + route: new(Route).Path(`/111/{v1:\d}{v2:.*}`).BuildVarsFunc(func(vars map[string]string) map[string]string { + vars["v1"] = "3" + vars["v2"] = "a" + return vars + }), + request: newRequest("GET", "http://localhost/111/2"), + path: "/111/3a", + path_template: `/111/{v1:\d}{v2:.*}`, + shouldMatch: true, + }, + { + title: "BuildVarsFunc set on route and parent route", + route: new(Route).PathPrefix(`/{v1:\d}`).BuildVarsFunc(func(vars map[string]string) map[string]string { + vars["v1"] = "2" + return vars + }).Subrouter().Path(`/{v2:\w}`).BuildVarsFunc(func(vars map[string]string) map[string]string { + vars["v2"] = "b" + return vars + }), + request: newRequest("GET", "http://localhost/1/a"), + path: "/2/b", + path_template: `/{v1:\d}/{v2:\w}`, + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestSubRouter(t *testing.T) { + subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter() + subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter() + + tests := []routeTest{ + { + route: subrouter1.Path("/{v2:[a-z]+}"), + request: newRequest("GET", "http://aaa.google.com/bbb"), + vars: map[string]string{"v1": "aaa", "v2": "bbb"}, + host: "aaa.google.com", + path: "/bbb", + path_template: `/{v2:[a-z]+}`, + host_template: `{v1:[a-z]+}.google.com`, + shouldMatch: true, + }, + { + route: subrouter1.Path("/{v2:[a-z]+}"), + request: newRequest("GET", "http://111.google.com/111"), + vars: map[string]string{"v1": "aaa", "v2": "bbb"}, + host: "aaa.google.com", + path: "/bbb", + path_template: `/{v2:[a-z]+}`, + host_template: `{v1:[a-z]+}.google.com`, + shouldMatch: false, + }, + { + route: subrouter2.Path("/baz/{v2}"), + request: newRequest("GET", "http://localhost/foo/bar/baz/ding"), + vars: map[string]string{"v1": "bar", "v2": "ding"}, + host: "", + path: "/foo/bar/baz/ding", + path_template: `/foo/{v1}/baz/{v2}`, + shouldMatch: true, + }, + { + route: subrouter2.Path("/baz/{v2}"), + request: newRequest("GET", "http://localhost/foo/bar"), + vars: map[string]string{"v1": "bar", "v2": "ding"}, + host: "", + path: "/foo/bar/baz/ding", + path_template: `/foo/{v1}/baz/{v2}`, + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestNamedRoutes(t *testing.T) { + r1 := NewRouter() + r1.NewRoute().Name("a") + r1.NewRoute().Name("b") + r1.NewRoute().Name("c") + + r2 := r1.NewRoute().Subrouter() + r2.NewRoute().Name("d") + r2.NewRoute().Name("e") + r2.NewRoute().Name("f") + + r3 := r2.NewRoute().Subrouter() + r3.NewRoute().Name("g") + r3.NewRoute().Name("h") + r3.NewRoute().Name("i") + + if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 { + t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes) + } else if r1.Get("i") == nil { + t.Errorf("Subroute name not registered") + } +} + +func TestStrictSlash(t *testing.T) { + r := NewRouter() + r.StrictSlash(true) + + tests := []routeTest{ + { + title: "Redirect path without slash", + route: r.NewRoute().Path("/111/"), + request: newRequest("GET", "http://localhost/111"), + vars: map[string]string{}, + host: "", + path: "/111/", + shouldMatch: true, + shouldRedirect: true, + }, + { + title: "Do not redirect path with slash", + route: r.NewRoute().Path("/111/"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111/", + shouldMatch: true, + shouldRedirect: false, + }, + { + title: "Redirect path with slash", + route: r.NewRoute().Path("/111"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: true, + shouldRedirect: true, + }, + { + title: "Do not redirect path without slash", + route: r.NewRoute().Path("/111"), + request: newRequest("GET", "http://localhost/111"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: true, + shouldRedirect: false, + }, + { + title: "Propagate StrictSlash to subrouters", + route: r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"), + request: newRequest("GET", "http://localhost/static/images"), + vars: map[string]string{}, + host: "", + path: "/static/images/", + shouldMatch: true, + shouldRedirect: true, + }, + { + title: "Ignore StrictSlash for path prefix", + route: r.NewRoute().PathPrefix("/static/"), + request: newRequest("GET", "http://localhost/static/logo.png"), + vars: map[string]string{}, + host: "", + path: "/static/", + shouldMatch: true, + shouldRedirect: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestWalkSingleDepth(t *testing.T) { + r0 := NewRouter() + r1 := NewRouter() + r2 := NewRouter() + + r0.Path("/g") + r0.Path("/o") + r0.Path("/d").Handler(r1) + r0.Path("/r").Handler(r2) + r0.Path("/a") + + r1.Path("/z") + r1.Path("/i") + r1.Path("/l") + r1.Path("/l") + + r2.Path("/i") + r2.Path("/l") + r2.Path("/l") + + paths := []string{"g", "o", "r", "i", "l", "l", "a"} + depths := []int{0, 0, 0, 1, 1, 1, 0} + i := 0 + err := r0.Walk(func(route *Route, router *Router, ancestors []*Route) error { + matcher := route.matchers[0].(*routeRegexp) + if matcher.template == "/d" { + return SkipRouter + } + if len(ancestors) != depths[i] { + t.Errorf(`Expected depth of %d at i = %d; got "%d"`, depths[i], i, len(ancestors)) + } + if matcher.template != "/"+paths[i] { + t.Errorf(`Expected "/%s" at i = %d; got "%s"`, paths[i], i, matcher.template) + } + i++ + return nil + }) + if err != nil { + panic(err) + } + if i != len(paths) { + t.Errorf("Expected %d routes, found %d", len(paths), i) + } +} + +func TestWalkNested(t *testing.T) { + router := NewRouter() + + g := router.Path("/g").Subrouter() + o := g.PathPrefix("/o").Subrouter() + r := o.PathPrefix("/r").Subrouter() + i := r.PathPrefix("/i").Subrouter() + l1 := i.PathPrefix("/l").Subrouter() + l2 := l1.PathPrefix("/l").Subrouter() + l2.Path("/a") + + paths := []string{"/g", "/g/o", "/g/o/r", "/g/o/r/i", "/g/o/r/i/l", "/g/o/r/i/l/l", "/g/o/r/i/l/l/a"} + idx := 0 + err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error { + path := paths[idx] + tpl := route.regexp.path.template + if tpl != path { + t.Errorf(`Expected %s got %s`, path, tpl) + } + idx++ + return nil + }) + if err != nil { + panic(err) + } + if idx != len(paths) { + t.Errorf("Expected %d routes, found %d", len(paths), idx) + } +} + +func TestSubrouterErrorHandling(t *testing.T) { + superRouterCalled := false + subRouterCalled := false + + router := NewRouter() + router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + superRouterCalled = true + }) + subRouter := router.PathPrefix("/bign8").Subrouter() + subRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + subRouterCalled = true + }) + + req, _ := http.NewRequest("GET", "http://localhost/bign8/was/here", nil) + router.ServeHTTP(NewRecorder(), req) + + if superRouterCalled { + t.Error("Super router 404 handler called when sub-router 404 handler is available.") + } + if !subRouterCalled { + t.Error("Sub-router 404 handler was not called.") + } +} + +// ---------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------- + +func getRouteTemplate(route *Route) string { + host, err := route.GetHostTemplate() + if err != nil { + host = "none" + } + path, err := route.GetPathTemplate() + if err != nil { + path = "none" + } + return fmt.Sprintf("Host: %v, Path: %v", host, path) +} + +func testRoute(t *testing.T, test routeTest) { + request := test.request + route := test.route + vars := test.vars + shouldMatch := test.shouldMatch + host := test.host + path := test.path + url := test.host + test.path + shouldRedirect := test.shouldRedirect + + var match RouteMatch + ok := route.Match(request, &match) + if ok != shouldMatch { + msg := "Should match" + if !shouldMatch { + msg = "Should not match" + } + t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars) + return + } + if shouldMatch { + if test.vars != nil && !stringMapEqual(test.vars, match.Vars) { + t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars) + return + } + if host != "" { + u, _ := test.route.URLHost(mapToPairs(match.Vars)...) + if host != u.Host { + t.Errorf("(%v) URLHost not equal: expected %v, got %v -- %v", test.title, host, u.Host, getRouteTemplate(route)) + return + } + } + if path != "" { + u, _ := route.URLPath(mapToPairs(match.Vars)...) + if path != u.Path { + t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, path, u.Path, getRouteTemplate(route)) + return + } + } + if url != "" { + u, _ := route.URL(mapToPairs(match.Vars)...) + if url != u.Host+u.Path { + t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, url, u.Host+u.Path, getRouteTemplate(route)) + return + } + } + if shouldRedirect && match.Handler == nil { + t.Errorf("(%v) Did not redirect", test.title) + return + } + if !shouldRedirect && match.Handler != nil { + t.Errorf("(%v) Unexpected redirect", test.title) + return + } + } +} + +func testTemplate(t *testing.T, test routeTest) { + route := test.route + path_template := test.path_template + if len(path_template) == 0 { + path_template = test.path + } + host_template := test.host_template + if len(host_template) == 0 { + host_template = test.host + } + + path_tmpl, path_err := route.GetPathTemplate() + if path_err == nil && path_tmpl != path_template { + t.Errorf("(%v) GetPathTemplate not equal: expected %v, got %v", test.title, path_template, path_tmpl) + } + + host_tmpl, host_err := route.GetHostTemplate() + if host_err == nil && host_tmpl != host_template { + t.Errorf("(%v) GetHostTemplate not equal: expected %v, got %v", test.title, host_template, host_tmpl) + } +} + +// Tests that the context is cleared or not cleared properly depending on +// the configuration of the router +func TestKeepContext(t *testing.T) { + func1 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/", func1).Name("func1") + + req, _ := http.NewRequest("GET", "http://localhost/", nil) + context.Set(req, "t", 1) + + res := new(http.ResponseWriter) + r.ServeHTTP(*res, req) + + if _, ok := context.GetOk(req, "t"); ok { + t.Error("Context should have been cleared at end of request") + } + + r.KeepContext = true + + req, _ = http.NewRequest("GET", "http://localhost/", nil) + context.Set(req, "t", 1) + + r.ServeHTTP(*res, req) + if _, ok := context.GetOk(req, "t"); !ok { + t.Error("Context should NOT have been cleared at end of request") + } + +} + +type TestA301ResponseWriter struct { + hh http.Header + status int +} + +func (ho TestA301ResponseWriter) Header() http.Header { + return http.Header(ho.hh) +} + +func (ho TestA301ResponseWriter) Write(b []byte) (int, error) { + return 0, nil +} + +func (ho TestA301ResponseWriter) WriteHeader(code int) { + ho.status = code +} + +func Test301Redirect(t *testing.T) { + m := make(http.Header) + + func1 := func(w http.ResponseWriter, r *http.Request) {} + func2 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/api/", func2).Name("func2") + r.HandleFunc("/", func1).Name("func1") + + req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil) + + res := TestA301ResponseWriter{ + hh: m, + status: 0, + } + r.ServeHTTP(&res, req) + + if "http://localhost/api/?abc=def" != res.hh["Location"][0] { + t.Errorf("Should have complete URL with query string") + } +} + +// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW +func TestSubrouterHeader(t *testing.T) { + expected := "func1 response" + func1 := func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, expected) + } + func2 := func(http.ResponseWriter, *http.Request) {} + + r := NewRouter() + s := r.Headers("SomeSpecialHeader", "").Subrouter() + s.HandleFunc("/", func1).Name("func1") + r.HandleFunc("/", func2).Name("func2") + + req, _ := http.NewRequest("GET", "http://localhost/", nil) + req.Header.Add("SomeSpecialHeader", "foo") + match := new(RouteMatch) + matched := r.Match(req, match) + if !matched { + t.Errorf("Should match request") + } + if match.Route.GetName() != "func1" { + t.Errorf("Expecting func1 handler, got %s", match.Route.GetName()) + } + resp := NewRecorder() + match.Handler.ServeHTTP(resp, req) + if resp.Body.String() != expected { + t.Errorf("Expecting %q", expected) + } +} + +// mapToPairs converts a string map to a slice of string pairs +func mapToPairs(m map[string]string) []string { + var i int + p := make([]string, len(m)*2) + for k, v := range m { + p[i] = k + p[i+1] = v + i += 2 + } + return p +} + +// stringMapEqual checks the equality of two string maps +func stringMapEqual(m1, m2 map[string]string) bool { + nil1 := m1 == nil + nil2 := m2 == nil + if nil1 != nil2 || len(m1) != len(m2) { + return false + } + for k, v := range m1 { + if v != m2[k] { + return false + } + } + return true +} + +// newRequest is a helper function to create a new request with a method and url +func newRequest(method, url string) *http.Request { + req, err := http.NewRequest(method, url, nil) + if err != nil { + panic(err) + } + return req +} diff --git a/vendor/github.com/gorilla/mux/old_test.go b/vendor/github.com/gorilla/mux/old_test.go new file mode 100644 index 0000000..c385a25 --- /dev/null +++ b/vendor/github.com/gorilla/mux/old_test.go @@ -0,0 +1,710 @@ +// Old tests ported to Go1. This is a mess. Want to drop it one day. + +// Copyright 2011 Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "bytes" + "net/http" + "testing" +) + +// ---------------------------------------------------------------------------- +// ResponseRecorder +// ---------------------------------------------------------------------------- +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// ResponseRecorder is an implementation of http.ResponseWriter that +// records its mutations for later inspection in tests. +type ResponseRecorder struct { + Code int // the HTTP response code from WriteHeader + HeaderMap http.Header // the HTTP response headers + Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to + Flushed bool +} + +// NewRecorder returns an initialized ResponseRecorder. +func NewRecorder() *ResponseRecorder { + return &ResponseRecorder{ + HeaderMap: make(http.Header), + Body: new(bytes.Buffer), + } +} + +// Header returns the response headers. +func (rw *ResponseRecorder) Header() http.Header { + return rw.HeaderMap +} + +// Write always succeeds and writes to rw.Body, if not nil. +func (rw *ResponseRecorder) Write(buf []byte) (int, error) { + if rw.Body != nil { + rw.Body.Write(buf) + } + if rw.Code == 0 { + rw.Code = http.StatusOK + } + return len(buf), nil +} + +// WriteHeader sets rw.Code. +func (rw *ResponseRecorder) WriteHeader(code int) { + rw.Code = code +} + +// Flush sets rw.Flushed to true. +func (rw *ResponseRecorder) Flush() { + rw.Flushed = true +} + +// ---------------------------------------------------------------------------- + +func TestRouteMatchers(t *testing.T) { + var scheme, host, path, query, method string + var headers map[string]string + var resultVars map[bool]map[string]string + + router := NewRouter() + router.NewRoute().Host("{var1}.google.com"). + Path("/{var2:[a-z]+}/{var3:[0-9]+}"). + Queries("foo", "bar"). + Methods("GET"). + Schemes("https"). + Headers("x-requested-with", "XMLHttpRequest") + router.NewRoute().Host("www.{var4}.com"). + PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}"). + Queries("baz", "ding"). + Methods("POST"). + Schemes("http"). + Headers("Content-Type", "application/json") + + reset := func() { + // Everything match. + scheme = "https" + host = "www.google.com" + path = "/product/42" + query = "?foo=bar" + method = "GET" + headers = map[string]string{"X-Requested-With": "XMLHttpRequest"} + resultVars = map[bool]map[string]string{ + true: {"var1": "www", "var2": "product", "var3": "42"}, + false: {}, + } + } + + reset2 := func() { + // Everything match. + scheme = "http" + host = "www.google.com" + path = "/foo/product/42/path/that/is/ignored" + query = "?baz=ding" + method = "POST" + headers = map[string]string{"Content-Type": "application/json"} + resultVars = map[bool]map[string]string{ + true: {"var4": "google", "var5": "product", "var6": "42"}, + false: {}, + } + } + + match := func(shouldMatch bool) { + url := scheme + "://" + host + path + query + request, _ := http.NewRequest(method, url, nil) + for key, value := range headers { + request.Header.Add(key, value) + } + + var routeMatch RouteMatch + matched := router.Match(request, &routeMatch) + if matched != shouldMatch { + // Need better messages. :) + if matched { + t.Errorf("Should match.") + } else { + t.Errorf("Should not match.") + } + } + + if matched { + currentRoute := routeMatch.Route + if currentRoute == nil { + t.Errorf("Expected a current route.") + } + vars := routeMatch.Vars + expectedVars := resultVars[shouldMatch] + if len(vars) != len(expectedVars) { + t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars) + } + for name, value := range vars { + if expectedVars[name] != value { + t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars) + } + } + } + } + + // 1st route -------------------------------------------------------------- + + // Everything match. + reset() + match(true) + + // Scheme doesn't match. + reset() + scheme = "http" + match(false) + + // Host doesn't match. + reset() + host = "www.mygoogle.com" + match(false) + + // Path doesn't match. + reset() + path = "/product/notdigits" + match(false) + + // Query doesn't match. + reset() + query = "?foo=baz" + match(false) + + // Method doesn't match. + reset() + method = "POST" + match(false) + + // Header doesn't match. + reset() + headers = map[string]string{} + match(false) + + // Everything match, again. + reset() + match(true) + + // 2nd route -------------------------------------------------------------- + + // Everything match. + reset2() + match(true) + + // Scheme doesn't match. + reset2() + scheme = "https" + match(false) + + // Host doesn't match. + reset2() + host = "sub.google.com" + match(false) + + // Path doesn't match. + reset2() + path = "/bar/product/42" + match(false) + + // Query doesn't match. + reset2() + query = "?foo=baz" + match(false) + + // Method doesn't match. + reset2() + method = "GET" + match(false) + + // Header doesn't match. + reset2() + headers = map[string]string{} + match(false) + + // Everything match, again. + reset2() + match(true) +} + +type headerMatcherTest struct { + matcher headerMatcher + headers map[string]string + result bool +} + +var headerMatcherTests = []headerMatcherTest{ + { + matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}), + headers: map[string]string{"X-Requested-With": "XMLHttpRequest"}, + result: true, + }, + { + matcher: headerMatcher(map[string]string{"x-requested-with": ""}), + headers: map[string]string{"X-Requested-With": "anything"}, + result: true, + }, + { + matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}), + headers: map[string]string{}, + result: false, + }, +} + +type hostMatcherTest struct { + matcher *Route + url string + vars map[string]string + result bool +} + +var hostMatcherTests = []hostMatcherTest{ + { + matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"), + url: "http://abc.def.ghi/", + vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"}, + result: true, + }, + { + matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"), + url: "http://a.b.c/", + vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"}, + result: false, + }, +} + +type methodMatcherTest struct { + matcher methodMatcher + method string + result bool +} + +var methodMatcherTests = []methodMatcherTest{ + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "GET", + result: true, + }, + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "POST", + result: true, + }, + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "PUT", + result: true, + }, + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "DELETE", + result: false, + }, +} + +type pathMatcherTest struct { + matcher *Route + url string + vars map[string]string + result bool +} + +var pathMatcherTests = []pathMatcherTest{ + { + matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"), + url: "http://localhost:8080/123/456/789", + vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"}, + result: true, + }, + { + matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"), + url: "http://localhost:8080/1/2/3", + vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"}, + result: false, + }, +} + +type schemeMatcherTest struct { + matcher schemeMatcher + url string + result bool +} + +var schemeMatcherTests = []schemeMatcherTest{ + { + matcher: schemeMatcher([]string{"http", "https"}), + url: "http://localhost:8080/", + result: true, + }, + { + matcher: schemeMatcher([]string{"http", "https"}), + url: "https://localhost:8080/", + result: true, + }, + { + matcher: schemeMatcher([]string{"https"}), + url: "http://localhost:8080/", + result: false, + }, + { + matcher: schemeMatcher([]string{"http"}), + url: "https://localhost:8080/", + result: false, + }, +} + +type urlBuildingTest struct { + route *Route + vars []string + url string +} + +var urlBuildingTests = []urlBuildingTest{ + { + route: new(Route).Host("foo.domain.com"), + vars: []string{}, + url: "http://foo.domain.com", + }, + { + route: new(Route).Host("{subdomain}.domain.com"), + vars: []string{"subdomain", "bar"}, + url: "http://bar.domain.com", + }, + { + route: new(Route).Host("foo.domain.com").Path("/articles"), + vars: []string{}, + url: "http://foo.domain.com/articles", + }, + { + route: new(Route).Path("/articles"), + vars: []string{}, + url: "/articles", + }, + { + route: new(Route).Path("/articles/{category}/{id:[0-9]+}"), + vars: []string{"category", "technology", "id", "42"}, + url: "/articles/technology/42", + }, + { + route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"), + vars: []string{"subdomain", "foo", "category", "technology", "id", "42"}, + url: "http://foo.domain.com/articles/technology/42", + }, +} + +func TestHeaderMatcher(t *testing.T) { + for _, v := range headerMatcherTests { + request, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + for key, value := range v.headers { + request.Header.Add(key, value) + } + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, request.Header) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, request.Header) + } + } + } +} + +func TestHostMatcher(t *testing.T) { + for _, v := range hostMatcherTests { + request, _ := http.NewRequest("GET", v.url, nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + vars := routeMatch.Vars + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.url) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.url) + } + } + if result { + if len(vars) != len(v.vars) { + t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars)) + } + for name, value := range vars { + if v.vars[name] != value { + t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value) + } + } + } else { + if len(vars) != 0 { + t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars)) + } + } + } +} + +func TestMethodMatcher(t *testing.T) { + for _, v := range methodMatcherTests { + request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.method) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.method) + } + } + } +} + +func TestPathMatcher(t *testing.T) { + for _, v := range pathMatcherTests { + request, _ := http.NewRequest("GET", v.url, nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + vars := routeMatch.Vars + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.url) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.url) + } + } + if result { + if len(vars) != len(v.vars) { + t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars)) + } + for name, value := range vars { + if v.vars[name] != value { + t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value) + } + } + } else { + if len(vars) != 0 { + t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars)) + } + } + } +} + +func TestSchemeMatcher(t *testing.T) { + for _, v := range schemeMatcherTests { + request, _ := http.NewRequest("GET", v.url, nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.url) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.url) + } + } + } +} + +func TestUrlBuilding(t *testing.T) { + + for _, v := range urlBuildingTests { + u, _ := v.route.URL(v.vars...) + url := u.String() + if url != v.url { + t.Errorf("expected %v, got %v", v.url, url) + /* + reversePath := "" + reverseHost := "" + if v.route.pathTemplate != nil { + reversePath = v.route.pathTemplate.Reverse + } + if v.route.hostTemplate != nil { + reverseHost = v.route.hostTemplate.Reverse + } + + t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost) + */ + } + } + + ArticleHandler := func(w http.ResponseWriter, r *http.Request) { + } + + router := NewRouter() + router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article") + + url, _ := router.Get("article").URL("category", "technology", "id", "42") + expected := "/articles/technology/42" + if url.String() != expected { + t.Errorf("Expected %v, got %v", expected, url.String()) + } +} + +func TestMatchedRouteName(t *testing.T) { + routeName := "stock" + router := NewRouter() + route := router.NewRoute().Path("/products/").Name(routeName) + + url := "http://www.example.com/products/" + request, _ := http.NewRequest("GET", url, nil) + var rv RouteMatch + ok := router.Match(request, &rv) + + if !ok || rv.Route != route { + t.Errorf("Expected same route, got %+v.", rv.Route) + } + + retName := rv.Route.GetName() + if retName != routeName { + t.Errorf("Expected %q, got %q.", routeName, retName) + } +} + +func TestSubRouting(t *testing.T) { + // Example from docs. + router := NewRouter() + subrouter := router.NewRoute().Host("www.example.com").Subrouter() + route := subrouter.NewRoute().Path("/products/").Name("products") + + url := "http://www.example.com/products/" + request, _ := http.NewRequest("GET", url, nil) + var rv RouteMatch + ok := router.Match(request, &rv) + + if !ok || rv.Route != route { + t.Errorf("Expected same route, got %+v.", rv.Route) + } + + u, _ := router.Get("products").URL() + builtURL := u.String() + // Yay, subroute aware of the domain when building! + if builtURL != url { + t.Errorf("Expected %q, got %q.", url, builtURL) + } +} + +func TestVariableNames(t *testing.T) { + route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}") + if route.err == nil { + t.Errorf("Expected error for duplicated variable names") + } +} + +func TestRedirectSlash(t *testing.T) { + var route *Route + var routeMatch RouteMatch + r := NewRouter() + + r.StrictSlash(false) + route = r.NewRoute() + if route.strictSlash != false { + t.Errorf("Expected false redirectSlash.") + } + + r.StrictSlash(true) + route = r.NewRoute() + if route.strictSlash != true { + t.Errorf("Expected true redirectSlash.") + } + + route = new(Route) + route.strictSlash = true + route.Path("/{arg1}/{arg2:[0-9]+}/") + request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil) + routeMatch = RouteMatch{} + _ = route.Match(request, &routeMatch) + vars := routeMatch.Vars + if vars["arg1"] != "foo" { + t.Errorf("Expected foo.") + } + if vars["arg2"] != "123" { + t.Errorf("Expected 123.") + } + rsp := NewRecorder() + routeMatch.Handler.ServeHTTP(rsp, request) + if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" { + t.Errorf("Expected redirect header.") + } + + route = new(Route) + route.strictSlash = true + route.Path("/{arg1}/{arg2:[0-9]+}") + request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil) + routeMatch = RouteMatch{} + _ = route.Match(request, &routeMatch) + vars = routeMatch.Vars + if vars["arg1"] != "foo" { + t.Errorf("Expected foo.") + } + if vars["arg2"] != "123" { + t.Errorf("Expected 123.") + } + rsp = NewRecorder() + routeMatch.Handler.ServeHTTP(rsp, request) + if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" { + t.Errorf("Expected redirect header.") + } +} + +// Test for the new regexp library, still not available in stable Go. +func TestNewRegexp(t *testing.T) { + var p *routeRegexp + var matches []string + + tests := map[string]map[string][]string{ + "/{foo:a{2}}": { + "/a": nil, + "/aa": {"aa"}, + "/aaa": nil, + "/aaaa": nil, + }, + "/{foo:a{2,}}": { + "/a": nil, + "/aa": {"aa"}, + "/aaa": {"aaa"}, + "/aaaa": {"aaaa"}, + }, + "/{foo:a{2,3}}": { + "/a": nil, + "/aa": {"aa"}, + "/aaa": {"aaa"}, + "/aaaa": nil, + }, + "/{foo:[a-z]{3}}/{bar:[a-z]{2}}": { + "/a": nil, + "/ab": nil, + "/abc": nil, + "/abcd": nil, + "/abc/ab": {"abc", "ab"}, + "/abc/abc": nil, + "/abcd/ab": nil, + }, + `/{foo:\w{3,}}/{bar:\d{2,}}`: { + "/a": nil, + "/ab": nil, + "/abc": nil, + "/abc/1": nil, + "/abc/12": {"abc", "12"}, + "/abcd/12": {"abcd", "12"}, + "/abcd/123": {"abcd", "123"}, + }, + } + + for pattern, paths := range tests { + p, _ = newRouteRegexp(pattern, false, false, false, false) + for path, result := range paths { + matches = p.regexp.FindStringSubmatch(path) + if result == nil { + if matches != nil { + t.Errorf("%v should not match %v.", pattern, path) + } + } else { + if len(matches) != len(result)+1 { + t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches)) + } else { + for k, v := range result { + if matches[k+1] != v { + t.Errorf("Expected %v, got %v.", v, matches[k+1]) + } + } + } + } + } + } +} diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go new file mode 100644 index 0000000..08710bc --- /dev/null +++ b/vendor/github.com/gorilla/mux/regexp.go @@ -0,0 +1,312 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "bytes" + "fmt" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" +) + +// newRouteRegexp parses a route template and returns a routeRegexp, +// used to match a host, a path or a query string. +// +// It will extract named variables, assemble a regexp to be matched, create +// a "reverse" template to build URLs and compile regexps to validate variable +// values used in URL building. +// +// Previously we accepted only Python-like identifiers for variable +// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that +// name and pattern can't be empty, and names can't contain a colon. +func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) { + // Check if it is well-formed. + idxs, errBraces := braceIndices(tpl) + if errBraces != nil { + return nil, errBraces + } + // Backup the original. + template := tpl + // Now let's parse it. + defaultPattern := "[^/]+" + if matchQuery { + defaultPattern = "[^?&]*" + } else if matchHost { + defaultPattern = "[^.]+" + matchPrefix = false + } + // Only match strict slash if not matching + if matchPrefix || matchHost || matchQuery { + strictSlash = false + } + // Set a flag for strictSlash. + endSlash := false + if strictSlash && strings.HasSuffix(tpl, "/") { + tpl = tpl[:len(tpl)-1] + endSlash = true + } + varsN := make([]string, len(idxs)/2) + varsR := make([]*regexp.Regexp, len(idxs)/2) + pattern := bytes.NewBufferString("") + pattern.WriteByte('^') + reverse := bytes.NewBufferString("") + var end int + var err error + for i := 0; i < len(idxs); i += 2 { + // Set all values we are interested in. + raw := tpl[end:idxs[i]] + end = idxs[i+1] + parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2) + name := parts[0] + patt := defaultPattern + if len(parts) == 2 { + patt = parts[1] + } + // Name or pattern can't be empty. + if name == "" || patt == "" { + return nil, fmt.Errorf("mux: missing name or pattern in %q", + tpl[idxs[i]:end]) + } + // Build the regexp pattern. + fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), patt) + + // Build the reverse template. + fmt.Fprintf(reverse, "%s%%s", raw) + + // Append variable name and compiled pattern. + varsN[i/2] = name + varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt)) + if err != nil { + return nil, err + } + } + // Add the remaining. + raw := tpl[end:] + pattern.WriteString(regexp.QuoteMeta(raw)) + if strictSlash { + pattern.WriteString("[/]?") + } + if matchQuery { + // Add the default pattern if the query value is empty + if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" { + pattern.WriteString(defaultPattern) + } + } + if !matchPrefix { + pattern.WriteByte('$') + } + reverse.WriteString(raw) + if endSlash { + reverse.WriteByte('/') + } + // Compile full regexp. + reg, errCompile := regexp.Compile(pattern.String()) + if errCompile != nil { + return nil, errCompile + } + // Done! + return &routeRegexp{ + template: template, + matchHost: matchHost, + matchQuery: matchQuery, + strictSlash: strictSlash, + regexp: reg, + reverse: reverse.String(), + varsN: varsN, + varsR: varsR, + }, nil +} + +// routeRegexp stores a regexp to match a host or path and information to +// collect and validate route variables. +type routeRegexp struct { + // The unmodified template. + template string + // True for host match, false for path or query string match. + matchHost bool + // True for query string match, false for path and host match. + matchQuery bool + // The strictSlash value defined on the route, but disabled if PathPrefix was used. + strictSlash bool + // Expanded regexp. + regexp *regexp.Regexp + // Reverse template. + reverse string + // Variable names. + varsN []string + // Variable regexps (validators). + varsR []*regexp.Regexp +} + +// Match matches the regexp against the URL host or path. +func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { + if !r.matchHost { + if r.matchQuery { + return r.matchQueryString(req) + } + + return r.regexp.MatchString(req.URL.Path) + } + + return r.regexp.MatchString(getHost(req)) +} + +// url builds a URL part using the given values. +func (r *routeRegexp) url(values map[string]string) (string, error) { + urlValues := make([]interface{}, len(r.varsN)) + for k, v := range r.varsN { + value, ok := values[v] + if !ok { + return "", fmt.Errorf("mux: missing route variable %q", v) + } + urlValues[k] = value + } + rv := fmt.Sprintf(r.reverse, urlValues...) + if !r.regexp.MatchString(rv) { + // The URL is checked against the full regexp, instead of checking + // individual variables. This is faster but to provide a good error + // message, we check individual regexps if the URL doesn't match. + for k, v := range r.varsN { + if !r.varsR[k].MatchString(values[v]) { + return "", fmt.Errorf( + "mux: variable %q doesn't match, expected %q", values[v], + r.varsR[k].String()) + } + } + } + return rv, nil +} + +// getURLQuery returns a single query parameter from a request URL. +// For a URL with foo=bar&baz=ding, we return only the relevant key +// value pair for the routeRegexp. +func (r *routeRegexp) getURLQuery(req *http.Request) string { + if !r.matchQuery { + return "" + } + templateKey := strings.SplitN(r.template, "=", 2)[0] + for key, vals := range req.URL.Query() { + if key == templateKey && len(vals) > 0 { + return key + "=" + vals[0] + } + } + return "" +} + +func (r *routeRegexp) matchQueryString(req *http.Request) bool { + return r.regexp.MatchString(r.getURLQuery(req)) +} + +// braceIndices returns the first level curly brace indices from a string. +// It returns an error in case of unbalanced braces. +func braceIndices(s string) ([]int, error) { + var level, idx int + var idxs []int + for i := 0; i < len(s); i++ { + switch s[i] { + case '{': + if level++; level == 1 { + idx = i + } + case '}': + if level--; level == 0 { + idxs = append(idxs, idx, i+1) + } else if level < 0 { + return nil, fmt.Errorf("mux: unbalanced braces in %q", s) + } + } + } + if level != 0 { + return nil, fmt.Errorf("mux: unbalanced braces in %q", s) + } + return idxs, nil +} + +// varGroupName builds a capturing group name for the indexed variable. +func varGroupName(idx int) string { + return "v" + strconv.Itoa(idx) +} + +// ---------------------------------------------------------------------------- +// routeRegexpGroup +// ---------------------------------------------------------------------------- + +// routeRegexpGroup groups the route matchers that carry variables. +type routeRegexpGroup struct { + host *routeRegexp + path *routeRegexp + queries []*routeRegexp +} + +// setMatch extracts the variables from the URL once a route matches. +func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) { + // Store host variables. + if v.host != nil { + host := getHost(req) + matches := v.host.regexp.FindStringSubmatchIndex(host) + if len(matches) > 0 { + extractVars(host, matches, v.host.varsN, m.Vars) + } + } + // Store path variables. + if v.path != nil { + matches := v.path.regexp.FindStringSubmatchIndex(req.URL.Path) + if len(matches) > 0 { + extractVars(req.URL.Path, matches, v.path.varsN, m.Vars) + // Check if we should redirect. + if v.path.strictSlash { + p1 := strings.HasSuffix(req.URL.Path, "/") + p2 := strings.HasSuffix(v.path.template, "/") + if p1 != p2 { + u, _ := url.Parse(req.URL.String()) + if p1 { + u.Path = u.Path[:len(u.Path)-1] + } else { + u.Path += "/" + } + m.Handler = http.RedirectHandler(u.String(), 301) + } + } + } + } + // Store query string variables. + for _, q := range v.queries { + queryURL := q.getURLQuery(req) + matches := q.regexp.FindStringSubmatchIndex(queryURL) + if len(matches) > 0 { + extractVars(queryURL, matches, q.varsN, m.Vars) + } + } +} + +// getHost tries its best to return the request host. +func getHost(r *http.Request) string { + if r.URL.IsAbs() { + return r.URL.Host + } + host := r.Host + // Slice off any port information. + if i := strings.Index(host, ":"); i != -1 { + host = host[:i] + } + return host + +} + +func extractVars(input string, matches []int, names []string, output map[string]string) { + matchesCount := 0 + prevEnd := -1 + for i := 2; i < len(matches) && matchesCount < len(names); i += 2 { + if prevEnd < matches[i+1] { + value := input[matches[i]:matches[i+1]] + output[names[matchesCount]] = value + prevEnd = matches[i+1] + matchesCount++ + } + } +} diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go new file mode 100644 index 0000000..bf92af2 --- /dev/null +++ b/vendor/github.com/gorilla/mux/route.go @@ -0,0 +1,627 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "errors" + "fmt" + "net/http" + "net/url" + "regexp" + "strings" +) + +// Route stores information to match a request and build URLs. +type Route struct { + // Parent where the route was registered (a Router). + parent parentRoute + // Request handler for the route. + handler http.Handler + // List of matchers. + matchers []matcher + // Manager for the variables from host and path. + regexp *routeRegexpGroup + // If true, when the path pattern is "/path/", accessing "/path" will + // redirect to the former and vice versa. + strictSlash bool + // If true, this route never matches: it is only used to build URLs. + buildOnly bool + // The name used to build URLs. + name string + // Error resulted from building a route. + err error + + buildVarsFunc BuildVarsFunc +} + +// Match matches the route against the request. +func (r *Route) Match(req *http.Request, match *RouteMatch) bool { + if r.buildOnly || r.err != nil { + return false + } + // Match everything. + for _, m := range r.matchers { + if matched := m.Match(req, match); !matched { + return false + } + } + // Yay, we have a match. Let's collect some info about it. + if match.Route == nil { + match.Route = r + } + if match.Handler == nil { + match.Handler = r.handler + } + if match.Vars == nil { + match.Vars = make(map[string]string) + } + // Set variables. + if r.regexp != nil { + r.regexp.setMatch(req, match, r) + } + return true +} + +// ---------------------------------------------------------------------------- +// Route attributes +// ---------------------------------------------------------------------------- + +// GetError returns an error resulted from building the route, if any. +func (r *Route) GetError() error { + return r.err +} + +// BuildOnly sets the route to never match: it is only used to build URLs. +func (r *Route) BuildOnly() *Route { + r.buildOnly = true + return r +} + +// Handler -------------------------------------------------------------------- + +// Handler sets a handler for the route. +func (r *Route) Handler(handler http.Handler) *Route { + if r.err == nil { + r.handler = handler + } + return r +} + +// HandlerFunc sets a handler function for the route. +func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route { + return r.Handler(http.HandlerFunc(f)) +} + +// GetHandler returns the handler for the route, if any. +func (r *Route) GetHandler() http.Handler { + return r.handler +} + +// Name ----------------------------------------------------------------------- + +// Name sets the name for the route, used to build URLs. +// If the name was registered already it will be overwritten. +func (r *Route) Name(name string) *Route { + if r.name != "" { + r.err = fmt.Errorf("mux: route already has name %q, can't set %q", + r.name, name) + } + if r.err == nil { + r.name = name + r.getNamedRoutes()[name] = r + } + return r +} + +// GetName returns the name for the route, if any. +func (r *Route) GetName() string { + return r.name +} + +// ---------------------------------------------------------------------------- +// Matchers +// ---------------------------------------------------------------------------- + +// matcher types try to match a request. +type matcher interface { + Match(*http.Request, *RouteMatch) bool +} + +// addMatcher adds a matcher to the route. +func (r *Route) addMatcher(m matcher) *Route { + if r.err == nil { + r.matchers = append(r.matchers, m) + } + return r +} + +// addRegexpMatcher adds a host or path matcher and builder to a route. +func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error { + if r.err != nil { + return r.err + } + r.regexp = r.getRegexpGroup() + if !matchHost && !matchQuery { + if len(tpl) == 0 || tpl[0] != '/' { + return fmt.Errorf("mux: path must start with a slash, got %q", tpl) + } + if r.regexp.path != nil { + tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl + } + } + rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash) + if err != nil { + return err + } + for _, q := range r.regexp.queries { + if err = uniqueVars(rr.varsN, q.varsN); err != nil { + return err + } + } + if matchHost { + if r.regexp.path != nil { + if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil { + return err + } + } + r.regexp.host = rr + } else { + if r.regexp.host != nil { + if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil { + return err + } + } + if matchQuery { + r.regexp.queries = append(r.regexp.queries, rr) + } else { + r.regexp.path = rr + } + } + r.addMatcher(rr) + return nil +} + +// Headers -------------------------------------------------------------------- + +// headerMatcher matches the request against header values. +type headerMatcher map[string]string + +func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool { + return matchMapWithString(m, r.Header, true) +} + +// Headers adds a matcher for request header values. +// It accepts a sequence of key/value pairs to be matched. For example: +// +// r := mux.NewRouter() +// r.Headers("Content-Type", "application/json", +// "X-Requested-With", "XMLHttpRequest") +// +// The above route will only match if both request header values match. +// If the value is an empty string, it will match any value if the key is set. +func (r *Route) Headers(pairs ...string) *Route { + if r.err == nil { + var headers map[string]string + headers, r.err = mapFromPairsToString(pairs...) + return r.addMatcher(headerMatcher(headers)) + } + return r +} + +// headerRegexMatcher matches the request against the route given a regex for the header +type headerRegexMatcher map[string]*regexp.Regexp + +func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool { + return matchMapWithRegex(m, r.Header, true) +} + +// HeadersRegexp accepts a sequence of key/value pairs, where the value has regex +// support. For example: +// +// r := mux.NewRouter() +// r.HeadersRegexp("Content-Type", "application/(text|json)", +// "X-Requested-With", "XMLHttpRequest") +// +// The above route will only match if both the request header matches both regular expressions. +// It the value is an empty string, it will match any value if the key is set. +func (r *Route) HeadersRegexp(pairs ...string) *Route { + if r.err == nil { + var headers map[string]*regexp.Regexp + headers, r.err = mapFromPairsToRegex(pairs...) + return r.addMatcher(headerRegexMatcher(headers)) + } + return r +} + +// Host ----------------------------------------------------------------------- + +// Host adds a matcher for the URL host. +// It accepts a template with zero or more URL variables enclosed by {}. +// Variables can define an optional regexp pattern to be matched: +// +// - {name} matches anything until the next dot. +// +// - {name:pattern} matches the given regexp pattern. +// +// For example: +// +// r := mux.NewRouter() +// r.Host("www.example.com") +// r.Host("{subdomain}.domain.com") +// r.Host("{subdomain:[a-z]+}.domain.com") +// +// Variable names must be unique in a given route. They can be retrieved +// calling mux.Vars(request). +func (r *Route) Host(tpl string) *Route { + r.err = r.addRegexpMatcher(tpl, true, false, false) + return r +} + +// MatcherFunc ---------------------------------------------------------------- + +// MatcherFunc is the function signature used by custom matchers. +type MatcherFunc func(*http.Request, *RouteMatch) bool + +// Match returns the match for a given request. +func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool { + return m(r, match) +} + +// MatcherFunc adds a custom function to be used as request matcher. +func (r *Route) MatcherFunc(f MatcherFunc) *Route { + return r.addMatcher(f) +} + +// Methods -------------------------------------------------------------------- + +// methodMatcher matches the request against HTTP methods. +type methodMatcher []string + +func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool { + return matchInArray(m, r.Method) +} + +// Methods adds a matcher for HTTP methods. +// It accepts a sequence of one or more methods to be matched, e.g.: +// "GET", "POST", "PUT". +func (r *Route) Methods(methods ...string) *Route { + for k, v := range methods { + methods[k] = strings.ToUpper(v) + } + return r.addMatcher(methodMatcher(methods)) +} + +// Path ----------------------------------------------------------------------- + +// Path adds a matcher for the URL path. +// It accepts a template with zero or more URL variables enclosed by {}. The +// template must start with a "/". +// Variables can define an optional regexp pattern to be matched: +// +// - {name} matches anything until the next slash. +// +// - {name:pattern} matches the given regexp pattern. +// +// For example: +// +// r := mux.NewRouter() +// r.Path("/products/").Handler(ProductsHandler) +// r.Path("/products/{key}").Handler(ProductsHandler) +// r.Path("/articles/{category}/{id:[0-9]+}"). +// Handler(ArticleHandler) +// +// Variable names must be unique in a given route. They can be retrieved +// calling mux.Vars(request). +func (r *Route) Path(tpl string) *Route { + r.err = r.addRegexpMatcher(tpl, false, false, false) + return r +} + +// PathPrefix ----------------------------------------------------------------- + +// PathPrefix adds a matcher for the URL path prefix. This matches if the given +// template is a prefix of the full URL path. See Route.Path() for details on +// the tpl argument. +// +// Note that it does not treat slashes specially ("/foobar/" will be matched by +// the prefix "/foo") so you may want to use a trailing slash here. +// +// Also note that the setting of Router.StrictSlash() has no effect on routes +// with a PathPrefix matcher. +func (r *Route) PathPrefix(tpl string) *Route { + r.err = r.addRegexpMatcher(tpl, false, true, false) + return r +} + +// Query ---------------------------------------------------------------------- + +// Queries adds a matcher for URL query values. +// It accepts a sequence of key/value pairs. Values may define variables. +// For example: +// +// r := mux.NewRouter() +// r.Queries("foo", "bar", "id", "{id:[0-9]+}") +// +// The above route will only match if the URL contains the defined queries +// values, e.g.: ?foo=bar&id=42. +// +// It the value is an empty string, it will match any value if the key is set. +// +// Variables can define an optional regexp pattern to be matched: +// +// - {name} matches anything until the next slash. +// +// - {name:pattern} matches the given regexp pattern. +func (r *Route) Queries(pairs ...string) *Route { + length := len(pairs) + if length%2 != 0 { + r.err = fmt.Errorf( + "mux: number of parameters must be multiple of 2, got %v", pairs) + return nil + } + for i := 0; i < length; i += 2 { + if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil { + return r + } + } + + return r +} + +// Schemes -------------------------------------------------------------------- + +// schemeMatcher matches the request against URL schemes. +type schemeMatcher []string + +func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool { + return matchInArray(m, r.URL.Scheme) +} + +// Schemes adds a matcher for URL schemes. +// It accepts a sequence of schemes to be matched, e.g.: "http", "https". +func (r *Route) Schemes(schemes ...string) *Route { + for k, v := range schemes { + schemes[k] = strings.ToLower(v) + } + return r.addMatcher(schemeMatcher(schemes)) +} + +// BuildVarsFunc -------------------------------------------------------------- + +// BuildVarsFunc is the function signature used by custom build variable +// functions (which can modify route variables before a route's URL is built). +type BuildVarsFunc func(map[string]string) map[string]string + +// BuildVarsFunc adds a custom function to be used to modify build variables +// before a route's URL is built. +func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { + r.buildVarsFunc = f + return r +} + +// Subrouter ------------------------------------------------------------------ + +// Subrouter creates a subrouter for the route. +// +// It will test the inner routes only if the parent route matched. For example: +// +// r := mux.NewRouter() +// s := r.Host("www.example.com").Subrouter() +// s.HandleFunc("/products/", ProductsHandler) +// s.HandleFunc("/products/{key}", ProductHandler) +// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) +// +// Here, the routes registered in the subrouter won't be tested if the host +// doesn't match. +func (r *Route) Subrouter() *Router { + router := &Router{parent: r, strictSlash: r.strictSlash} + r.addMatcher(router) + return router +} + +// ---------------------------------------------------------------------------- +// URL building +// ---------------------------------------------------------------------------- + +// URL builds a URL for the route. +// +// It accepts a sequence of key/value pairs for the route variables. For +// example, given this route: +// +// r := mux.NewRouter() +// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). +// Name("article") +// +// ...a URL for it can be built using: +// +// url, err := r.Get("article").URL("category", "technology", "id", "42") +// +// ...which will return an url.URL with the following path: +// +// "/articles/technology/42" +// +// This also works for host variables: +// +// r := mux.NewRouter() +// r.Host("{subdomain}.domain.com"). +// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). +// Name("article") +// +// // url.String() will be "http://news.domain.com/articles/technology/42" +// url, err := r.Get("article").URL("subdomain", "news", +// "category", "technology", +// "id", "42") +// +// All variables defined in the route are required, and their values must +// conform to the corresponding patterns. +func (r *Route) URL(pairs ...string) (*url.URL, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp == nil { + return nil, errors.New("mux: route doesn't have a host or path") + } + values, err := r.prepareVars(pairs...) + if err != nil { + return nil, err + } + var scheme, host, path string + if r.regexp.host != nil { + // Set a default scheme. + scheme = "http" + if host, err = r.regexp.host.url(values); err != nil { + return nil, err + } + } + if r.regexp.path != nil { + if path, err = r.regexp.path.url(values); err != nil { + return nil, err + } + } + return &url.URL{ + Scheme: scheme, + Host: host, + Path: path, + }, nil +} + +// URLHost builds the host part of the URL for a route. See Route.URL(). +// +// The route must have a host defined. +func (r *Route) URLHost(pairs ...string) (*url.URL, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp == nil || r.regexp.host == nil { + return nil, errors.New("mux: route doesn't have a host") + } + values, err := r.prepareVars(pairs...) + if err != nil { + return nil, err + } + host, err := r.regexp.host.url(values) + if err != nil { + return nil, err + } + return &url.URL{ + Scheme: "http", + Host: host, + }, nil +} + +// URLPath builds the path part of the URL for a route. See Route.URL(). +// +// The route must have a path defined. +func (r *Route) URLPath(pairs ...string) (*url.URL, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp == nil || r.regexp.path == nil { + return nil, errors.New("mux: route doesn't have a path") + } + values, err := r.prepareVars(pairs...) + if err != nil { + return nil, err + } + path, err := r.regexp.path.url(values) + if err != nil { + return nil, err + } + return &url.URL{ + Path: path, + }, nil +} + +// GetPathTemplate returns the template used to build the +// route match. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An error will be returned if the route does not define a path. +func (r *Route) GetPathTemplate() (string, error) { + if r.err != nil { + return "", r.err + } + if r.regexp == nil || r.regexp.path == nil { + return "", errors.New("mux: route doesn't have a path") + } + return r.regexp.path.template, nil +} + +// GetHostTemplate returns the template used to build the +// route match. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An error will be returned if the route does not define a host. +func (r *Route) GetHostTemplate() (string, error) { + if r.err != nil { + return "", r.err + } + if r.regexp == nil || r.regexp.host == nil { + return "", errors.New("mux: route doesn't have a host") + } + return r.regexp.host.template, nil +} + +// prepareVars converts the route variable pairs into a map. If the route has a +// BuildVarsFunc, it is invoked. +func (r *Route) prepareVars(pairs ...string) (map[string]string, error) { + m, err := mapFromPairsToString(pairs...) + if err != nil { + return nil, err + } + return r.buildVars(m), nil +} + +func (r *Route) buildVars(m map[string]string) map[string]string { + if r.parent != nil { + m = r.parent.buildVars(m) + } + if r.buildVarsFunc != nil { + m = r.buildVarsFunc(m) + } + return m +} + +// ---------------------------------------------------------------------------- +// parentRoute +// ---------------------------------------------------------------------------- + +// parentRoute allows routes to know about parent host and path definitions. +type parentRoute interface { + getNamedRoutes() map[string]*Route + getRegexpGroup() *routeRegexpGroup + buildVars(map[string]string) map[string]string +} + +// getNamedRoutes returns the map where named routes are registered. +func (r *Route) getNamedRoutes() map[string]*Route { + if r.parent == nil { + // During tests router is not always set. + r.parent = NewRouter() + } + return r.parent.getNamedRoutes() +} + +// getRegexpGroup returns regexp definitions from this route. +func (r *Route) getRegexpGroup() *routeRegexpGroup { + if r.regexp == nil { + if r.parent == nil { + // During tests router is not always set. + r.parent = NewRouter() + } + regexp := r.parent.getRegexpGroup() + if regexp == nil { + r.regexp = new(routeRegexpGroup) + } else { + // Copy. + r.regexp = &routeRegexpGroup{ + host: regexp.host, + path: regexp.path, + queries: regexp.queries, + } + } + } + return r.regexp +} diff --git a/vendor/github.com/onsi/gomega/.gitignore b/vendor/github.com/onsi/gomega/.gitignore new file mode 100644 index 0000000..5514532 --- /dev/null +++ b/vendor/github.com/onsi/gomega/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +*.test +. diff --git a/vendor/github.com/onsi/gomega/.travis.yml b/vendor/github.com/onsi/gomega/.travis.yml new file mode 100644 index 0000000..2ecdf95 --- /dev/null +++ b/vendor/github.com/onsi/gomega/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.3 + +install: + - go get -v ./... + - go get github.com/onsi/ginkgo + - go install github.com/onsi/ginkgo/ginkgo + +script: $HOME/gopath/bin/ginkgo -r --randomizeAllSpecs --failOnPending --randomizeSuites --race diff --git a/vendor/github.com/onsi/gomega/CHANGELOG.md b/vendor/github.com/onsi/gomega/CHANGELOG.md new file mode 100644 index 0000000..cbcdd08 --- /dev/null +++ b/vendor/github.com/onsi/gomega/CHANGELOG.md @@ -0,0 +1,49 @@ +## 1.0 (8/2/2014) + +No changes. Dropping "beta" from the version number. + +## 1.0.0-beta (7/8/2014) +Breaking Changes: + +- Changed OmegaMatcher interface. Instead of having `Match` return failure messages, two new methods `FailureMessage` and `NegatedFailureMessage` are called instead. +- Moved and renamed OmegaFailHandler to types.GomegaFailHandler and OmegaMatcher to types.GomegaMatcher. Any references to OmegaMatcher in any custom matchers will need to be changed to point to types.GomegaMatcher + +New Test-Support Features: + +- `ghttp`: supports testing http clients + - Provides a flexible fake http server + - Provides a collection of chainable http handlers that perform assertions. +- `gbytes`: supports making ordered assertions against streams of data + - Provides a `gbytes.Buffer` + - Provides a `Say` matcher to perform ordered assertions against output data +- `gexec`: supports testing external processes + - Provides support for building Go binaries + - Wraps and starts `exec.Cmd` commands + - Makes it easy to assert against stdout and stderr + - Makes it easy to send signals and wait for processes to exit + - Provides an `Exit` matcher to assert against exit code. + +DSL Changes: + +- `Eventually` and `Consistently` can accept `time.Duration` interval and polling inputs. +- The default timeouts for `Eventually` and `Consistently` are now configurable. + +New Matchers: + +- `ConsistOf`: order-independent assertion against the elements of an array/slice or keys of a map. +- `BeTemporally`: like `BeNumerically` but for `time.Time` +- `HaveKeyWithValue`: asserts a map has a given key with the given value. + +Updated Matchers: + +- `Receive` matcher can take a matcher as an argument and passes only if the channel under test receives an objet that satisfies the passed-in matcher. +- Matchers that implement `MatchMayChangeInTheFuture(actual interface{}) bool` can inform `Eventually` and/or `Consistently` when a match has no chance of changing status in the future. For example, `Receive` returns `false` when a channel is closed. + +Misc: + +- Start using semantic versioning +- Start maintaining changelog + +Major refactor: + +- Pull out Gomega's internal to `internal` diff --git a/vendor/github.com/onsi/gomega/MIT.LICENSE b/vendor/github.com/onsi/gomega/MIT.LICENSE new file mode 100644 index 0000000..941ee5b --- /dev/null +++ b/vendor/github.com/onsi/gomega/MIT.LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013 Onsi Fakhouri + +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. \ No newline at end of file diff --git a/vendor/github.com/onsi/gomega/README.md b/vendor/github.com/onsi/gomega/README.md new file mode 100644 index 0000000..9520451 --- /dev/null +++ b/vendor/github.com/onsi/gomega/README.md @@ -0,0 +1,17 @@ +![Gomega: Ginkgo's Preferred Matcher Library](http://onsi.github.io/gomega/images/gomega.png) + +[![Build Status](https://travis-ci.org/onsi/gomega.png)](https://travis-ci.org/onsi/gomega) + +Jump straight to the [docs](http://onsi.github.io/gomega/) to learn about Gomega, including a list of [all available matchers](http://onsi.github.io/gomega/#provided_matchers). + +To discuss Gomega and get updates, join the [google group](https://groups.google.com/d/forum/ginkgo-and-gomega). + +## [Ginkgo](http://github.com/onsi/ginkgo): a BDD Testing Framework for Golang + +Learn more about Ginkgo [here](http://onsi.github.io/ginkgo/) + +## License + +Gomega is MIT-Licensed + +The `ConsistOf` matcher uses [goraph](https://github.com/amitkgupta/goraph) which is embedded in the source to simplify distribution. goraph has an MIT license. diff --git a/vendor/github.com/onsi/gomega/format/format.go b/vendor/github.com/onsi/gomega/format/format.go new file mode 100644 index 0000000..ec9c91a --- /dev/null +++ b/vendor/github.com/onsi/gomega/format/format.go @@ -0,0 +1,276 @@ +/* +Gomega's format package pretty-prints objects. It explores input objects recursively and generates formatted, indented output with type information. +*/ +package format + +import ( + "fmt" + "reflect" + "strings" +) + +// Use MaxDepth to set the maximum recursion depth when printing deeply nested objects +var MaxDepth = uint(10) + +/* +By default, all objects (even those that implement fmt.Stringer and fmt.GoStringer) are recursively inspected to generate output. + +Set UseStringerRepresentation = true to use GoString (for fmt.GoStringers) or String (for fmt.Stringer) instead. + +Note that GoString and String don't always have all the information you need to understand why a test failed! +*/ +var UseStringerRepresentation = false + +//The default indentation string emitted by the format package +var Indent = " " + +var longFormThreshold = 20 + +/* +Generates a formatted matcher success/failure message of the form: + + Expected + + + + +If expected is omited, then the message looks like: + + Expected + + +*/ +func Message(actual interface{}, message string, expected ...interface{}) string { + if len(expected) == 0 { + return fmt.Sprintf("Expected\n%s\n%s", Object(actual, 1), message) + } else { + return fmt.Sprintf("Expected\n%s\n%s\n%s", Object(actual, 1), message, Object(expected[0], 1)) + } +} + +/* +Pretty prints the passed in object at the passed in indentation level. + +Object recurses into deeply nested objects emitting pretty-printed representations of their components. + +Modify format.MaxDepth to control how deep the recursion is allowed to go +Set format.UseStringerRepresentation to true to return object.GoString() or object.String() when available instead of +recursing into the object. +*/ +func Object(object interface{}, indentation uint) string { + indent := strings.Repeat(Indent, int(indentation)) + value := reflect.ValueOf(object) + return fmt.Sprintf("%s<%s>: %s", indent, formatType(object), formatValue(value, indentation)) +} + +/* +IndentString takes a string and indents each line by the specified amount. +*/ +func IndentString(s string, indentation uint) string { + components := strings.Split(s, "\n") + result := "" + indent := strings.Repeat(Indent, int(indentation)) + for i, component := range components { + result += indent + component + if i < len(components)-1 { + result += "\n" + } + } + + return result +} + +func formatType(object interface{}) string { + t := reflect.TypeOf(object) + if t == nil { + return "nil" + } + switch t.Kind() { + case reflect.Chan: + v := reflect.ValueOf(object) + return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap()) + case reflect.Ptr: + return fmt.Sprintf("%T | %p", object, object) + case reflect.Slice: + v := reflect.ValueOf(object) + return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap()) + case reflect.Map: + v := reflect.ValueOf(object) + return fmt.Sprintf("%T | len:%d", object, v.Len()) + default: + return fmt.Sprintf("%T", object) + } +} + +func formatValue(value reflect.Value, indentation uint) string { + if indentation > MaxDepth { + return "..." + } + + if isNilValue(value) { + return "nil" + } + + if UseStringerRepresentation { + if value.CanInterface() { + obj := value.Interface() + switch x := obj.(type) { + case fmt.GoStringer: + return x.GoString() + case fmt.Stringer: + return x.String() + } + } + } + + switch value.Kind() { + case reflect.Bool: + return fmt.Sprintf("%v", value.Bool()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return fmt.Sprintf("%v", value.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return fmt.Sprintf("%v", value.Uint()) + case reflect.Uintptr: + return fmt.Sprintf("0x%x", value.Uint()) + case reflect.Float32, reflect.Float64: + return fmt.Sprintf("%v", value.Float()) + case reflect.Complex64, reflect.Complex128: + return fmt.Sprintf("%v", value.Complex()) + case reflect.Chan: + return fmt.Sprintf("0x%x", value.Pointer()) + case reflect.Func: + return fmt.Sprintf("0x%x", value.Pointer()) + case reflect.Ptr: + return formatValue(value.Elem(), indentation) + case reflect.Slice: + if value.Type().Elem().Kind() == reflect.Uint8 { + return formatString(value.Bytes(), indentation) + } + return formatSlice(value, indentation) + case reflect.String: + return formatString(value.String(), indentation) + case reflect.Array: + return formatSlice(value, indentation) + case reflect.Map: + return formatMap(value, indentation) + case reflect.Struct: + return formatStruct(value, indentation) + case reflect.Interface: + return formatValue(value.Elem(), indentation) + default: + if value.CanInterface() { + return fmt.Sprintf("%#v", value.Interface()) + } else { + return fmt.Sprintf("%#v", value) + } + } +} + +func formatString(object interface{}, indentation uint) string { + if indentation == 1 { + s := fmt.Sprintf("%s", object) + components := strings.Split(s, "\n") + result := "" + for i, component := range components { + if i == 0 { + result += component + } else { + result += Indent + component + } + if i < len(components)-1 { + result += "\n" + } + } + + return fmt.Sprintf("%s", result) + } else { + return fmt.Sprintf("%q", object) + } +} + +func formatSlice(v reflect.Value, indentation uint) string { + l := v.Len() + result := make([]string, l) + longest := 0 + for i := 0; i < l; i++ { + result[i] = formatValue(v.Index(i), indentation+1) + if len(result[i]) > longest { + longest = len(result[i]) + } + } + + if longest > longFormThreshold { + indenter := strings.Repeat(Indent, int(indentation)) + return fmt.Sprintf("[\n%s%s,\n%s]", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter) + } else { + return fmt.Sprintf("[%s]", strings.Join(result, ", ")) + } +} + +func formatMap(v reflect.Value, indentation uint) string { + l := v.Len() + result := make([]string, l) + + longest := 0 + for i, key := range v.MapKeys() { + value := v.MapIndex(key) + result[i] = fmt.Sprintf("%s: %s", formatValue(key, 0), formatValue(value, indentation+1)) + if len(result[i]) > longest { + longest = len(result[i]) + } + } + + if longest > longFormThreshold { + indenter := strings.Repeat(Indent, int(indentation)) + return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter) + } else { + return fmt.Sprintf("{%s}", strings.Join(result, ", ")) + } +} + +func formatStruct(v reflect.Value, indentation uint) string { + t := v.Type() + + l := v.NumField() + result := []string{} + longest := 0 + for i := 0; i < l; i++ { + structField := t.Field(i) + fieldEntry := v.Field(i) + representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1)) + result = append(result, representation) + if len(representation) > longest { + longest = len(representation) + } + } + if longest > longFormThreshold { + indenter := strings.Repeat(Indent, int(indentation)) + return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter) + } else { + return fmt.Sprintf("{%s}", strings.Join(result, ", ")) + } +} + +func isNilValue(a reflect.Value) bool { + switch a.Kind() { + case reflect.Invalid: + return true + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return a.IsNil() + } + + return false +} + +func isNil(a interface{}) bool { + if a == nil { + return true + } + + switch reflect.TypeOf(a).Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return reflect.ValueOf(a).IsNil() + } + + return false +} diff --git a/vendor/github.com/onsi/gomega/format/format_suite_test.go b/vendor/github.com/onsi/gomega/format/format_suite_test.go new file mode 100644 index 0000000..8e65a95 --- /dev/null +++ b/vendor/github.com/onsi/gomega/format/format_suite_test.go @@ -0,0 +1,13 @@ +package format_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestFormat(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Format Suite") +} diff --git a/vendor/github.com/onsi/gomega/format/format_test.go b/vendor/github.com/onsi/gomega/format/format_test.go new file mode 100644 index 0000000..fd926f5 --- /dev/null +++ b/vendor/github.com/onsi/gomega/format/format_test.go @@ -0,0 +1,449 @@ +package format_test + +import ( + "fmt" + "strings" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/format" + "github.com/onsi/gomega/types" +) + +//recursive struct + +type StringAlias string +type ByteAlias []byte +type IntAlias int + +type AStruct struct { + Exported string +} + +type SimpleStruct struct { + Name string + Enumeration int + Veritas bool + Data []byte + secret uint32 +} + +type ComplexStruct struct { + Strings []string + SimpleThings []*SimpleStruct + DataMaps map[int]ByteAlias +} + +type SecretiveStruct struct { + boolValue bool + intValue int + uintValue uint + uintptrValue uintptr + floatValue float32 + complexValue complex64 + chanValue chan bool + funcValue func() + pointerValue *int + sliceValue []string + byteSliceValue []byte + stringValue string + arrValue [3]int + byteArrValue [3]byte + mapValue map[string]int + structValue AStruct + interfaceValue interface{} +} + +type GoStringer struct { +} + +func (g GoStringer) GoString() string { + return "go-string" +} + +func (g GoStringer) String() string { + return "string" +} + +type Stringer struct { +} + +func (g Stringer) String() string { + return "string" +} + +var _ = Describe("Format", func() { + match := func(typeRepresentation string, valueRepresentation string, args ...interface{}) types.GomegaMatcher { + if len(args) > 0 { + valueRepresentation = fmt.Sprintf(valueRepresentation, args...) + } + return Equal(fmt.Sprintf("%s<%s>: %s", Indent, typeRepresentation, valueRepresentation)) + } + + matchRegexp := func(typeRepresentation string, valueRepresentation string, args ...interface{}) types.GomegaMatcher { + if len(args) > 0 { + valueRepresentation = fmt.Sprintf(valueRepresentation, args...) + } + return MatchRegexp(fmt.Sprintf("%s<%s>: %s", Indent, typeRepresentation, valueRepresentation)) + } + + hashMatchingRegexp := func(entries ...string) string { + entriesSwitch := "(" + strings.Join(entries, "|") + ")" + arr := make([]string, len(entries)) + for i := range arr { + arr[i] = entriesSwitch + } + return "{" + strings.Join(arr, ", ") + "}" + } + + Describe("Message", func() { + Context("with only an actual value", func() { + It("should print out an indented formatted representation of the value and the message", func() { + Ω(Message(3, "to be three.")).Should(Equal("Expected\n : 3\nto be three.")) + }) + }) + + Context("with an actual and an expected value", func() { + It("should print out an indented formatted representatino of both values, and the message", func() { + Ω(Message(3, "to equal", 4)).Should(Equal("Expected\n : 3\nto equal\n : 4")) + }) + }) + }) + + Describe("IndentString", func() { + It("should indent the string", func() { + Ω(IndentString("foo\n bar\nbaz", 2)).Should(Equal(" foo\n bar\n baz")) + }) + }) + + Describe("Object", func() { + Describe("formatting boolean values", func() { + It("should give the type and format values correctly", func() { + Ω(Object(true, 1)).Should(match("bool", "true")) + Ω(Object(false, 1)).Should(match("bool", "false")) + }) + }) + + Describe("formatting numbers", func() { + It("should give the type and format values correctly", func() { + Ω(Object(int(3), 1)).Should(match("int", "3")) + Ω(Object(int8(3), 1)).Should(match("int8", "3")) + Ω(Object(int16(3), 1)).Should(match("int16", "3")) + Ω(Object(int32(3), 1)).Should(match("int32", "3")) + Ω(Object(int64(3), 1)).Should(match("int64", "3")) + + Ω(Object(uint(3), 1)).Should(match("uint", "3")) + Ω(Object(uint8(3), 1)).Should(match("uint8", "3")) + Ω(Object(uint16(3), 1)).Should(match("uint16", "3")) + Ω(Object(uint32(3), 1)).Should(match("uint32", "3")) + Ω(Object(uint64(3), 1)).Should(match("uint64", "3")) + }) + + It("should handle uintptr differently", func() { + Ω(Object(uintptr(3), 1)).Should(match("uintptr", "0x3")) + }) + }) + + Describe("formatting channels", func() { + It("should give the type and format values correctly", func() { + c := make(chan<- bool, 3) + c <- true + c <- false + Ω(Object(c, 1)).Should(match("chan<- bool | len:2, cap:3", "%v", c)) + }) + }) + + Describe("formatting strings", func() { + It("should give the type and format values correctly", func() { + s := "a\nb\nc" + Ω(Object(s, 1)).Should(match("string", `a + b + c`)) + }) + }) + + Describe("formatting []byte slices", func() { + It("should present them as strings", func() { + b := []byte("a\nb\nc") + Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:5, cap:\d+`, `a + b + c`)) + }) + }) + + Describe("formatting functions", func() { + It("should give the type and format values correctly", func() { + f := func(a string, b []int) ([]byte, error) { + return []byte("abc"), nil + } + Ω(Object(f, 1)).Should(match("func(string, []int) ([]uint8, error)", "%v", f)) + }) + }) + + Describe("formatting pointers", func() { + It("should give the type and dereference the value to format it correctly", func() { + a := 3 + Ω(Object(&a, 1)).Should(match(fmt.Sprintf("*int | %p", &a), "3")) + }) + + Context("when there are pointers to pointers...", func() { + It("should recursively deference the pointer until it gets to a value", func() { + a := 3 + var b *int + var c **int + var d ***int + b = &a + c = &b + d = &c + + Ω(Object(d, 1)).Should(match(fmt.Sprintf("***int | %p", d), "3")) + }) + }) + + Context("when the pointer points to nil", func() { + It("should say nil and not explode", func() { + var a *AStruct + Ω(Object(a, 1)).Should(match("*format_test.AStruct | 0x0", "nil")) + }) + }) + }) + + Describe("formatting arrays", func() { + It("should give the type and format values correctly", func() { + w := [3]string{"Jed Bartlet", "Toby Ziegler", "CJ Cregg"} + Ω(Object(w, 1)).Should(match("[3]string", `["Jed Bartlet", "Toby Ziegler", "CJ Cregg"]`)) + }) + + Context("with byte arrays", func() { + It("should give the type and format values correctly", func() { + w := [3]byte{17, 28, 19} + Ω(Object(w, 1)).Should(match("[3]uint8", `[17, 28, 19]`)) + }) + }) + }) + + Describe("formatting slices", func() { + It("should include the length and capacity in the type information", func() { + s := make([]bool, 3, 4) + Ω(Object(s, 1)).Should(match("[]bool | len:3, cap:4", "[false, false, false]")) + }) + + Context("when the slice contains long entries", func() { + It("should format the entries with newlines", func() { + w := []string{"Josiah Edward Bartlet", "Toby Ziegler", "CJ Cregg"} + expected := `[ + "Josiah Edward Bartlet", + "Toby Ziegler", + "CJ Cregg", + ]` + Ω(Object(w, 1)).Should(match("[]string | len:3, cap:3", expected)) + }) + }) + }) + + Describe("formatting maps", func() { + It("should include the length in the type information", func() { + m := make(map[int]bool, 5) + m[3] = true + m[4] = false + Ω(Object(m, 1)).Should(matchRegexp(`map\[int\]bool \| len:2`, hashMatchingRegexp("3: true", "4: false"))) + }) + + Context("when the slice contains long entries", func() { + It("should format the entries with newlines", func() { + m := map[string][]byte{} + m["Josiah Edward Bartlet"] = []byte("Martin Sheen") + m["Toby Ziegler"] = []byte("Richard Schiff") + m["CJ Cregg"] = []byte("Allison Janney") + expected := `{ + ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"), + ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"), + ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"), + }` + Ω(Object(m, 1)).Should(matchRegexp(`map\[string\]\[\]uint8 \| len:3`, expected)) + }) + }) + }) + + Describe("formatting structs", func() { + It("should include the struct name and the field names", func() { + s := SimpleStruct{ + Name: "Oswald", + Enumeration: 17, + Veritas: true, + Data: []byte("datum"), + secret: 1983, + } + + Ω(Object(s, 1)).Should(match("format_test.SimpleStruct", `{Name: "Oswald", Enumeration: 17, Veritas: true, Data: "datum", secret: 1983}`)) + }) + + Context("when the struct contains long entries", func() { + It("should format the entries with new lines", func() { + s := &SimpleStruct{ + Name: "Mithrandir Gandalf Greyhame", + Enumeration: 2021, + Veritas: true, + Data: []byte("wizard"), + secret: 3, + } + + Ω(Object(s, 1)).Should(match(fmt.Sprintf("*format_test.SimpleStruct | %p", s), `{ + Name: "Mithrandir Gandalf Greyhame", + Enumeration: 2021, + Veritas: true, + Data: "wizard", + secret: 3, + }`)) + }) + }) + }) + + Describe("formatting nil values", func() { + It("should print out nil", func() { + Ω(Object(nil, 1)).Should(match("nil", "nil")) + var typedNil *AStruct + Ω(Object(typedNil, 1)).Should(match("*format_test.AStruct | 0x0", "nil")) + var c chan<- bool + Ω(Object(c, 1)).Should(match("chan<- bool | len:0, cap:0", "nil")) + var s []string + Ω(Object(s, 1)).Should(match("[]string | len:0, cap:0", "nil")) + var m map[string]bool + Ω(Object(m, 1)).Should(match("map[string]bool | len:0", "nil")) + }) + }) + + Describe("formatting aliased types", func() { + It("should print out the correct alias type", func() { + Ω(Object(StringAlias("alias"), 1)).Should(match("format_test.StringAlias", `alias`)) + Ω(Object(ByteAlias("alias"), 1)).Should(matchRegexp(`format_test\.ByteAlias \| len:5, cap:\d+`, `alias`)) + Ω(Object(IntAlias(3), 1)).Should(match("format_test.IntAlias", "3")) + }) + }) + + Describe("handling nested things", func() { + It("should produce a correctly nested representation", func() { + s := ComplexStruct{ + Strings: []string{"lots", "of", "short", "strings"}, + SimpleThings: []*SimpleStruct{ + {"short", 7, true, []byte("succinct"), 17}, + {"something longer", 427, true, []byte("designed to wrap around nicely"), 30}, + }, + DataMaps: map[int]ByteAlias{ + 17: ByteAlias("some substantially longer chunks of data"), + 1138: ByteAlias("that should make things wrap"), + }, + } + expected := `{ + Strings: \["lots", "of", "short", "strings"\], + SimpleThings: \[ + {Name: "short", Enumeration: 7, Veritas: true, Data: "succinct", secret: 17}, + { + Name: "something longer", + Enumeration: 427, + Veritas: true, + Data: "designed to wrap around nicely", + secret: 30, + }, + \], + DataMaps: { + (17: "some substantially longer chunks of data"|1138: "that should make things wrap"), + (17: "some substantially longer chunks of data"|1138: "that should make things wrap"), + }, + }` + Ω(Object(s, 1)).Should(matchRegexp(`format_test\.ComplexStruct`, expected)) + }) + }) + }) + + Describe("Handling unexported fields in structs", func() { + It("should handle all the various types correctly", func() { + a := int(5) + s := SecretiveStruct{ + boolValue: true, + intValue: 3, + uintValue: 4, + uintptrValue: 5, + floatValue: 6.0, + complexValue: complex(5.0, 3.0), + chanValue: make(chan bool, 2), + funcValue: func() {}, + pointerValue: &a, + sliceValue: []string{"string", "slice"}, + byteSliceValue: []byte("bytes"), + stringValue: "a string", + arrValue: [3]int{11, 12, 13}, + byteArrValue: [3]byte{17, 20, 32}, + mapValue: map[string]int{"a key": 20, "b key": 30}, + structValue: AStruct{"exported"}, + interfaceValue: map[string]int{"a key": 17}, + } + + expected := fmt.Sprintf(`{ + boolValue: true, + intValue: 3, + uintValue: 4, + uintptrValue: 0x5, + floatValue: 6, + complexValue: \(5\+3i\), + chanValue: %p, + funcValue: %p, + pointerValue: 5, + sliceValue: \["string", "slice"\], + byteSliceValue: "bytes", + stringValue: "a string", + arrValue: \[11, 12, 13\], + byteArrValue: \[17, 20, 32\], + mapValue: %s, + structValue: {Exported: "exported"}, + interfaceValue: {"a key": 17}, + }`, s.chanValue, s.funcValue, hashMatchingRegexp(`"a key": 20`, `"b key": 30`)) + + Ω(Object(s, 1)).Should(matchRegexp(`format_test\.SecretiveStruct`, expected)) + }) + }) + + Describe("Handling interfaces", func() { + It("should unpack the interface", func() { + outerHash := map[string]interface{}{} + innerHash := map[string]int{} + + innerHash["inner"] = 3 + outerHash["integer"] = 2 + outerHash["map"] = innerHash + + expected := hashMatchingRegexp(`"integer": 2`, `"map": {"inner": 3}`) + Ω(Object(outerHash, 1)).Should(matchRegexp(`map\[string\]interface {} \| len:2`, expected)) + }) + }) + + Describe("Handling recursive things", func() { + It("should not go crazy...", func() { + m := map[string]interface{}{} + m["integer"] = 2 + m["map"] = m + Ω(Object(m, 1)).Should(ContainSubstring("...")) + }) + }) + + Describe("When instructed to use the Stringer representation", func() { + BeforeEach(func() { + UseStringerRepresentation = true + }) + + AfterEach(func() { + UseStringerRepresentation = false + }) + + Context("when passed a GoStringer", func() { + It("should use what GoString() returns", func() { + Ω(Object(GoStringer{}, 1)).Should(ContainSubstring(": go-string")) + }) + }) + + Context("when passed a stringer", func() { + It("should use what String() returns", func() { + Ω(Object(Stringer{}, 1)).Should(ContainSubstring(": string")) + }) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/gbytes/buffer.go b/vendor/github.com/onsi/gomega/gbytes/buffer.go new file mode 100644 index 0000000..7e42334 --- /dev/null +++ b/vendor/github.com/onsi/gomega/gbytes/buffer.go @@ -0,0 +1,204 @@ +/* +Package gbytes provides a buffer that supports incrementally detecting input. + +You use gbytes.Buffer with the gbytes.Say matcher. When Say finds a match, it fastforwards the buffer's read cursor to the end of that match. + +Subsequent matches against the buffer will only operate against data that appears *after* the read cursor. + +The read cursor is an opaque implementation detail that you cannot access. You should use the Say matcher to sift through the buffer. You can always +access the entire buffer's contents with Contents(). + +*/ +package gbytes + +import ( + "errors" + "fmt" + "regexp" + "sync" + "time" +) + +/* +gbytes.Buffer implements an io.Writer and can be used with the gbytes.Say matcher. + +You should only use a gbytes.Buffer in test code. It stores all writes in an in-memory buffer - behavior that is inappropriate for production code! +*/ +type Buffer struct { + contents []byte + readCursor uint64 + lock *sync.Mutex + detectCloser chan interface{} + closed bool +} + +/* +NewBuffer returns a new gbytes.Buffer +*/ +func NewBuffer() *Buffer { + return &Buffer{ + lock: &sync.Mutex{}, + } +} + +/* +BufferWithBytes returns a new gbytes.Buffer seeded with the passed in bytes +*/ +func BufferWithBytes(bytes []byte) *Buffer { + return &Buffer{ + lock: &sync.Mutex{}, + contents: bytes, + } +} + +/* +Write implements the io.Writer interface +*/ +func (b *Buffer) Write(p []byte) (n int, err error) { + b.lock.Lock() + defer b.lock.Unlock() + + if b.closed { + return 0, errors.New("attempt to write to closed buffer") + } + + b.contents = append(b.contents, p...) + return len(p), nil +} + +/* +Close signifies that the buffer will no longer be written to +*/ +func (b *Buffer) Close() error { + b.lock.Lock() + defer b.lock.Unlock() + + b.closed = true + + return nil +} + +/* +Closed returns true if the buffer has been closed +*/ +func (b *Buffer) Closed() bool { + b.lock.Lock() + defer b.lock.Unlock() + + return b.closed +} + +/* +Contents returns all data ever written to the buffer. +*/ +func (b *Buffer) Contents() []byte { + b.lock.Lock() + defer b.lock.Unlock() + + contents := make([]byte, len(b.contents)) + copy(contents, b.contents) + return contents +} + +/* +Detect takes a regular expression and returns a channel. + +The channel will receive true the first time data matching the regular expression is written to the buffer. +The channel is subsequently closed and the buffer's read-cursor is fast-forwarded to just after the matching region. + +You typically don't need to use Detect and should use the ghttp.Say matcher instead. Detect is useful, however, in cases where your code must +be branch and handle different outputs written to the buffer. + +For example, consider a buffer hooked up to the stdout of a client library. You may (or may not, depending on state outside of your control) need to authenticate the client library. + +You could do something like: + +select { +case <-buffer.Detect("You are not logged in"): + //log in +case <-buffer.Detect("Success"): + //carry on +case <-time.After(time.Second): + //welp +} +buffer.CancelDetects() + +You should always call CancelDetects after using Detect. This will close any channels that have not detected and clean up the goroutines that were spawned to support them. + +Finally, you can pass detect a format string followed by variadic arguments. This will construct the regexp using fmt.Sprintf. +*/ +func (b *Buffer) Detect(desired string, args ...interface{}) chan bool { + formattedRegexp := desired + if len(args) > 0 { + formattedRegexp = fmt.Sprintf(desired, args...) + } + re := regexp.MustCompile(formattedRegexp) + + b.lock.Lock() + defer b.lock.Unlock() + + if b.detectCloser == nil { + b.detectCloser = make(chan interface{}) + } + + closer := b.detectCloser + response := make(chan bool) + go func() { + ticker := time.NewTicker(10 * time.Millisecond) + defer ticker.Stop() + defer close(response) + for { + select { + case <-ticker.C: + b.lock.Lock() + data, cursor := b.contents[b.readCursor:], b.readCursor + loc := re.FindIndex(data) + b.lock.Unlock() + + if loc != nil { + response <- true + b.lock.Lock() + newCursorPosition := cursor + uint64(loc[1]) + if newCursorPosition >= b.readCursor { + b.readCursor = newCursorPosition + } + b.lock.Unlock() + return + } + case <-closer: + return + } + } + }() + + return response +} + +/* +CancelDetects cancels any pending detects and cleans up their goroutines. You should always call this when you're done with a set of Detect channels. +*/ +func (b *Buffer) CancelDetects() { + b.lock.Lock() + defer b.lock.Unlock() + + close(b.detectCloser) + b.detectCloser = nil +} + +func (b *Buffer) didSay(re *regexp.Regexp) (bool, []byte) { + b.lock.Lock() + defer b.lock.Unlock() + + unreadBytes := b.contents[b.readCursor:] + copyOfUnreadBytes := make([]byte, len(unreadBytes)) + copy(copyOfUnreadBytes, unreadBytes) + + loc := re.FindIndex(unreadBytes) + + if loc != nil { + b.readCursor += uint64(loc[1]) + return true, copyOfUnreadBytes + } else { + return false, copyOfUnreadBytes + } +} diff --git a/vendor/github.com/onsi/gomega/gbytes/buffer_test.go b/vendor/github.com/onsi/gomega/gbytes/buffer_test.go new file mode 100644 index 0000000..9aa2937 --- /dev/null +++ b/vendor/github.com/onsi/gomega/gbytes/buffer_test.go @@ -0,0 +1,121 @@ +package gbytes_test + +import ( + "time" + . "github.com/onsi/gomega/gbytes" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Buffer", func() { + var buffer *Buffer + + BeforeEach(func() { + buffer = NewBuffer() + }) + + Describe("dumping the entire contents of the buffer", func() { + It("should return everything that's been written", func() { + buffer.Write([]byte("abc")) + buffer.Write([]byte("def")) + Ω(buffer.Contents()).Should(Equal([]byte("abcdef"))) + + Ω(buffer).Should(Say("bcd")) + Ω(buffer.Contents()).Should(Equal([]byte("abcdef"))) + }) + }) + + Describe("creating a buffer with bytes", func() { + It("should create the buffer with the cursor set to the beginning", func() { + buffer := BufferWithBytes([]byte("abcdef")) + Ω(buffer.Contents()).Should(Equal([]byte("abcdef"))) + Ω(buffer).Should(Say("abc")) + Ω(buffer).ShouldNot(Say("abc")) + Ω(buffer).Should(Say("def")) + }) + }) + + Describe("detecting regular expressions", func() { + It("should fire the appropriate channel when the passed in pattern matches, then close it", func(done Done) { + go func() { + time.Sleep(10 * time.Millisecond) + buffer.Write([]byte("abcde")) + }() + + A := buffer.Detect("%s", "a.c") + B := buffer.Detect("def") + + var gotIt bool + select { + case gotIt = <-A: + case <-B: + Fail("should not have gotten here") + } + + Ω(gotIt).Should(BeTrue()) + Eventually(A).Should(BeClosed()) + + buffer.Write([]byte("f")) + Eventually(B).Should(Receive()) + Eventually(B).Should(BeClosed()) + + close(done) + }) + + It("should fast-forward the buffer upon detection", func(done Done) { + buffer.Write([]byte("abcde")) + <-buffer.Detect("abc") + Ω(buffer).ShouldNot(Say("abc")) + Ω(buffer).Should(Say("de")) + close(done) + }) + + It("should only fast-forward the buffer when the channel is read, and only if doing so would not rewind it", func(done Done) { + buffer.Write([]byte("abcde")) + A := buffer.Detect("abc") + time.Sleep(20 * time.Millisecond) //give the goroutine a chance to detect and write to the channel + Ω(buffer).Should(Say("abcd")) + <-A + Ω(buffer).ShouldNot(Say("d")) + Ω(buffer).Should(Say("e")) + Eventually(A).Should(BeClosed()) + close(done) + }) + + It("should be possible to cancel a detection", func(done Done) { + A := buffer.Detect("abc") + B := buffer.Detect("def") + buffer.CancelDetects() + buffer.Write([]byte("abcdef")) + Eventually(A).Should(BeClosed()) + Eventually(B).Should(BeClosed()) + + Ω(buffer).Should(Say("bcde")) + <-buffer.Detect("f") + close(done) + }) + }) + + Describe("closing the buffer", func() { + It("should error when further write attempts are made", func() { + _, err := buffer.Write([]byte("abc")) + Ω(err).ShouldNot(HaveOccurred()) + + buffer.Close() + + _, err = buffer.Write([]byte("def")) + Ω(err).Should(HaveOccurred()) + + Ω(buffer.Contents()).Should(Equal([]byte("abc"))) + }) + + It("should be closed", func() { + Ω(buffer.Closed()).Should(BeFalse()) + + buffer.Close() + + Ω(buffer.Closed()).Should(BeTrue()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/gbytes/gbuffer_suite_test.go b/vendor/github.com/onsi/gomega/gbytes/gbuffer_suite_test.go new file mode 100644 index 0000000..3a7dc06 --- /dev/null +++ b/vendor/github.com/onsi/gomega/gbytes/gbuffer_suite_test.go @@ -0,0 +1,13 @@ +package gbytes_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestGbytes(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Gbytes Suite") +} diff --git a/vendor/github.com/onsi/gomega/gbytes/say_matcher.go b/vendor/github.com/onsi/gomega/gbytes/say_matcher.go new file mode 100644 index 0000000..ce5ebcb --- /dev/null +++ b/vendor/github.com/onsi/gomega/gbytes/say_matcher.go @@ -0,0 +1,105 @@ +package gbytes + +import ( + "fmt" + "regexp" + + "github.com/onsi/gomega/format" +) + +//Objects satisfying the BufferProvider can be used with the Say matcher. +type BufferProvider interface { + Buffer() *Buffer +} + +/* +Say is a Gomega matcher that operates on gbytes.Buffers: + + Ω(buffer).Should(Say("something")) + +will succeed if the unread portion of the buffer matches the regular expression "something". + +When Say succeeds, it fast forwards the gbytes.Buffer's read cursor to just after the succesful match. +Thus, subsequent calls to Say will only match against the unread portion of the buffer + +Say pairs very well with Eventually. To asser that a buffer eventually receives data matching "[123]-star" within 3 seconds you can: + + Eventually(buffer, 3).Should(Say("[123]-star")) + +Ditto with consistently. To assert that a buffer does not receive data matching "never-see-this" for 1 second you can: + + Consistently(buffer, 1).ShouldNot(Say("never-see-this")) + +In addition to bytes.Buffers, Say can operate on objects that implement the gbytes.BufferProvider interface. +In such cases, Say simply operates on the *gbytes.Buffer returned by Buffer() + +If the buffer is closed, the Say matcher will tell Eventually to abort. +*/ +func Say(expected string, args ...interface{}) *sayMatcher { + formattedRegexp := expected + if len(args) > 0 { + formattedRegexp = fmt.Sprintf(expected, args...) + } + return &sayMatcher{ + re: regexp.MustCompile(formattedRegexp), + } +} + +type sayMatcher struct { + re *regexp.Regexp + receivedSayings []byte +} + +func (m *sayMatcher) buffer(actual interface{}) (*Buffer, bool) { + var buffer *Buffer + + switch x := actual.(type) { + case *Buffer: + buffer = x + case BufferProvider: + buffer = x.Buffer() + default: + return nil, false + } + + return buffer, true +} + +func (m *sayMatcher) Match(actual interface{}) (success bool, err error) { + buffer, ok := m.buffer(actual) + if !ok { + return false, fmt.Errorf("Say must be passed a *gbytes.Buffer or BufferProvider. Got:\n%s", format.Object(actual, 1)) + } + + didSay, sayings := buffer.didSay(m.re) + m.receivedSayings = sayings + + return didSay, nil +} + +func (m *sayMatcher) FailureMessage(actual interface{}) (message string) { + return fmt.Sprintf( + "Got stuck at:\n%s\nWaiting for:\n%s", + format.IndentString(string(m.receivedSayings), 1), + format.IndentString(m.re.String(), 1), + ) +} + +func (m *sayMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return fmt.Sprintf( + "Saw:\n%s\nWhich matches the unexpected:\n%s", + format.IndentString(string(m.receivedSayings), 1), + format.IndentString(m.re.String(), 1), + ) +} + +func (m *sayMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { + switch x := actual.(type) { + case *Buffer: + return !x.Closed() + case BufferProvider: + return !x.Buffer().Closed() + default: + return true + } +} diff --git a/vendor/github.com/onsi/gomega/gbytes/say_matcher_test.go b/vendor/github.com/onsi/gomega/gbytes/say_matcher_test.go new file mode 100644 index 0000000..d0ddf1f --- /dev/null +++ b/vendor/github.com/onsi/gomega/gbytes/say_matcher_test.go @@ -0,0 +1,163 @@ +package gbytes_test + +import ( + "time" + . "github.com/onsi/gomega/gbytes" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +type speaker struct { + buffer *Buffer +} + +func (s *speaker) Buffer() *Buffer { + return s.buffer +} + +var _ = Describe("SayMatcher", func() { + var buffer *Buffer + + BeforeEach(func() { + buffer = NewBuffer() + buffer.Write([]byte("abc")) + }) + + Context("when actual is not a gexec Buffer, or a BufferProvider", func() { + It("should error", func() { + failures := InterceptGomegaFailures(func() { + Ω("foo").Should(Say("foo")) + }) + Ω(failures[0]).Should(ContainSubstring("*gbytes.Buffer")) + }) + }) + + Context("when a match is found", func() { + It("should succeed", func() { + Ω(buffer).Should(Say("abc")) + }) + + It("should support printf-like formatting", func() { + Ω(buffer).Should(Say("a%sc", "b")) + }) + + It("should use a regular expression", func() { + Ω(buffer).Should(Say("a.c")) + }) + + It("should fastforward the buffer", func() { + buffer.Write([]byte("def")) + Ω(buffer).Should(Say("abcd")) + Ω(buffer).Should(Say("ef")) + Ω(buffer).ShouldNot(Say("[a-z]")) + }) + }) + + Context("when no match is found", func() { + It("should not error", func() { + Ω(buffer).ShouldNot(Say("def")) + }) + + Context("when the buffer is closed", func() { + BeforeEach(func() { + buffer.Close() + }) + + It("should abort an eventually", func() { + t := time.Now() + failures := InterceptGomegaFailures(func() { + Eventually(buffer).Should(Say("def")) + }) + Eventually(buffer).ShouldNot(Say("def")) + Ω(time.Since(t)).Should(BeNumerically("<", 500*time.Millisecond)) + Ω(failures).Should(HaveLen(1)) + + t = time.Now() + Eventually(buffer).Should(Say("abc")) + Ω(time.Since(t)).Should(BeNumerically("<", 500*time.Millisecond)) + }) + + It("should abort a consistently", func() { + t := time.Now() + Consistently(buffer, 2.0).ShouldNot(Say("def")) + Ω(time.Since(t)).Should(BeNumerically("<", 500*time.Millisecond)) + }) + + It("should not error with a synchronous matcher", func() { + Ω(buffer).ShouldNot(Say("def")) + Ω(buffer).Should(Say("abc")) + }) + }) + }) + + Context("when a positive match fails", func() { + It("should report where it got stuck", func() { + Ω(buffer).Should(Say("abc")) + buffer.Write([]byte("def")) + failures := InterceptGomegaFailures(func() { + Ω(buffer).Should(Say("abc")) + }) + Ω(failures[0]).Should(ContainSubstring("Got stuck at:")) + Ω(failures[0]).Should(ContainSubstring("def")) + }) + }) + + Context("when a negative match fails", func() { + It("should report where it got stuck", func() { + failures := InterceptGomegaFailures(func() { + Ω(buffer).ShouldNot(Say("abc")) + }) + Ω(failures[0]).Should(ContainSubstring("Saw:")) + Ω(failures[0]).Should(ContainSubstring("Which matches the unexpected:")) + Ω(failures[0]).Should(ContainSubstring("abc")) + }) + }) + + Context("when a match is not found", func() { + It("should not fastforward the buffer", func() { + Ω(buffer).ShouldNot(Say("def")) + Ω(buffer).Should(Say("abc")) + }) + }) + + Context("a nice real-life example", func() { + It("should behave well", func() { + Ω(buffer).Should(Say("abc")) + go func() { + time.Sleep(10 * time.Millisecond) + buffer.Write([]byte("def")) + }() + Ω(buffer).ShouldNot(Say("def")) + Eventually(buffer).Should(Say("def")) + }) + }) + + Context("when actual is a BufferProvider", func() { + It("should use actual's buffer", func() { + s := &speaker{ + buffer: NewBuffer(), + } + + Ω(s).ShouldNot(Say("abc")) + + s.Buffer().Write([]byte("abc")) + Ω(s).Should(Say("abc")) + }) + + It("should abort an eventually", func() { + s := &speaker{ + buffer: NewBuffer(), + } + + s.buffer.Close() + + t := time.Now() + failures := InterceptGomegaFailures(func() { + Eventually(s).Should(Say("def")) + }) + Ω(failures).Should(HaveLen(1)) + Ω(time.Since(t)).Should(BeNumerically("<", 500*time.Millisecond)) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/gexec/_fixture/firefly/main.go b/vendor/github.com/onsi/gomega/gexec/_fixture/firefly/main.go new file mode 100644 index 0000000..16091c2 --- /dev/null +++ b/vendor/github.com/onsi/gomega/gexec/_fixture/firefly/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "math/rand" + "os" + "strconv" + "time" +) + +var outQuote = "We've done the impossible, and that makes us mighty." +var errQuote = "Ah, curse your sudden but inevitable betrayal!" + +var randomQuotes = []string{ + "Can we maybe vote on the whole murdering people issue?", + "I swear by my pretty floral bonnet, I will end you.", + "My work's illegal, but at least it's honest.", +} + +func main() { + fmt.Fprintln(os.Stdout, outQuote) + fmt.Fprintln(os.Stderr, errQuote) + + randomIndex := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(len(randomQuotes)) + + time.Sleep(100 * time.Millisecond) + + fmt.Fprintln(os.Stdout, randomQuotes[randomIndex]) + + if len(os.Args) == 2 { + exitCode, _ := strconv.Atoi(os.Args[1]) + os.Exit(exitCode) + } else { + os.Exit(randomIndex) + } +} diff --git a/vendor/github.com/onsi/gomega/gexec/build.go b/vendor/github.com/onsi/gomega/gexec/build.go new file mode 100644 index 0000000..3e9bf9f --- /dev/null +++ b/vendor/github.com/onsi/gomega/gexec/build.go @@ -0,0 +1,78 @@ +package gexec + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path" + "path/filepath" + "runtime" +) + +var tmpDir string + +/* +Build uses go build to compile the package at packagePath. The resulting binary is saved off in a temporary directory. +A path pointing to this binary is returned. + +Build uses the $GOPATH set in your environment. It passes the variadic args on to `go build`. +*/ +func Build(packagePath string, args ...string) (compiledPath string, err error) { + return BuildIn(os.Getenv("GOPATH"), packagePath, args...) +} + +/* +BuildIn is identical to Build but allows you to specify a custom $GOPATH (the first argument). +*/ +func BuildIn(gopath string, packagePath string, args ...string) (compiledPath string, err error) { + tmpDir, err := temporaryDirectory() + if err != nil { + return "", err + } + + if len(gopath) == 0 { + return "", errors.New("$GOPATH not provided when building " + packagePath) + } + + executable := filepath.Join(tmpDir, path.Base(packagePath)) + if runtime.GOOS == "windows" { + executable = executable + ".exe" + } + + cmdArgs := append([]string{"build"}, args...) + cmdArgs = append(cmdArgs, "-o", executable, packagePath) + + build := exec.Command("go", cmdArgs...) + build.Env = append([]string{"GOPATH=" + gopath}, os.Environ()...) + + output, err := build.CombinedOutput() + if err != nil { + return "", fmt.Errorf("Failed to build %s:\n\nError:\n%s\n\nOutput:\n%s", packagePath, err, string(output)) + } + + return executable, nil +} + +/* +You should call CleanupBuildArtifacts before your test ends to clean up any temporary artifacts generated by +gexec. In Ginkgo this is typically done in an AfterSuite callback. +*/ +func CleanupBuildArtifacts() { + if tmpDir != "" { + os.RemoveAll(tmpDir) + } +} + +func temporaryDirectory() (string, error) { + var err error + if tmpDir == "" { + tmpDir, err = ioutil.TempDir("", "gexec_artifacts") + if err != nil { + return "", err + } + } + + return ioutil.TempDir(tmpDir, "g") +} diff --git a/vendor/github.com/onsi/gomega/gexec/exit_matcher.go b/vendor/github.com/onsi/gomega/gexec/exit_matcher.go new file mode 100644 index 0000000..e6f4329 --- /dev/null +++ b/vendor/github.com/onsi/gomega/gexec/exit_matcher.go @@ -0,0 +1,88 @@ +package gexec + +import ( + "fmt" + + "github.com/onsi/gomega/format" +) + +/* +The Exit matcher operates on a session: + + Ω(session).Should(Exit()) + +Exit passes if the session has already exited. + +If no status code is provided, then Exit will succeed if the session has exited regardless of exit code. +Otherwise, Exit will only succeed if the process has exited with the provided status code. + +Note that the process must have already exited. To wait for a process to exit, use Eventually: + + Eventually(session, 3).Should(Exit(0)) +*/ +func Exit(optionalExitCode ...int) *exitMatcher { + exitCode := -1 + if len(optionalExitCode) > 0 { + exitCode = optionalExitCode[0] + } + + return &exitMatcher{ + exitCode: exitCode, + } +} + +type exitMatcher struct { + exitCode int + didExit bool + actualExitCode int +} + +type Exiter interface { + ExitCode() int +} + +func (m *exitMatcher) Match(actual interface{}) (success bool, err error) { + exiter, ok := actual.(Exiter) + if !ok { + return false, fmt.Errorf("Exit must be passed a gexec.Exiter (Missing method ExitCode() int) Got:\n%s", format.Object(actual, 1)) + } + + m.actualExitCode = exiter.ExitCode() + + if m.actualExitCode == -1 { + return false, nil + } + + if m.exitCode == -1 { + return true, nil + } + return m.exitCode == m.actualExitCode, nil +} + +func (m *exitMatcher) FailureMessage(actual interface{}) (message string) { + if m.actualExitCode == -1 { + return "Expected process to exit. It did not." + } else { + return format.Message(m.actualExitCode, "to match exit code:", m.exitCode) + } +} + +func (m *exitMatcher) NegatedFailureMessage(actual interface{}) (message string) { + if m.actualExitCode == -1 { + return "you really shouldn't be able to see this!" + } else { + if m.exitCode == -1 { + return "Expected process not to exit. It did." + } else { + return format.Message(m.actualExitCode, "not to match exit code:", m.exitCode) + } + } +} + +func (m *exitMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { + session, ok := actual.(*Session) + if ok { + return session.ExitCode() == -1 + } + return true +} diff --git a/vendor/github.com/onsi/gomega/gexec/exit_matcher_test.go b/vendor/github.com/onsi/gomega/gexec/exit_matcher_test.go new file mode 100644 index 0000000..9f18e2d --- /dev/null +++ b/vendor/github.com/onsi/gomega/gexec/exit_matcher_test.go @@ -0,0 +1,113 @@ +package gexec_test + +import ( + "os/exec" + "time" + . "github.com/onsi/gomega/gexec" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +type NeverExits struct{} + +func (e NeverExits) ExitCode() int { + return -1 +} + +var _ = Describe("ExitMatcher", func() { + var command *exec.Cmd + var session *Session + + BeforeEach(func() { + var err error + command = exec.Command(fireflyPath, "0") + session, err = Start(command, nil, nil) + Ω(err).ShouldNot(HaveOccurred()) + }) + + Describe("when passed something that is an Exiter", func() { + It("should act normally", func() { + failures := InterceptGomegaFailures(func() { + Ω(NeverExits{}).Should(Exit()) + }) + + Ω(failures[0]).Should(ContainSubstring("Expected process to exit. It did not.")) + }) + }) + + Describe("when passed something that is not an Exiter", func() { + It("should error", func() { + failures := InterceptGomegaFailures(func() { + Ω("aardvark").Should(Exit()) + }) + + Ω(failures[0]).Should(ContainSubstring("Exit must be passed a gexec.Exiter")) + }) + }) + + Context("with no exit code", func() { + It("should say the right things when it fails", func() { + Ω(session).ShouldNot(Exit()) + + failures := InterceptGomegaFailures(func() { + Ω(session).Should(Exit()) + }) + + Ω(failures[0]).Should(ContainSubstring("Expected process to exit. It did not.")) + + Eventually(session).Should(Exit()) + + Ω(session).Should(Exit()) + + failures = InterceptGomegaFailures(func() { + Ω(session).ShouldNot(Exit()) + }) + + Ω(failures[0]).Should(ContainSubstring("Expected process not to exit. It did.")) + }) + }) + + Context("with an exit code", func() { + It("should say the right things when it fails", func() { + Ω(session).ShouldNot(Exit(0)) + Ω(session).ShouldNot(Exit(1)) + + failures := InterceptGomegaFailures(func() { + Ω(session).Should(Exit(0)) + }) + + Ω(failures[0]).Should(ContainSubstring("Expected process to exit. It did not.")) + + Eventually(session).Should(Exit(0)) + + Ω(session).Should(Exit(0)) + + failures = InterceptGomegaFailures(func() { + Ω(session).Should(Exit(1)) + }) + + Ω(failures[0]).Should(ContainSubstring("to match exit code:")) + + Ω(session).ShouldNot(Exit(1)) + + failures = InterceptGomegaFailures(func() { + Ω(session).ShouldNot(Exit(0)) + }) + + Ω(failures[0]).Should(ContainSubstring("not to match exit code:")) + }) + }) + + Describe("bailing out early", func() { + It("should bail out early once the process exits", func() { + t := time.Now() + + failures := InterceptGomegaFailures(func() { + Eventually(session).Should(Exit(1)) + }) + Ω(time.Since(t)).Should(BeNumerically("<=", 500*time.Millisecond)) + Ω(failures).Should(HaveLen(1)) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/gexec/gexec_suite_test.go b/vendor/github.com/onsi/gomega/gexec/gexec_suite_test.go new file mode 100644 index 0000000..87672aa --- /dev/null +++ b/vendor/github.com/onsi/gomega/gexec/gexec_suite_test.go @@ -0,0 +1,26 @@ +package gexec_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" + + "testing" +) + +var fireflyPath string + +func TestGexec(t *testing.T) { + BeforeSuite(func() { + var err error + fireflyPath, err = gexec.Build("./_fixture/firefly") + Ω(err).ShouldNot(HaveOccurred()) + }) + + AfterSuite(func() { + gexec.CleanupBuildArtifacts() + }) + + RegisterFailHandler(Fail) + RunSpecs(t, "Gexec Suite") +} diff --git a/vendor/github.com/onsi/gomega/gexec/prefixed_writer.go b/vendor/github.com/onsi/gomega/gexec/prefixed_writer.go new file mode 100644 index 0000000..556182b --- /dev/null +++ b/vendor/github.com/onsi/gomega/gexec/prefixed_writer.go @@ -0,0 +1,80 @@ +package gexec + +import ( + "bytes" + "io" + "sync" +) + +/* +PrefixedWriter wraps an io.Writer, emiting the passed in prefix at the beginning of each new line. +This can be useful when running multiple gexec.Sessions concurrently - you can prefix the log output of each +session by passing in a PrefixedWriter: + +gexec.Start(cmd, NewPrefixedWriter("[my-cmd] ", GinkgoWriter), NewPrefixedWriter("[my-cmd] ", GinkgoWriter)) +*/ +type PrefixedWriter struct { + prefix []byte + writer io.Writer + lock *sync.Mutex + isNewLine bool + isFirstWrite bool +} + +func NewPrefixedWriter(prefix string, writer io.Writer) *PrefixedWriter { + return &PrefixedWriter{ + prefix: []byte(prefix), + writer: writer, + lock: &sync.Mutex{}, + isFirstWrite: true, + } +} + +func (w *PrefixedWriter) Write(b []byte) (int, error) { + w.lock.Lock() + defer w.lock.Unlock() + + newLine := []byte("\n") + segments := bytes.Split(b, newLine) + + if len(segments) != 0 { + toWrite := []byte{} + if w.isFirstWrite { + toWrite = append(toWrite, w.prefix...) + toWrite = append(toWrite, segments[0]...) + w.isFirstWrite = false + } else if w.isNewLine { + toWrite = append(toWrite, newLine...) + toWrite = append(toWrite, w.prefix...) + toWrite = append(toWrite, segments[0]...) + } else { + toWrite = append(toWrite, segments[0]...) + } + + for i := 1; i < len(segments)-1; i++ { + toWrite = append(toWrite, newLine...) + toWrite = append(toWrite, w.prefix...) + toWrite = append(toWrite, segments[i]...) + } + + if len(segments) > 1 { + lastSegment := segments[len(segments)-1] + + if len(lastSegment) == 0 { + w.isNewLine = true + } else { + toWrite = append(toWrite, newLine...) + toWrite = append(toWrite, w.prefix...) + toWrite = append(toWrite, lastSegment...) + w.isNewLine = false + } + } + + _, err := w.writer.Write(toWrite) + if err != nil { + return 0, err + } + } + + return len(b), nil +} diff --git a/vendor/github.com/onsi/gomega/gexec/prefixed_writer_test.go b/vendor/github.com/onsi/gomega/gexec/prefixed_writer_test.go new file mode 100644 index 0000000..27f7487 --- /dev/null +++ b/vendor/github.com/onsi/gomega/gexec/prefixed_writer_test.go @@ -0,0 +1,41 @@ +package gexec_test + +import ( + "bytes" + . "github.com/onsi/gomega/gexec" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("PrefixedWriter", func() { + var buffer *bytes.Buffer + var writer *PrefixedWriter + BeforeEach(func() { + buffer = &bytes.Buffer{} + writer = NewPrefixedWriter("[p]", buffer) + }) + + It("should emit the prefix on newlines", func() { + writer.Write([]byte("abc")) + writer.Write([]byte("def\n")) + writer.Write([]byte("hij\n")) + writer.Write([]byte("\n\n")) + writer.Write([]byte("klm\n\nnop")) + writer.Write([]byte("")) + writer.Write([]byte("qrs")) + writer.Write([]byte("\ntuv\nwx")) + writer.Write([]byte("yz\n\n")) + + Ω(buffer.String()).Should(Equal(`[p]abcdef +[p]hij +[p] +[p] +[p]klm +[p] +[p]nopqrs +[p]tuv +[p]wxyz +[p]`)) + }) +}) diff --git a/vendor/github.com/onsi/gomega/gexec/session.go b/vendor/github.com/onsi/gomega/gexec/session.go new file mode 100644 index 0000000..460cfe5 --- /dev/null +++ b/vendor/github.com/onsi/gomega/gexec/session.go @@ -0,0 +1,214 @@ +/* +Package gexec provides support for testing external processes. +*/ +package gexec + +import ( + "io" + "os" + "os/exec" + "reflect" + "sync" + "syscall" + + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" +) + +const INVALID_EXIT_CODE = 254 + +type Session struct { + //The wrapped command + Command *exec.Cmd + + //A *gbytes.Buffer connected to the command's stdout + Out *gbytes.Buffer + + //A *gbytes.Buffer connected to the command's stderr + Err *gbytes.Buffer + + //A channel that will close when the command exits + Exited <-chan struct{} + + lock *sync.Mutex + exitCode int +} + +/* +Start starts the passed-in *exec.Cmd command. It wraps the command in a *gexec.Session. + +The session pipes the command's stdout and stderr to two *gbytes.Buffers available as properties on the session: session.Out and session.Err. +These buffers can be used with the gbytes.Say matcher to match against unread output: + + Ω(session.Out).Should(gbytes.Say("foo-out")) + Ω(session.Err).Should(gbytes.Say("foo-err")) + +In addition, Session satisfies the gbytes.BufferProvider interface and provides the stdout *gbytes.Buffer. This allows you to replace the first line, above, with: + + Ω(session).Should(gbytes.Say("foo-out")) + +When outWriter and/or errWriter are non-nil, the session will pipe stdout and/or stderr output both into the session *gybtes.Buffers and to the passed-in outWriter/errWriter. +This is useful for capturing the process's output or logging it to screen. In particular, when using Ginkgo it can be convenient to direct output to the GinkgoWriter: + + session, err := Start(command, GinkgoWriter, GinkgoWriter) + +This will log output when running tests in verbose mode, but - otherwise - will only log output when a test fails. + +The session wrapper is responsible for waiting on the *exec.Cmd command. You *should not* call command.Wait() yourself. +Instead, to assert that the command has exited you can use the gexec.Exit matcher: + + Ω(session).Should(gexec.Exit()) + +When the session exits it closes the stdout and stderr gbytes buffers. This will short circuit any +Eventuallys waiting fo the buffers to Say something. +*/ +func Start(command *exec.Cmd, outWriter io.Writer, errWriter io.Writer) (*Session, error) { + exited := make(chan struct{}) + + session := &Session{ + Command: command, + Out: gbytes.NewBuffer(), + Err: gbytes.NewBuffer(), + Exited: exited, + lock: &sync.Mutex{}, + exitCode: -1, + } + + var commandOut, commandErr io.Writer + + commandOut, commandErr = session.Out, session.Err + + if outWriter != nil && !reflect.ValueOf(outWriter).IsNil() { + commandOut = io.MultiWriter(commandOut, outWriter) + } + + if errWriter != nil && !reflect.ValueOf(errWriter).IsNil() { + commandErr = io.MultiWriter(commandErr, errWriter) + } + + command.Stdout = commandOut + command.Stderr = commandErr + + err := command.Start() + if err == nil { + go session.monitorForExit(exited) + } + + return session, err +} + +/* +Buffer implements the gbytes.BufferProvider interface and returns s.Out +This allows you to make gbytes.Say matcher assertions against stdout without having to reference .Out: + + Eventually(session).Should(gbytes.Say("foo")) +*/ +func (s *Session) Buffer() *gbytes.Buffer { + return s.Out +} + +/* +ExitCode returns the wrapped command's exit code. If the command hasn't exited yet, ExitCode returns -1. + +To assert that the command has exited it is more convenient to use the Exit matcher: + + Eventually(s).Should(gexec.Exit()) + +When the process exits because it has received a particular signal, the exit code will be 128+signal-value +(See http://www.tldp.org/LDP/abs/html/exitcodes.html and http://man7.org/linux/man-pages/man7/signal.7.html) + +*/ +func (s *Session) ExitCode() int { + s.lock.Lock() + defer s.lock.Unlock() + return s.exitCode +} + +/* +Wait waits until the wrapped command exits. It can be passed an optional timeout. +If the command does not exit within the timeout, Wait will trigger a test failure. + +Wait returns the session, making it possible to chain: + + session.Wait().Out.Contents() + +will wait for the command to exit then return the entirety of Out's contents. + +Wait uses eventually under the hood and accepts the same timeout/polling intervals that eventually does. +*/ +func (s *Session) Wait(timeout ...interface{}) *Session { + Eventually(s, timeout...).Should(Exit()) + return s +} + +/* +Kill sends the running command a SIGKILL signal. It does not wait for the process to exit. + +If the command has already exited, Kill returns silently. + +The session is returned to enable chaining. +*/ +func (s *Session) Kill() *Session { + if s.ExitCode() != -1 { + return s + } + s.Command.Process.Kill() + return s +} + +/* +Interrupt sends the running command a SIGINT signal. It does not wait for the process to exit. + +If the command has already exited, Interrupt returns silently. + +The session is returned to enable chaining. +*/ +func (s *Session) Interrupt() *Session { + return s.Signal(syscall.SIGINT) +} + +/* +Terminate sends the running command a SIGTERM signal. It does not wait for the process to exit. + +If the command has already exited, Terminate returns silently. + +The session is returned to enable chaining. +*/ +func (s *Session) Terminate() *Session { + return s.Signal(syscall.SIGTERM) +} + +/* +Terminate sends the running command the passed in signal. It does not wait for the process to exit. + +If the command has already exited, Signal returns silently. + +The session is returned to enable chaining. +*/ +func (s *Session) Signal(signal os.Signal) *Session { + if s.ExitCode() != -1 { + return s + } + s.Command.Process.Signal(signal) + return s +} + +func (s *Session) monitorForExit(exited chan<- struct{}) { + err := s.Command.Wait() + s.lock.Lock() + s.Out.Close() + s.Err.Close() + status := s.Command.ProcessState.Sys().(syscall.WaitStatus) + if status.Signaled() { + s.exitCode = 128 + int(status.Signal()) + } else { + exitStatus := status.ExitStatus() + if exitStatus == -1 && err != nil { + s.exitCode = INVALID_EXIT_CODE + } + s.exitCode = exitStatus + } + s.lock.Unlock() + + close(exited) +} diff --git a/vendor/github.com/onsi/gomega/gexec/session_test.go b/vendor/github.com/onsi/gomega/gexec/session_test.go new file mode 100644 index 0000000..cd48e6f --- /dev/null +++ b/vendor/github.com/onsi/gomega/gexec/session_test.go @@ -0,0 +1,177 @@ +package gexec_test + +import ( + "os/exec" + "syscall" + "time" + . "github.com/onsi/gomega/gbytes" + . "github.com/onsi/gomega/gexec" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Session", func() { + var command *exec.Cmd + var session *Session + + var outWriter, errWriter *Buffer + + BeforeEach(func() { + outWriter = nil + errWriter = nil + }) + + JustBeforeEach(func() { + command = exec.Command(fireflyPath) + var err error + session, err = Start(command, outWriter, errWriter) + Ω(err).ShouldNot(HaveOccurred()) + }) + + Context("running a command", func() { + It("should start the process", func() { + Ω(command.Process).ShouldNot(BeNil()) + }) + + It("should wrap the process's stdout and stderr with gbytes buffers", func(done Done) { + Eventually(session.Out).Should(Say("We've done the impossible, and that makes us mighty")) + Eventually(session.Err).Should(Say("Ah, curse your sudden but inevitable betrayal!")) + defer session.Out.CancelDetects() + + select { + case <-session.Out.Detect("Can we maybe vote on the whole murdering people issue"): + Eventually(session).Should(Exit(0)) + case <-session.Out.Detect("I swear by my pretty floral bonnet, I will end you."): + Eventually(session).Should(Exit(1)) + case <-session.Out.Detect("My work's illegal, but at least it's honest."): + Eventually(session).Should(Exit(2)) + } + + close(done) + }) + + It("should satisfy the gbytes.BufferProvider interface, passing Stdout", func() { + Eventually(session).Should(Say("We've done the impossible, and that makes us mighty")) + Eventually(session).Should(Exit()) + }) + }) + + Describe("providing the exit code", func() { + It("should provide the app's exit code", func() { + Ω(session.ExitCode()).Should(Equal(-1)) + + Eventually(session).Should(Exit()) + Ω(session.ExitCode()).Should(BeNumerically(">=", 0)) + Ω(session.ExitCode()).Should(BeNumerically("<", 3)) + }) + }) + + Describe("wait", func() { + It("should wait till the command exits", func() { + Ω(session.ExitCode()).Should(Equal(-1)) + Ω(session.Wait().ExitCode()).Should(BeNumerically(">=", 0)) + Ω(session.Wait().ExitCode()).Should(BeNumerically("<", 3)) + }) + }) + + Describe("exited", func() { + It("should close when the command exits", func() { + Eventually(session.Exited).Should(BeClosed()) + Ω(session.ExitCode()).ShouldNot(Equal(-1)) + }) + }) + + Describe("kill", func() { + It("should kill the command and wait for it to exit", func() { + session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter) + Ω(err).ShouldNot(HaveOccurred()) + + session.Kill() + Ω(session).ShouldNot(Exit(), "Should not exit immediately...") + Eventually(session).Should(Exit(128 + 9)) + }) + }) + + Describe("interrupt", func() { + It("should interrupt the command", func() { + session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter) + Ω(err).ShouldNot(HaveOccurred()) + + session.Interrupt() + Ω(session).ShouldNot(Exit(), "Should not exit immediately...") + Eventually(session).Should(Exit(128 + 2)) + }) + }) + + Describe("terminate", func() { + It("should terminate the command", func() { + session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter) + Ω(err).ShouldNot(HaveOccurred()) + + session.Terminate() + Ω(session).ShouldNot(Exit(), "Should not exit immediately...") + Eventually(session).Should(Exit(128 + 15)) + }) + }) + + Describe("signal", func() { + It("should send the signal to the command", func() { + session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter) + Ω(err).ShouldNot(HaveOccurred()) + + session.Signal(syscall.SIGABRT) + Ω(session).ShouldNot(Exit(), "Should not exit immediately...") + Eventually(session).Should(Exit(128 + 6)) + }) + }) + + Context("when the command exits", func() { + It("should close the buffers", func() { + Eventually(session).Should(Exit()) + + Ω(session.Out.Closed()).Should(BeTrue()) + Ω(session.Err.Closed()).Should(BeTrue()) + + Ω(session.Out).Should(Say("We've done the impossible, and that makes us mighty")) + }) + + var So = It + + So("this means that eventually should short circuit", func() { + t := time.Now() + failures := InterceptGomegaFailures(func() { + Eventually(session).Should(Say("blah blah blah blah blah")) + }) + Ω(time.Since(t)).Should(BeNumerically("<=", 500*time.Millisecond)) + Ω(failures).Should(HaveLen(1)) + }) + }) + + Context("when wrapping out and err", func() { + BeforeEach(func() { + outWriter = NewBuffer() + errWriter = NewBuffer() + }) + + It("should route to both the provided writers and the gbytes buffers", func() { + Eventually(session.Out).Should(Say("We've done the impossible, and that makes us mighty")) + Eventually(session.Err).Should(Say("Ah, curse your sudden but inevitable betrayal!")) + + Ω(outWriter.Contents()).Should(ContainSubstring("We've done the impossible, and that makes us mighty")) + Ω(errWriter.Contents()).Should(ContainSubstring("Ah, curse your sudden but inevitable betrayal!")) + + Eventually(session).Should(Exit()) + + Ω(outWriter.Contents()).Should(Equal(session.Out.Contents())) + Ω(errWriter.Contents()).Should(Equal(session.Err.Contents())) + }) + }) + + Describe("when the command fails to start", func() { + It("should return an error", func() { + _, err := Start(exec.Command("agklsjdfas"), nil, nil) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/ghttp/handlers.go b/vendor/github.com/onsi/gomega/ghttp/handlers.go new file mode 100644 index 0000000..d27ad80 --- /dev/null +++ b/vendor/github.com/onsi/gomega/ghttp/handlers.go @@ -0,0 +1,202 @@ +package ghttp + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/types" +) + +//CombineHandler takes variadic list of handlers and produces one handler +//that calls each handler in order. +func CombineHandlers(handlers ...http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + for _, handler := range handlers { + handler(w, req) + } + } +} + +//VerifyRequest returns a handler that verifies that a request uses the specified method to connect to the specified path +//You may also pass in an optional rawQuery string which is tested against the request's `req.URL.RawQuery` +// +//For path, you may pass in a string, in which case strict equality will be applied +//Alternatively you can pass in a matcher (ContainSubstring("/foo") and MatchRegexp("/foo/[a-f0-9]+") for example) +func VerifyRequest(method string, path interface{}, rawQuery ...string) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + Ω(req.Method).Should(Equal(method), "Method mismatch") + switch p := path.(type) { + case types.GomegaMatcher: + Ω(req.URL.Path).Should(p, "Path mismatch") + default: + Ω(req.URL.Path).Should(Equal(path), "Path mismatch") + } + if len(rawQuery) > 0 { + Ω(req.URL.RawQuery).Should(Equal(rawQuery[0]), "RawQuery mismatch") + } + } +} + +//VerifyContentType returns a handler that verifies that a request has a Content-Type header set to the +//specified value +func VerifyContentType(contentType string) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + Ω(req.Header.Get("Content-Type")).Should(Equal(contentType)) + } +} + +//VerifyBasicAuth returns a handler that verifies the request contains a BasicAuth Authorization header +//matching the passed in username and password +func VerifyBasicAuth(username string, password string) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + auth := req.Header.Get("Authorization") + decoded, err := base64.StdEncoding.DecodeString(auth[6:]) + Ω(err).ShouldNot(HaveOccurred()) + + Ω(string(decoded)).Should(Equal(fmt.Sprintf("%s:%s", username, password)), "Authorization mismatch") + } +} + +//VerifyHeader returns a handler that verifies the request contains the passed in headers. +//The passed in header keys are first canonicalized via http.CanonicalHeaderKey. +// +//The request must contain *all* the passed in headers, but it is allowed to have additional headers +//beyond the passed in set. +func VerifyHeader(header http.Header) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + for key, values := range header { + key = http.CanonicalHeaderKey(key) + Ω(req.Header[key]).Should(Equal(values), "Header mismatch for key: %s", key) + } + } +} + +//VerifyHeaderKV returns a handler that verifies the request contains a header matching the passed in key and values +//(recall that a `http.Header` is a mapping from string (key) to []string (values)) +//It is a convenience wrapper around `VerifyHeader` that allows you to avoid having to create an `http.Header` object. +func VerifyHeaderKV(key string, values ...string) http.HandlerFunc { + return VerifyHeader(http.Header{key: values}) +} + +//VerifyJSON returns a handler that verifies that the body of the request is a valid JSON representation +//matching the passed in JSON string. It does this using Gomega's MatchJSON method +// +//VerifyJSON also verifies that the request's content type is application/json +func VerifyJSON(expectedJSON string) http.HandlerFunc { + return CombineHandlers( + VerifyContentType("application/json"), + func(w http.ResponseWriter, req *http.Request) { + body, err := ioutil.ReadAll(req.Body) + req.Body.Close() + Ω(err).ShouldNot(HaveOccurred()) + Ω(body).Should(MatchJSON(expectedJSON), "JSON Mismatch") + }, + ) +} + +//VerifyJSONRepresenting is similar to VerifyJSON. Instead of taking a JSON string, however, it +//takes an arbitrary JSON-encodable object and verifies that the requests's body is a JSON representation +//that matches the object +func VerifyJSONRepresenting(object interface{}) http.HandlerFunc { + data, err := json.Marshal(object) + Ω(err).ShouldNot(HaveOccurred()) + return CombineHandlers( + VerifyContentType("application/json"), + VerifyJSON(string(data)), + ) +} + +func copyHeader(src http.Header, dst http.Header) { + for key, value := range src { + dst[key] = value + } +} + +/* +RespondWith returns a handler that responds to a request with the specified status code and body + +Body may be a string or []byte + +Also, RespondWith can be given an optional http.Header. The headers defined therein will be added to the response headers. +*/ +func RespondWith(statusCode int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + if len(optionalHeader) == 1 { + copyHeader(optionalHeader[0], w.Header()) + } + w.WriteHeader(statusCode) + switch x := body.(type) { + case string: + w.Write([]byte(x)) + case []byte: + w.Write(x) + default: + Ω(body).Should(BeNil(), "Invalid type for body. Should be string or []byte.") + } + } +} + +/* +RespondWithPtr returns a handler that responds to a request with the specified status code and body + +Unlike RespondWith, you pass RepondWithPtr a pointer to the status code and body allowing different tests +to share the same setup but specify different status codes and bodies. + +Also, RespondWithPtr can be given an optional http.Header. The headers defined therein will be added to the response headers. +Since the http.Header can be mutated after the fact you don't need to pass in a pointer. +*/ +func RespondWithPtr(statusCode *int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + if len(optionalHeader) == 1 { + copyHeader(optionalHeader[0], w.Header()) + } + w.WriteHeader(*statusCode) + if body != nil { + switch x := (body).(type) { + case *string: + w.Write([]byte(*x)) + case *[]byte: + w.Write(*x) + default: + Ω(body).Should(BeNil(), "Invalid type for body. Should be string or []byte.") + } + } + } +} + +/* +RespondWithJSONEncoded returns a handler that responds to a request with the specified status code and a body +containing the JSON-encoding of the passed in object + +Also, RespondWithJSONEncoded can be given an optional http.Header. The headers defined therein will be added to the response headers. +*/ +func RespondWithJSONEncoded(statusCode int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc { + data, err := json.Marshal(object) + Ω(err).ShouldNot(HaveOccurred()) + return RespondWith(statusCode, string(data), optionalHeader...) +} + +/* +RespondWithJSONEncodedPtr behaves like RespondWithJSONEncoded but takes a pointer +to a status code and object. + +This allows different tests to share the same setup but specify different status codes and JSON-encoded +objects. + +Also, RespondWithJSONEncodedPtr can be given an optional http.Header. The headers defined therein will be added to the response headers. +Since the http.Header can be mutated after the fact you don't need to pass in a pointer. +*/ +func RespondWithJSONEncodedPtr(statusCode *int, object *interface{}, optionalHeader ...http.Header) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + data, err := json.Marshal(*object) + Ω(err).ShouldNot(HaveOccurred()) + if len(optionalHeader) == 1 { + copyHeader(optionalHeader[0], w.Header()) + } + w.WriteHeader(*statusCode) + w.Write(data) + } +} diff --git a/vendor/github.com/onsi/gomega/ghttp/test_server.go b/vendor/github.com/onsi/gomega/ghttp/test_server.go new file mode 100644 index 0000000..07a2bb3 --- /dev/null +++ b/vendor/github.com/onsi/gomega/ghttp/test_server.go @@ -0,0 +1,303 @@ +/* +Package ghttp supports testing HTTP clients by providing a test server (simply a thin wrapper around httptest's server) that supports +registering multiple handlers. Incoming requests are not routed between the different handlers +- rather it is merely the order of the handlers that matters. The first request is handled by the first +registered handler, the second request by the second handler, etc. + +The intent here is to have each handler *verify* that the incoming request is valid. To accomplish, ghttp +also provides a collection of bite-size handlers that each perform one aspect of request verification. These can +be composed together and registered with a ghttp server. The result is an expressive language for describing +the requests generated by the client under test. + +Here's a simple example, note that the server handler is only defined in one BeforeEach and then modified, as required, by the nested BeforeEaches. +A more comprehensive example is available at https://onsi.github.io/gomega/#_testing_http_clients + + var _ = Describe("A Sprockets Client", func() { + var server *ghttp.Server + var client *SprocketClient + BeforeEach(func() { + server = ghttp.NewServer() + client = NewSprocketClient(server.URL(), "skywalker", "tk427") + }) + + AfterEach(func() { + server.Close() + }) + + Describe("fetching sprockets", func() { + var statusCode int + var sprockets []Sprocket + BeforeEach(func() { + statusCode = http.StatusOK + sprockets = []Sprocket{} + server.AppendHandlers(ghttp.CombineHandlers( + ghttp.VerifyRequest("GET", "/sprockets"), + ghttp.VerifyBasicAuth("skywalker", "tk427"), + ghttp.RespondWithJSONEncodedPtr(&statusCode, &sprockets), + )) + }) + + Context("when requesting all sprockets", func() { + Context("when the response is succesful", func() { + BeforeEach(func() { + sprockets = []Sprocket{ + NewSprocket("Alfalfa"), + NewSprocket("Banana"), + } + }) + + It("should return the returned sprockets", func() { + Ω(client.Sprockets()).Should(Equal(sprockets)) + }) + }) + + Context("when the response is missing", func() { + BeforeEach(func() { + statusCode = http.StatusNotFound + }) + + It("should return an empty list of sprockets", func() { + Ω(client.Sprockets()).Should(BeEmpty()) + }) + }) + + Context("when the response fails to authenticate", func() { + BeforeEach(func() { + statusCode = http.StatusUnauthorized + }) + + It("should return an AuthenticationError error", func() { + sprockets, err := client.Sprockets() + Ω(sprockets).Should(BeEmpty()) + Ω(err).Should(MatchError(AuthenticationError)) + }) + }) + + Context("when the response is a server failure", func() { + BeforeEach(func() { + statusCode = http.StatusInternalServerError + }) + + It("should return an InternalError error", func() { + sprockets, err := client.Sprockets() + Ω(sprockets).Should(BeEmpty()) + Ω(err).Should(MatchError(InternalError)) + }) + }) + }) + + Context("when requesting some sprockets", func() { + BeforeEach(func() { + sprockets = []Sprocket{ + NewSprocket("Alfalfa"), + NewSprocket("Banana"), + } + + server.WrapHandler(0, ghttp.VerifyRequest("GET", "/sprockets", "filter=FOOD")) + }) + + It("should make the request with a filter", func() { + Ω(client.Sprockets("food")).Should(Equal(sprockets)) + }) + }) + }) + }) +*/ +package ghttp + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "reflect" + "regexp" + "sync" + . "github.com/onsi/gomega" +) + +func new() *Server { + return &Server{ + AllowUnhandledRequests: false, + UnhandledRequestStatusCode: http.StatusInternalServerError, + writeLock: &sync.Mutex{}, + } +} + +type routedHandler struct { + method string + pathRegexp *regexp.Regexp + path string + handler http.HandlerFunc +} + +// NewServer returns a new `*ghttp.Server` that wraps an `httptest` server. The server is started automatically. +func NewServer() *Server { + s := new() + s.HTTPTestServer = httptest.NewServer(s) + return s +} + +// NewTLSServer returns a new `*ghttp.Server` that wraps an `httptest` TLS server. The server is started automatically. +func NewTLSServer() *Server { + s := new() + s.HTTPTestServer = httptest.NewTLSServer(s) + return s +} + +type Server struct { + //The underlying httptest server + HTTPTestServer *httptest.Server + + //Defaults to false. If set to true, the Server will allow more requests than there are registered handlers. + AllowUnhandledRequests bool + + //The status code returned when receiving an unhandled request. + //Defaults to http.StatusInternalServerError. + //Only applies if AllowUnhandledRequests is true + UnhandledRequestStatusCode int + + receivedRequests []*http.Request + requestHandlers []http.HandlerFunc + routedHandlers []routedHandler + + writeLock *sync.Mutex + calls int +} + +//URL() returns a url that will hit the server +func (s *Server) URL() string { + return s.HTTPTestServer.URL +} + +//Close() should be called at the end of each test. It spins down and cleans up the test server. +func (s *Server) Close() { + server := s.HTTPTestServer + s.HTTPTestServer = nil + server.Close() +} + +//ServeHTTP() makes Server an http.Handler +//When the server receives a request it handles the request in the following order: +// +//1. If the request matches a handler registered with RouteToHandler, that handler is called. +//2. Otherwise, if there are handlers registered via AppendHandlers, those handlers are called in order. +//3. If all registered handlers have been called then: +// a) If AllowUnhandledRequests is true, the request will be handled with response code of UnhandledRequestStatusCode +// b) If AllowUnhandledRequests is false, the request will not be handled and the current test will be marked as failed. +func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { + s.writeLock.Lock() + defer s.writeLock.Unlock() + defer func() { + recover() + }() + + if routedHandler, ok := s.handlerForRoute(req.Method, req.URL.Path); ok { + routedHandler(w, req) + } else if s.calls < len(s.requestHandlers) { + s.requestHandlers[s.calls](w, req) + s.calls++ + } else { + if s.AllowUnhandledRequests { + ioutil.ReadAll(req.Body) + req.Body.Close() + w.WriteHeader(s.UnhandledRequestStatusCode) + } else { + Ω(req).Should(BeNil(), "Received Unhandled Request") + } + } + s.receivedRequests = append(s.receivedRequests, req) +} + +//ReceivedRequests is an array containing all requests received by the server (both handled and unhandled requests) +func (s *Server) ReceivedRequests() []*http.Request { + s.writeLock.Lock() + defer s.writeLock.Unlock() + + return s.receivedRequests +} + +//RouteToHandler can be used to register handlers that will always handle requests that match +//the passed in method and path. +// +//The path may be either a string object or a *regexp.Regexp. +func (s *Server) RouteToHandler(method string, path interface{}, handler http.HandlerFunc) { + s.writeLock.Lock() + defer s.writeLock.Unlock() + + rh := routedHandler{ + method: method, + handler: handler, + } + + switch p := path.(type) { + case *regexp.Regexp: + rh.pathRegexp = p + case string: + rh.path = p + default: + panic("path must be a string or a regular expression") + } + + for i, existingRH := range s.routedHandlers { + if existingRH.method == method && + reflect.DeepEqual(existingRH.pathRegexp, rh.pathRegexp) && + existingRH.path == rh.path { + s.routedHandlers[i] = rh + return + } + } + s.routedHandlers = append(s.routedHandlers, rh) +} + +func (s *Server) handlerForRoute(method string, path string) (http.HandlerFunc, bool) { + for _, rh := range s.routedHandlers { + if rh.method == method { + if rh.pathRegexp != nil { + if rh.pathRegexp.Match([]byte(path)) { + return rh.handler, true + } + } else if rh.path == path { + return rh.handler, true + } + } + } + + return nil, false +} + +//AppendHandlers will appends http.HandlerFuncs to the server's list of registered handlers. The first incoming request is handled by the first handler, the second by the second, etc... +func (s *Server) AppendHandlers(handlers ...http.HandlerFunc) { + s.writeLock.Lock() + defer s.writeLock.Unlock() + + s.requestHandlers = append(s.requestHandlers, handlers...) +} + +//SetHandler overrides the registered handler at the passed in index with the passed in handler +//This is useful, for example, when a server has been set up in a shared context, but must be tweaked +//for a particular test. +func (s *Server) SetHandler(index int, handler http.HandlerFunc) { + s.writeLock.Lock() + defer s.writeLock.Unlock() + + s.requestHandlers[index] = handler +} + +//GetHandler returns the handler registered at the passed in index. +func (s *Server) GetHandler(index int) http.HandlerFunc { + s.writeLock.Lock() + defer s.writeLock.Unlock() + + return s.requestHandlers[index] +} + +//WrapHandler combines the passed in handler with the handler registered at the passed in index. +//This is useful, for example, when a server has been set up in a shared context but must be tweaked +//for a particular test. +// +//If the currently registered handler is A, and the new passed in handler is B then +//WrapHandler will generate a new handler that first calls A, then calls B, and assign it to index +func (s *Server) WrapHandler(index int, handler http.HandlerFunc) { + existingHandler := s.GetHandler(index) + s.SetHandler(index, CombineHandlers(existingHandler, handler)) +} diff --git a/vendor/github.com/onsi/gomega/ghttp/test_server_suite_test.go b/vendor/github.com/onsi/gomega/ghttp/test_server_suite_test.go new file mode 100644 index 0000000..7c12360 --- /dev/null +++ b/vendor/github.com/onsi/gomega/ghttp/test_server_suite_test.go @@ -0,0 +1,13 @@ +package ghttp_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestGHTTP(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "GHTTP Suite") +} diff --git a/vendor/github.com/onsi/gomega/ghttp/test_server_test.go b/vendor/github.com/onsi/gomega/ghttp/test_server_test.go new file mode 100644 index 0000000..d992fb4 --- /dev/null +++ b/vendor/github.com/onsi/gomega/ghttp/test_server_test.go @@ -0,0 +1,555 @@ +package ghttp_test + +import ( + "bytes" + "io/ioutil" + "net/http" + "regexp" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/ghttp" +) + +var _ = Describe("TestServer", func() { + var ( + resp *http.Response + err error + s *Server + ) + + BeforeEach(func() { + s = NewServer() + }) + + AfterEach(func() { + s.Close() + }) + + Describe("allowing unhandled requests", func() { + Context("when true", func() { + BeforeEach(func() { + s.AllowUnhandledRequests = true + s.UnhandledRequestStatusCode = http.StatusForbidden + resp, err = http.Get(s.URL() + "/foo") + Ω(err).ShouldNot(HaveOccurred()) + }) + + It("should allow unhandled requests and respond with the passed in status code", func() { + Ω(err).ShouldNot(HaveOccurred()) + Ω(resp.StatusCode).Should(Equal(http.StatusForbidden)) + + data, err := ioutil.ReadAll(resp.Body) + Ω(err).ShouldNot(HaveOccurred()) + Ω(data).Should(BeEmpty()) + }) + + It("should record the requests", func() { + Ω(s.ReceivedRequests()).Should(HaveLen(1)) + Ω(s.ReceivedRequests()[0].URL.Path).Should(Equal("/foo")) + }) + }) + + Context("when false", func() { + It("should fail when attempting a request", func() { + failures := InterceptGomegaFailures(func() { + http.Get(s.URL() + "/foo") + }) + + Ω(failures[0]).Should(ContainSubstring("Received Unhandled Request")) + }) + }) + }) + + Describe("Managing Handlers", func() { + var called []string + BeforeEach(func() { + called = []string{} + s.RouteToHandler("GET", "/routed", func(w http.ResponseWriter, req *http.Request) { + called = append(called, "r1") + }) + s.RouteToHandler("POST", regexp.MustCompile(`/routed\d`), func(w http.ResponseWriter, req *http.Request) { + called = append(called, "r2") + }) + s.AppendHandlers(func(w http.ResponseWriter, req *http.Request) { + called = append(called, "A") + }, func(w http.ResponseWriter, req *http.Request) { + called = append(called, "B") + }) + }) + + It("should prefer routed handlers if there is a match", func() { + http.Get(s.URL() + "/routed") + http.Post(s.URL()+"/routed7", "application/json", nil) + http.Get(s.URL() + "/foo") + http.Get(s.URL() + "/routed") + http.Post(s.URL()+"/routed9", "application/json", nil) + http.Get(s.URL() + "/bar") + + failures := InterceptGomegaFailures(func() { + http.Get(s.URL() + "/foo") + http.Get(s.URL() + "/routed/not/a/match") + http.Get(s.URL() + "/routed7") + http.Post(s.URL()+"/routed", "application/json", nil) + }) + + Ω(failures[0]).Should(ContainSubstring("Received Unhandled Request")) + Ω(failures).Should(HaveLen(4)) + + http.Post(s.URL()+"/routed3", "application/json", nil) + + Ω(called).Should(Equal([]string{"r1", "r2", "A", "r1", "r2", "B", "r2"})) + }) + + It("should override routed handlers when reregistered", func() { + s.RouteToHandler("GET", "/routed", func(w http.ResponseWriter, req *http.Request) { + called = append(called, "r3") + }) + s.RouteToHandler("POST", regexp.MustCompile(`/routed\d`), func(w http.ResponseWriter, req *http.Request) { + called = append(called, "r4") + }) + + http.Get(s.URL() + "/routed") + http.Post(s.URL()+"/routed7", "application/json", nil) + + Ω(called).Should(Equal([]string{"r3", "r4"})) + }) + + It("should call the appended handlers, in order, as requests come in", func() { + http.Get(s.URL() + "/foo") + Ω(called).Should(Equal([]string{"A"})) + + http.Get(s.URL() + "/foo") + Ω(called).Should(Equal([]string{"A", "B"})) + + failures := InterceptGomegaFailures(func() { + http.Get(s.URL() + "/foo") + }) + + Ω(failures[0]).Should(ContainSubstring("Received Unhandled Request")) + }) + + Describe("Overwriting an existing handler", func() { + BeforeEach(func() { + s.SetHandler(0, func(w http.ResponseWriter, req *http.Request) { + called = append(called, "C") + }) + }) + + It("should override the specified handler", func() { + http.Get(s.URL() + "/foo") + http.Get(s.URL() + "/foo") + Ω(called).Should(Equal([]string{"C", "B"})) + }) + }) + + Describe("Getting an existing handler", func() { + It("should return the handler func", func() { + s.GetHandler(1)(nil, nil) + Ω(called).Should(Equal([]string{"B"})) + }) + }) + + Describe("Wrapping an existing handler", func() { + BeforeEach(func() { + s.WrapHandler(0, func(w http.ResponseWriter, req *http.Request) { + called = append(called, "C") + }) + }) + + It("should wrap the existing handler in a new handler", func() { + http.Get(s.URL() + "/foo") + http.Get(s.URL() + "/foo") + Ω(called).Should(Equal([]string{"A", "C", "B"})) + }) + }) + }) + + Describe("Request Handlers", func() { + Describe("VerifyRequest", func() { + BeforeEach(func() { + s.AppendHandlers(VerifyRequest("GET", "/foo")) + }) + + It("should verify the method, path", func() { + resp, err = http.Get(s.URL() + "/foo?baz=bar") + Ω(err).ShouldNot(HaveOccurred()) + }) + + It("should verify the method, path", func() { + failures := InterceptGomegaFailures(func() { + http.Get(s.URL() + "/foo2") + }) + Ω(failures).Should(HaveLen(1)) + }) + + It("should verify the method, path", func() { + failures := InterceptGomegaFailures(func() { + http.Post(s.URL()+"/foo", "application/json", nil) + }) + Ω(failures).Should(HaveLen(1)) + }) + + Context("when passed a rawQuery", func() { + It("should also be possible to verify the rawQuery", func() { + s.SetHandler(0, VerifyRequest("GET", "/foo", "baz=bar")) + resp, err = http.Get(s.URL() + "/foo?baz=bar") + Ω(err).ShouldNot(HaveOccurred()) + }) + }) + + Context("when passed a matcher for path", func() { + It("should apply the matcher", func() { + s.SetHandler(0, VerifyRequest("GET", MatchRegexp(`/foo/[a-f]*/3`))) + resp, err = http.Get(s.URL() + "/foo/abcdefa/3") + Ω(err).ShouldNot(HaveOccurred()) + }) + }) + }) + + Describe("VerifyContentType", func() { + BeforeEach(func() { + s.AppendHandlers(CombineHandlers( + VerifyRequest("GET", "/foo"), + VerifyContentType("application/octet-stream"), + )) + }) + + It("should verify the content type", func() { + req, err := http.NewRequest("GET", s.URL()+"/foo", nil) + Ω(err).ShouldNot(HaveOccurred()) + req.Header.Set("Content-Type", "application/octet-stream") + + resp, err = http.DefaultClient.Do(req) + Ω(err).ShouldNot(HaveOccurred()) + }) + + It("should verify the content type", func() { + req, err := http.NewRequest("GET", s.URL()+"/foo", nil) + Ω(err).ShouldNot(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + + failures := InterceptGomegaFailures(func() { + http.DefaultClient.Do(req) + }) + Ω(failures).Should(HaveLen(1)) + }) + }) + + Describe("Verify BasicAuth", func() { + BeforeEach(func() { + s.AppendHandlers(CombineHandlers( + VerifyRequest("GET", "/foo"), + VerifyBasicAuth("bob", "password"), + )) + }) + + It("should verify basic auth", func() { + req, err := http.NewRequest("GET", s.URL()+"/foo", nil) + Ω(err).ShouldNot(HaveOccurred()) + req.SetBasicAuth("bob", "password") + + resp, err = http.DefaultClient.Do(req) + Ω(err).ShouldNot(HaveOccurred()) + }) + + It("should verify basic auth", func() { + req, err := http.NewRequest("GET", s.URL()+"/foo", nil) + Ω(err).ShouldNot(HaveOccurred()) + req.SetBasicAuth("bob", "bassword") + + failures := InterceptGomegaFailures(func() { + http.DefaultClient.Do(req) + }) + Ω(failures).Should(HaveLen(1)) + }) + + }) + + Describe("VerifyHeader", func() { + BeforeEach(func() { + s.AppendHandlers(CombineHandlers( + VerifyRequest("GET", "/foo"), + VerifyHeader(http.Header{ + "accept": []string{"jpeg", "png"}, + "cache-control": []string{"omicron"}, + "Return-Path": []string{"hobbiton"}, + }), + )) + }) + + It("should verify the headers", func() { + req, err := http.NewRequest("GET", s.URL()+"/foo", nil) + Ω(err).ShouldNot(HaveOccurred()) + req.Header.Add("Accept", "jpeg") + req.Header.Add("Accept", "png") + req.Header.Add("Cache-Control", "omicron") + req.Header.Add("return-path", "hobbiton") + + resp, err = http.DefaultClient.Do(req) + Ω(err).ShouldNot(HaveOccurred()) + }) + + It("should verify the headers", func() { + req, err := http.NewRequest("GET", s.URL()+"/foo", nil) + Ω(err).ShouldNot(HaveOccurred()) + req.Header.Add("Schmaccept", "jpeg") + req.Header.Add("Schmaccept", "png") + req.Header.Add("Cache-Control", "omicron") + req.Header.Add("return-path", "hobbiton") + + failures := InterceptGomegaFailures(func() { + http.DefaultClient.Do(req) + }) + Ω(failures).Should(HaveLen(1)) + }) + }) + + Describe("VerifyHeaderKV", func() { + BeforeEach(func() { + s.AppendHandlers(CombineHandlers( + VerifyRequest("GET", "/foo"), + VerifyHeaderKV("accept", "jpeg", "png"), + VerifyHeaderKV("cache-control", "omicron"), + VerifyHeaderKV("Return-Path", "hobbiton"), + )) + }) + + It("should verify the headers", func() { + req, err := http.NewRequest("GET", s.URL()+"/foo", nil) + Ω(err).ShouldNot(HaveOccurred()) + req.Header.Add("Accept", "jpeg") + req.Header.Add("Accept", "png") + req.Header.Add("Cache-Control", "omicron") + req.Header.Add("return-path", "hobbiton") + + resp, err = http.DefaultClient.Do(req) + Ω(err).ShouldNot(HaveOccurred()) + }) + + It("should verify the headers", func() { + req, err := http.NewRequest("GET", s.URL()+"/foo", nil) + Ω(err).ShouldNot(HaveOccurred()) + req.Header.Add("Accept", "jpeg") + req.Header.Add("Cache-Control", "omicron") + req.Header.Add("return-path", "hobbiton") + + failures := InterceptGomegaFailures(func() { + http.DefaultClient.Do(req) + }) + Ω(failures).Should(HaveLen(1)) + }) + }) + + Describe("VerifyJSON", func() { + BeforeEach(func() { + s.AppendHandlers(CombineHandlers( + VerifyRequest("POST", "/foo"), + VerifyJSON(`{"a":3, "b":2}`), + )) + }) + + It("should verify the json body and the content type", func() { + resp, err = http.Post(s.URL()+"/foo", "application/json", bytes.NewReader([]byte(`{"b":2, "a":3}`))) + Ω(err).ShouldNot(HaveOccurred()) + }) + + It("should verify the json body and the content type", func() { + failures := InterceptGomegaFailures(func() { + http.Post(s.URL()+"/foo", "application/json", bytes.NewReader([]byte(`{"b":2, "a":4}`))) + }) + Ω(failures).Should(HaveLen(1)) + }) + + It("should verify the json body and the content type", func() { + failures := InterceptGomegaFailures(func() { + http.Post(s.URL()+"/foo", "application/not-json", bytes.NewReader([]byte(`{"b":2, "a":3}`))) + }) + Ω(failures).Should(HaveLen(1)) + }) + }) + + Describe("VerifyJSONRepresenting", func() { + BeforeEach(func() { + s.AppendHandlers(CombineHandlers( + VerifyRequest("POST", "/foo"), + VerifyJSONRepresenting([]int{1, 3, 5}), + )) + }) + + It("should verify the json body and the content type", func() { + resp, err = http.Post(s.URL()+"/foo", "application/json", bytes.NewReader([]byte(`[1,3,5]`))) + Ω(err).ShouldNot(HaveOccurred()) + }) + + It("should verify the json body and the content type", func() { + failures := InterceptGomegaFailures(func() { + http.Post(s.URL()+"/foo", "application/json", bytes.NewReader([]byte(`[1,3]`))) + }) + Ω(failures).Should(HaveLen(1)) + }) + }) + + Describe("RespondWith", func() { + Context("without headers", func() { + BeforeEach(func() { + s.AppendHandlers(CombineHandlers( + VerifyRequest("POST", "/foo"), + RespondWith(http.StatusCreated, "sweet"), + ), CombineHandlers( + VerifyRequest("POST", "/foo"), + RespondWith(http.StatusOK, []byte("sour")), + )) + }) + + It("should return the response", func() { + resp, err = http.Post(s.URL()+"/foo", "application/json", nil) + Ω(err).ShouldNot(HaveOccurred()) + + Ω(resp.StatusCode).Should(Equal(http.StatusCreated)) + + body, err := ioutil.ReadAll(resp.Body) + Ω(err).ShouldNot(HaveOccurred()) + Ω(body).Should(Equal([]byte("sweet"))) + + resp, err = http.Post(s.URL()+"/foo", "application/json", nil) + Ω(err).ShouldNot(HaveOccurred()) + + Ω(resp.StatusCode).Should(Equal(http.StatusOK)) + + body, err = ioutil.ReadAll(resp.Body) + Ω(err).ShouldNot(HaveOccurred()) + Ω(body).Should(Equal([]byte("sour"))) + }) + }) + + Context("with headers", func() { + BeforeEach(func() { + s.AppendHandlers(CombineHandlers( + VerifyRequest("POST", "/foo"), + RespondWith(http.StatusCreated, "sweet", http.Header{"X-Custom-Header": []string{"my header"}}), + )) + }) + + It("should return the headers too", func() { + resp, err = http.Post(s.URL()+"/foo", "application/json", nil) + Ω(err).ShouldNot(HaveOccurred()) + + Ω(resp.StatusCode).Should(Equal(http.StatusCreated)) + Ω(ioutil.ReadAll(resp.Body)).Should(Equal([]byte("sweet"))) + Ω(resp.Header.Get("X-Custom-Header")).Should(Equal("my header")) + }) + }) + }) + + Describe("RespondWithPtr", func() { + var code int + var byteBody []byte + var stringBody string + BeforeEach(func() { + code = http.StatusOK + byteBody = []byte("sweet") + stringBody = "sour" + + s.AppendHandlers(CombineHandlers( + VerifyRequest("POST", "/foo"), + RespondWithPtr(&code, &byteBody), + ), CombineHandlers( + VerifyRequest("POST", "/foo"), + RespondWithPtr(&code, &stringBody), + )) + }) + + It("should return the response", func() { + code = http.StatusCreated + byteBody = []byte("tasty") + stringBody = "treat" + + resp, err = http.Post(s.URL()+"/foo", "application/json", nil) + Ω(err).ShouldNot(HaveOccurred()) + + Ω(resp.StatusCode).Should(Equal(http.StatusCreated)) + + body, err := ioutil.ReadAll(resp.Body) + Ω(err).ShouldNot(HaveOccurred()) + Ω(body).Should(Equal([]byte("tasty"))) + + resp, err = http.Post(s.URL()+"/foo", "application/json", nil) + Ω(err).ShouldNot(HaveOccurred()) + + Ω(resp.StatusCode).Should(Equal(http.StatusCreated)) + + body, err = ioutil.ReadAll(resp.Body) + Ω(err).ShouldNot(HaveOccurred()) + Ω(body).Should(Equal([]byte("treat"))) + }) + + Context("when passed a nil body", func() { + BeforeEach(func() { + s.SetHandler(0, CombineHandlers( + VerifyRequest("POST", "/foo"), + RespondWithPtr(&code, nil), + )) + }) + + It("should return an empty body and not explode", func() { + resp, err = http.Post(s.URL()+"/foo", "application/json", nil) + + Ω(err).ShouldNot(HaveOccurred()) + Ω(resp.StatusCode).Should(Equal(http.StatusOK)) + body, err := ioutil.ReadAll(resp.Body) + Ω(err).ShouldNot(HaveOccurred()) + Ω(body).Should(BeEmpty()) + + Ω(s.ReceivedRequests()).Should(HaveLen(1)) + }) + }) + }) + + Describe("RespondWithJSON", func() { + BeforeEach(func() { + s.AppendHandlers(CombineHandlers( + VerifyRequest("POST", "/foo"), + RespondWithJSONEncoded(http.StatusCreated, []int{1, 2, 3}), + )) + }) + + It("should return the response", func() { + resp, err = http.Post(s.URL()+"/foo", "application/json", nil) + Ω(err).ShouldNot(HaveOccurred()) + + Ω(resp.StatusCode).Should(Equal(http.StatusCreated)) + + body, err := ioutil.ReadAll(resp.Body) + Ω(err).ShouldNot(HaveOccurred()) + Ω(body).Should(MatchJSON("[1,2,3]")) + }) + }) + + Describe("RespondWithJSONPtr", func() { + var code int + var object interface{} + BeforeEach(func() { + code = http.StatusOK + object = []int{1, 2, 3} + + s.AppendHandlers(CombineHandlers( + VerifyRequest("POST", "/foo"), + RespondWithJSONEncodedPtr(&code, &object), + )) + }) + + It("should return the response", func() { + code = http.StatusCreated + object = []int{4, 5, 6} + resp, err = http.Post(s.URL()+"/foo", "application/json", nil) + Ω(err).ShouldNot(HaveOccurred()) + + Ω(resp.StatusCode).Should(Equal(http.StatusCreated)) + + body, err := ioutil.ReadAll(resp.Body) + Ω(err).ShouldNot(HaveOccurred()) + Ω(body).Should(MatchJSON("[4,5,6]")) + }) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/gomega_dsl.go b/vendor/github.com/onsi/gomega/gomega_dsl.go new file mode 100644 index 0000000..acfd787 --- /dev/null +++ b/vendor/github.com/onsi/gomega/gomega_dsl.go @@ -0,0 +1,321 @@ +/* +Gomega is the Ginkgo BDD-style testing framework's preferred matcher library. + +The godoc documentation describes Gomega's API. More comprehensive documentation (with examples!) is available at http://onsi.github.io/gomega/ + +Gomega on Github: http://github.com/onsi/gomega + +Learn more about Ginkgo online: http://onsi.github.io/ginkgo + +Ginkgo on Github: http://github.com/onsi/ginkgo + +Gomega is MIT-Licensed +*/ +package gomega + +import ( + "fmt" + "reflect" + "time" + + "github.com/onsi/gomega/internal/assertion" + "github.com/onsi/gomega/internal/asyncassertion" + "github.com/onsi/gomega/internal/testingtsupport" + "github.com/onsi/gomega/types" +) + +const GOMEGA_VERSION = "1.0" + +var globalFailHandler types.GomegaFailHandler + +var defaultEventuallyTimeout = time.Second +var defaultEventuallyPollingInterval = 10 * time.Millisecond +var defaultConsistentlyDuration = 100 * time.Millisecond +var defaultConsistentlyPollingInterval = 10 * time.Millisecond + +//RegisterFailHandler connects Ginkgo to Gomega. When a matcher fails +//the fail handler passed into RegisterFailHandler is called. +func RegisterFailHandler(handler types.GomegaFailHandler) { + globalFailHandler = handler +} + +//RegisterTestingT connects Gomega to Golang's XUnit style +//Testing.T tests. You'll need to call this at the top of each XUnit style test: +// +// func TestFarmHasCow(t *testing.T) { +// RegisterTestingT(t) +// +// f := farm.New([]string{"Cow", "Horse"}) +// Expect(f.HasCow()).To(BeTrue(), "Farm should have cow") +// } +// +// Note that this *testing.T is registered *globally* by Gomega (this is why you don't have to +// pass `t` down to the matcher itself). This means that you cannot run the XUnit style tests +// in parallel as the global fail handler cannot point to more than one testing.T at a time. +// +// (As an aside: Ginkgo gets around this limitation by running parallel tests in different *processes*). +func RegisterTestingT(t types.GomegaTestingT) { + RegisterFailHandler(testingtsupport.BuildTestingTGomegaFailHandler(t)) +} + +//InterceptGomegaHandlers runs a given callback and returns an array of +//failure messages generated by any Gomega assertions within the callback. +// +//This is accomplished by temporarily replacing the *global* fail handler +//with a fail handler that simply annotates failures. The original fail handler +//is reset when InterceptGomegaFailures returns. +// +//This is most useful when testing custom matchers, but can also be used to check +//on a value using a Gomega assertion without causing a test failure. +func InterceptGomegaFailures(f func()) []string { + originalHandler := globalFailHandler + failures := []string{} + RegisterFailHandler(func(message string, callerSkip ...int) { + failures = append(failures, message) + }) + f() + RegisterFailHandler(originalHandler) + return failures +} + +//Ω wraps an actual value allowing assertions to be made on it: +// Ω("foo").Should(Equal("foo")) +// +//If Ω is passed more than one argument it will pass the *first* argument to the matcher. +//All subsequent arguments will be required to be nil/zero. +// +//This is convenient if you want to make an assertion on a method/function that returns +//a value and an error - a common patter in Go. +// +//For example, given a function with signature: +// func MyAmazingThing() (int, error) +// +//Then: +// Ω(MyAmazingThing()).Should(Equal(3)) +//Will succeed only if `MyAmazingThing()` returns `(3, nil)` +// +//Ω and Expect are identical +func Ω(actual interface{}, extra ...interface{}) GomegaAssertion { + return ExpectWithOffset(0, actual, extra...) +} + +//Expect wraps an actual value allowing assertions to be made on it: +// Expect("foo").To(Equal("foo")) +// +//If Expect is passed more than one argument it will pass the *first* argument to the matcher. +//All subsequent arguments will be required to be nil/zero. +// +//This is convenient if you want to make an assertion on a method/function that returns +//a value and an error - a common patter in Go. +// +//For example, given a function with signature: +// func MyAmazingThing() (int, error) +// +//Then: +// Expect(MyAmazingThing()).Should(Equal(3)) +//Will succeed only if `MyAmazingThing()` returns `(3, nil)` +// +//Expect and Ω are identical +func Expect(actual interface{}, extra ...interface{}) GomegaAssertion { + return ExpectWithOffset(0, actual, extra...) +} + +//ExpectWithOffset wraps an actual value allowing assertions to be made on it: +// ExpectWithOffset(1, "foo").To(Equal("foo")) +// +//Unlike `Expect` and `Ω`, `ExpectWithOffset` takes an additional integer argument +//this is used to modify the call-stack offset when computing line numbers. +// +//This is most useful in helper functions that make assertions. If you want Gomega's +//error message to refer to the calling line in the test (as opposed to the line in the helper function) +//set the first argument of `ExpectWithOffset` appropriately. +func ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) GomegaAssertion { + return assertion.New(actual, globalFailHandler, offset, extra...) +} + +//Eventually wraps an actual value allowing assertions to be made on it. +//The assertion is tried periodically until it passes or a timeout occurs. +// +//Both the timeout and polling interval are configurable as optional arguments: +//The first optional argument is the timeout +//The second optional argument is the polling interval +// +//Both intervals can either be specified as time.Duration, parsable duration strings or as floats/integers. In the +//last case they are interpreted as seconds. +// +//If Eventually is passed an actual that is a function taking no arguments and returning at least one value, +//then Eventually will call the function periodically and try the matcher against the function's first return value. +// +//Example: +// +// Eventually(func() int { +// return thingImPolling.Count() +// }).Should(BeNumerically(">=", 17)) +// +//Note that this example could be rewritten: +// +// Eventually(thingImPolling.Count).Should(BeNumerically(">=", 17)) +// +//If the function returns more than one value, then Eventually will pass the first value to the matcher and +//assert that all other values are nil/zero. +//This allows you to pass Eventually a function that returns a value and an error - a common pattern in Go. +// +//For example, consider a method that returns a value and an error: +// func FetchFromDB() (string, error) +// +//Then +// Eventually(FetchFromDB).Should(Equal("hasselhoff")) +// +//Will pass only if the the returned error is nil and the returned string passes the matcher. +// +//Eventually's default timeout is 1 second, and its default polling interval is 10ms +func Eventually(actual interface{}, intervals ...interface{}) GomegaAsyncAssertion { + return EventuallyWithOffset(0, actual, intervals...) +} + +//EventuallyWithOffset operates like Eventually but takes an additional +//initial argument to indicate an offset in the call stack. This is useful when building helper +//functions that contain matchers. To learn more, read about `ExpectWithOffset`. +func EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) GomegaAsyncAssertion { + timeoutInterval := defaultEventuallyTimeout + pollingInterval := defaultEventuallyPollingInterval + if len(intervals) > 0 { + timeoutInterval = toDuration(intervals[0]) + } + if len(intervals) > 1 { + pollingInterval = toDuration(intervals[1]) + } + return asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, actual, globalFailHandler, timeoutInterval, pollingInterval, offset) +} + +//Consistently wraps an actual value allowing assertions to be made on it. +//The assertion is tried periodically and is required to pass for a period of time. +// +//Both the total time and polling interval are configurable as optional arguments: +//The first optional argument is the duration that Consistently will run for +//The second optional argument is the polling interval +// +//Both intervals can either be specified as time.Duration, parsable duration strings or as floats/integers. In the +//last case they are interpreted as seconds. +// +//If Consistently is passed an actual that is a function taking no arguments and returning at least one value, +//then Consistently will call the function periodically and try the matcher against the function's first return value. +// +//If the function returns more than one value, then Consistently will pass the first value to the matcher and +//assert that all other values are nil/zero. +//This allows you to pass Consistently a function that returns a value and an error - a common pattern in Go. +// +//Consistently is useful in cases where you want to assert that something *does not happen* over a period of tiem. +//For example, you want to assert that a goroutine does *not* send data down a channel. In this case, you could: +// +// Consistently(channel).ShouldNot(Receive()) +// +//Consistently's default duration is 100ms, and its default polling interval is 10ms +func Consistently(actual interface{}, intervals ...interface{}) GomegaAsyncAssertion { + return ConsistentlyWithOffset(0, actual, intervals...) +} + +//ConsistentlyWithOffset operates like Consistnetly but takes an additional +//initial argument to indicate an offset in the call stack. This is useful when building helper +//functions that contain matchers. To learn more, read about `ExpectWithOffset`. +func ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) GomegaAsyncAssertion { + timeoutInterval := defaultConsistentlyDuration + pollingInterval := defaultConsistentlyPollingInterval + if len(intervals) > 0 { + timeoutInterval = toDuration(intervals[0]) + } + if len(intervals) > 1 { + pollingInterval = toDuration(intervals[1]) + } + return asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, actual, globalFailHandler, timeoutInterval, pollingInterval, offset) +} + +//Set the default timeout duration for Eventually. Eventually will repeatedly poll your condition until it succeeds, or until this timeout elapses. +func SetDefaultEventuallyTimeout(t time.Duration) { + defaultEventuallyTimeout = t +} + +//Set the default polling interval for Eventually. +func SetDefaultEventuallyPollingInterval(t time.Duration) { + defaultEventuallyPollingInterval = t +} + +//Set the default duration for Consistently. Consistently will verify that your condition is satsified for this long. +func SetDefaultConsistentlyDuration(t time.Duration) { + defaultConsistentlyDuration = t +} + +//Set the default polling interval for Consistently. +func SetDefaultConsistentlyPollingInterval(t time.Duration) { + defaultConsistentlyPollingInterval = t +} + +//GomegaAsyncAssertion is returned by Eventually and Consistently and polls the actual value passed into Eventually against +//the matcher passed to the Should and ShouldNot methods. +// +//Both Should and ShouldNot take a variadic optionalDescription argument. This is passed on to +//fmt.Sprintf() and is used to annotate failure messages. This allows you to make your failure messages more +//descriptive +// +//Both Should and ShouldNot return a boolean that is true if the assertion passed and false if it failed. +// +//Example: +// +// Eventually(myChannel).Should(Receive(), "Something should have come down the pipe.") +// Consistently(myChannel).ShouldNot(Receive(), "Nothing should have come down the pipe.") +type GomegaAsyncAssertion interface { + Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool + ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool +} + +//GomegaAssertion is returned by Ω and Expect and compares the actual value to the matcher +//passed to the Should/ShouldNot and To/ToNot/NotTo methods. +// +//Typically Should/ShouldNot are used with Ω and To/ToNot/NotTo are used with Expect +//though this is not enforced. +// +//All methods take a variadic optionalDescription argument. This is passed on to fmt.Sprintf() +//and is used to annotate failure messages. +// +//All methods return a bool that is true if hte assertion passed and false if it failed. +// +//Example: +// +// Ω(farm.HasCow()).Should(BeTrue(), "Farm %v should have a cow", farm) +type GomegaAssertion interface { + Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool + ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool + + To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool + ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool + NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool +} + +//OmegaMatcher is deprecated in favor of the better-named and better-organized types.GomegaMatcher but sticks around to support existing code that uses it +type OmegaMatcher types.GomegaMatcher + +func toDuration(input interface{}) time.Duration { + duration, ok := input.(time.Duration) + if ok { + return duration + } + + value := reflect.ValueOf(input) + kind := reflect.TypeOf(input).Kind() + + if reflect.Int <= kind && kind <= reflect.Int64 { + return time.Duration(value.Int()) * time.Second + } else if reflect.Uint <= kind && kind <= reflect.Uint64 { + return time.Duration(value.Uint()) * time.Second + } else if reflect.Float32 <= kind && kind <= reflect.Float64 { + return time.Duration(value.Float() * float64(time.Second)) + } else if reflect.String == kind { + duration, err := time.ParseDuration(value.String()) + if err != nil { + panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input)) + } + return duration + } + + panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input)) +} diff --git a/vendor/github.com/onsi/gomega/internal/assertion/assertion.go b/vendor/github.com/onsi/gomega/internal/assertion/assertion.go new file mode 100644 index 0000000..b73673f --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/assertion/assertion.go @@ -0,0 +1,98 @@ +package assertion + +import ( + "fmt" + "reflect" + + "github.com/onsi/gomega/types" +) + +type Assertion struct { + actualInput interface{} + fail types.GomegaFailHandler + offset int + extra []interface{} +} + +func New(actualInput interface{}, fail types.GomegaFailHandler, offset int, extra ...interface{}) *Assertion { + return &Assertion{ + actualInput: actualInput, + fail: fail, + offset: offset, + extra: extra, + } +} + +func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...) +} + +func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) +} + +func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...) +} + +func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) +} + +func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) +} + +func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string { + switch len(optionalDescription) { + case 0: + return "" + default: + return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" + } +} + +func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { + matches, err := matcher.Match(assertion.actualInput) + description := assertion.buildDescription(optionalDescription...) + if err != nil { + assertion.fail(description+err.Error(), 2+assertion.offset) + return false + } + if matches != desiredMatch { + var message string + if desiredMatch { + message = matcher.FailureMessage(assertion.actualInput) + } else { + message = matcher.NegatedFailureMessage(assertion.actualInput) + } + assertion.fail(description+message, 2+assertion.offset) + return false + } + + return true +} + +func (assertion *Assertion) vetExtras(optionalDescription ...interface{}) bool { + success, message := vetExtras(assertion.extra) + if success { + return true + } + + description := assertion.buildDescription(optionalDescription...) + assertion.fail(description+message, 2+assertion.offset) + return false +} + +func vetExtras(extras []interface{}) (bool, string) { + for i, extra := range extras { + if extra != nil { + zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface() + if !reflect.DeepEqual(zeroValue, extra) { + message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra) + return false, message + } + } + } + return true, "" +} diff --git a/vendor/github.com/onsi/gomega/internal/assertion/assertion_suite_test.go b/vendor/github.com/onsi/gomega/internal/assertion/assertion_suite_test.go new file mode 100644 index 0000000..dae47a4 --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/assertion/assertion_suite_test.go @@ -0,0 +1,13 @@ +package assertion_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestAssertion(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Assertion Suite") +} diff --git a/vendor/github.com/onsi/gomega/internal/assertion/assertion_test.go b/vendor/github.com/onsi/gomega/internal/assertion/assertion_test.go new file mode 100644 index 0000000..f6468c1 --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/assertion/assertion_test.go @@ -0,0 +1,236 @@ +package assertion_test + +import ( + "errors" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/internal/assertion" + "github.com/onsi/gomega/internal/fakematcher" +) + +var _ = Describe("Assertion", func() { + var ( + a *Assertion + failureMessage string + failureCallerSkip int + matcher *fakematcher.FakeMatcher + ) + + input := "The thing I'm testing" + + var fakeFailHandler = func(message string, callerSkip ...int) { + failureMessage = message + if len(callerSkip) == 1 { + failureCallerSkip = callerSkip[0] + } + } + + BeforeEach(func() { + matcher = &fakematcher.FakeMatcher{} + failureMessage = "" + failureCallerSkip = 0 + a = New(input, fakeFailHandler, 1) + }) + + Context("when called", func() { + It("should pass the provided input value to the matcher", func() { + a.Should(matcher) + + Ω(matcher.ReceivedActual).Should(Equal(input)) + matcher.ReceivedActual = "" + + a.ShouldNot(matcher) + + Ω(matcher.ReceivedActual).Should(Equal(input)) + matcher.ReceivedActual = "" + + a.To(matcher) + + Ω(matcher.ReceivedActual).Should(Equal(input)) + matcher.ReceivedActual = "" + + a.ToNot(matcher) + + Ω(matcher.ReceivedActual).Should(Equal(input)) + matcher.ReceivedActual = "" + + a.NotTo(matcher) + + Ω(matcher.ReceivedActual).Should(Equal(input)) + }) + }) + + Context("when the matcher succeeds", func() { + BeforeEach(func() { + matcher.MatchesToReturn = true + matcher.ErrToReturn = nil + }) + + Context("and a positive assertion is being made", func() { + It("should not call the failure callback", func() { + a.Should(matcher) + Ω(failureMessage).Should(Equal("")) + }) + + It("should be true", func() { + Ω(a.Should(matcher)).Should(BeTrue()) + }) + }) + + Context("and a negative assertion is being made", func() { + It("should call the failure callback", func() { + a.ShouldNot(matcher) + Ω(failureMessage).Should(Equal("negative: The thing I'm testing")) + Ω(failureCallerSkip).Should(Equal(3)) + }) + + It("should be false", func() { + Ω(a.ShouldNot(matcher)).Should(BeFalse()) + }) + }) + }) + + Context("when the matcher fails", func() { + BeforeEach(func() { + matcher.MatchesToReturn = false + matcher.ErrToReturn = nil + }) + + Context("and a positive assertion is being made", func() { + It("should call the failure callback", func() { + a.Should(matcher) + Ω(failureMessage).Should(Equal("positive: The thing I'm testing")) + Ω(failureCallerSkip).Should(Equal(3)) + }) + + It("should be false", func() { + Ω(a.Should(matcher)).Should(BeFalse()) + }) + }) + + Context("and a negative assertion is being made", func() { + It("should not call the failure callback", func() { + a.ShouldNot(matcher) + Ω(failureMessage).Should(Equal("")) + }) + + It("should be true", func() { + Ω(a.ShouldNot(matcher)).Should(BeTrue()) + }) + }) + }) + + Context("When reporting a failure", func() { + BeforeEach(func() { + matcher.MatchesToReturn = false + matcher.ErrToReturn = nil + }) + + Context("and there is an optional description", func() { + It("should append the description to the failure message", func() { + a.Should(matcher, "A description") + Ω(failureMessage).Should(Equal("A description\npositive: The thing I'm testing")) + Ω(failureCallerSkip).Should(Equal(3)) + }) + }) + + Context("and there are multiple arguments to the optional description", func() { + It("should append the formatted description to the failure message", func() { + a.Should(matcher, "A description of [%d]", 3) + Ω(failureMessage).Should(Equal("A description of [3]\npositive: The thing I'm testing")) + Ω(failureCallerSkip).Should(Equal(3)) + }) + }) + }) + + Context("When the matcher returns an error", func() { + BeforeEach(func() { + matcher.ErrToReturn = errors.New("Kaboom!") + }) + + Context("and a positive assertion is being made", func() { + It("should call the failure callback", func() { + matcher.MatchesToReturn = true + a.Should(matcher) + Ω(failureMessage).Should(Equal("Kaboom!")) + Ω(failureCallerSkip).Should(Equal(3)) + }) + }) + + Context("and a negative assertion is being made", func() { + It("should call the failure callback", func() { + matcher.MatchesToReturn = false + a.ShouldNot(matcher) + Ω(failureMessage).Should(Equal("Kaboom!")) + Ω(failureCallerSkip).Should(Equal(3)) + }) + }) + + It("should always be false", func() { + Ω(a.Should(matcher)).Should(BeFalse()) + Ω(a.ShouldNot(matcher)).Should(BeFalse()) + }) + }) + + Context("when there are extra parameters", func() { + It("(a simple example)", func() { + Ω(func() (string, int, error) { + return "foo", 0, nil + }()).Should(Equal("foo")) + }) + + Context("when the parameters are all nil or zero", func() { + It("should invoke the matcher", func() { + matcher.MatchesToReturn = true + matcher.ErrToReturn = nil + + var typedNil []string + a = New(input, fakeFailHandler, 1, 0, nil, typedNil) + + result := a.Should(matcher) + Ω(result).Should(BeTrue()) + Ω(matcher.ReceivedActual).Should(Equal(input)) + + Ω(failureMessage).Should(BeZero()) + }) + }) + + Context("when any of the parameters are not nil or zero", func() { + It("should call the failure callback", func() { + matcher.MatchesToReturn = false + matcher.ErrToReturn = nil + + a = New(input, fakeFailHandler, 1, errors.New("foo")) + result := a.Should(matcher) + Ω(result).Should(BeFalse()) + Ω(matcher.ReceivedActual).Should(BeZero(), "The matcher doesn't even get called") + Ω(failureMessage).Should(ContainSubstring("foo")) + failureMessage = "" + + a = New(input, fakeFailHandler, 1, nil, 1) + result = a.ShouldNot(matcher) + Ω(result).Should(BeFalse()) + Ω(failureMessage).Should(ContainSubstring("1")) + failureMessage = "" + + a = New(input, fakeFailHandler, 1, nil, 0, []string{"foo"}) + result = a.To(matcher) + Ω(result).Should(BeFalse()) + Ω(failureMessage).Should(ContainSubstring("foo")) + failureMessage = "" + + a = New(input, fakeFailHandler, 1, nil, 0, []string{"foo"}) + result = a.ToNot(matcher) + Ω(result).Should(BeFalse()) + Ω(failureMessage).Should(ContainSubstring("foo")) + failureMessage = "" + + a = New(input, fakeFailHandler, 1, nil, 0, []string{"foo"}) + result = a.NotTo(matcher) + Ω(result).Should(BeFalse()) + Ω(failureMessage).Should(ContainSubstring("foo")) + Ω(failureCallerSkip).Should(Equal(3)) + }) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go b/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go new file mode 100644 index 0000000..7bbec43 --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go @@ -0,0 +1,197 @@ +package asyncassertion + +import ( + "errors" + "fmt" + "reflect" + "time" + + "github.com/onsi/gomega/types" +) + +type AsyncAssertionType uint + +const ( + AsyncAssertionTypeEventually AsyncAssertionType = iota + AsyncAssertionTypeConsistently +) + +type AsyncAssertion struct { + asyncType AsyncAssertionType + actualInput interface{} + timeoutInterval time.Duration + pollingInterval time.Duration + fail types.GomegaFailHandler + offset int +} + +func New(asyncType AsyncAssertionType, actualInput interface{}, fail types.GomegaFailHandler, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion { + actualType := reflect.TypeOf(actualInput) + if actualType.Kind() == reflect.Func { + if actualType.NumIn() != 0 || actualType.NumOut() == 0 { + panic("Expected a function with no arguments and one or more return values.") + } + } + + return &AsyncAssertion{ + asyncType: asyncType, + actualInput: actualInput, + fail: fail, + timeoutInterval: timeoutInterval, + pollingInterval: pollingInterval, + offset: offset, + } +} + +func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + return assertion.match(matcher, true, optionalDescription...) +} + +func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + return assertion.match(matcher, false, optionalDescription...) +} + +func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string { + switch len(optionalDescription) { + case 0: + return "" + default: + return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" + } +} + +func (assertion *AsyncAssertion) actualInputIsAFunction() bool { + actualType := reflect.TypeOf(assertion.actualInput) + return actualType.Kind() == reflect.Func && actualType.NumIn() == 0 && actualType.NumOut() > 0 +} + +func (assertion *AsyncAssertion) pollActual() (interface{}, error) { + if assertion.actualInputIsAFunction() { + values := reflect.ValueOf(assertion.actualInput).Call([]reflect.Value{}) + + extras := []interface{}{} + for _, value := range values[1:] { + extras = append(extras, value.Interface()) + } + + success, message := vetExtras(extras) + + if !success { + return nil, errors.New(message) + } + + return values[0].Interface(), nil + } + + return assertion.actualInput, nil +} + +type oracleMatcher interface { + MatchMayChangeInTheFuture(actual interface{}) bool +} + +func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool { + if assertion.actualInputIsAFunction() { + return true + } + + oracleMatcher, ok := matcher.(oracleMatcher) + if !ok { + return true + } + + return oracleMatcher.MatchMayChangeInTheFuture(value) +} + +func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { + timer := time.Now() + timeout := time.After(assertion.timeoutInterval) + + description := assertion.buildDescription(optionalDescription...) + + var matches bool + var err error + mayChange := true + value, err := assertion.pollActual() + if err == nil { + mayChange = assertion.matcherMayChange(matcher, value) + matches, err = matcher.Match(value) + } + + fail := func(preamble string) { + errMsg := "" + message := "" + if err != nil { + errMsg = "Error: " + err.Error() + } else { + if desiredMatch { + message = matcher.FailureMessage(value) + } else { + message = matcher.NegatedFailureMessage(value) + } + } + assertion.fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset) + } + + if assertion.asyncType == AsyncAssertionTypeEventually { + for { + if err == nil && matches == desiredMatch { + return true + } + + if !mayChange { + fail("No future change is possible. Bailing out early") + return false + } + + select { + case <-time.After(assertion.pollingInterval): + value, err = assertion.pollActual() + if err == nil { + mayChange = assertion.matcherMayChange(matcher, value) + matches, err = matcher.Match(value) + } + case <-timeout: + fail("Timed out") + return false + } + } + } else if assertion.asyncType == AsyncAssertionTypeConsistently { + for { + if !(err == nil && matches == desiredMatch) { + fail("Failed") + return false + } + + if !mayChange { + return true + } + + select { + case <-time.After(assertion.pollingInterval): + value, err = assertion.pollActual() + if err == nil { + mayChange = assertion.matcherMayChange(matcher, value) + matches, err = matcher.Match(value) + } + case <-timeout: + return true + } + } + } + + return false +} + +func vetExtras(extras []interface{}) (bool, string) { + for i, extra := range extras { + if extra != nil { + zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface() + if !reflect.DeepEqual(zeroValue, extra) { + message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra) + return false, message + } + } + } + return true, "" +} diff --git a/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion_suite_test.go b/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion_suite_test.go new file mode 100644 index 0000000..bdb0c3d --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion_suite_test.go @@ -0,0 +1,13 @@ +package asyncassertion_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestAsyncAssertion(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "AsyncAssertion Suite") +} diff --git a/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion_test.go b/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion_test.go new file mode 100644 index 0000000..2afd608 --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion_test.go @@ -0,0 +1,315 @@ +package asyncassertion_test + +import ( + "errors" + "time" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/internal/asyncassertion" +) + +var _ = Describe("Async Assertion", func() { + var ( + failureMessage string + callerSkip int + ) + + var fakeFailHandler = func(message string, skip ...int) { + failureMessage = message + callerSkip = skip[0] + } + + BeforeEach(func() { + failureMessage = "" + callerSkip = 0 + }) + + Describe("Eventually", func() { + Context("the positive case", func() { + It("should poll the function and matcher", func() { + arr := []int{} + a := New(AsyncAssertionTypeEventually, func() []int { + arr = append(arr, 1) + return arr + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.Should(HaveLen(10)) + + Ω(arr).Should(HaveLen(10)) + Ω(failureMessage).Should(BeZero()) + }) + + It("should continue when the matcher errors", func() { + var arr = []int{} + a := New(AsyncAssertionTypeEventually, func() interface{} { + arr = append(arr, 1) + if len(arr) == 4 { + return 0 //this should cause the matcher to error + } + return arr + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.Should(HaveLen(4), "My description %d", 2) + + Ω(failureMessage).Should(ContainSubstring("Timed out after")) + Ω(failureMessage).Should(ContainSubstring("My description 2")) + Ω(callerSkip).Should(Equal(4)) + }) + + It("should be able to timeout", func() { + arr := []int{} + a := New(AsyncAssertionTypeEventually, func() []int { + arr = append(arr, 1) + return arr + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.Should(HaveLen(11), "My description %d", 2) + + Ω(arr).Should(HaveLen(10)) + Ω(failureMessage).Should(ContainSubstring("Timed out after")) + Ω(failureMessage).Should(ContainSubstring("<[]int | len:10"), "Should pass the correct value to the matcher message formatter.") + Ω(failureMessage).Should(ContainSubstring("My description 2")) + Ω(callerSkip).Should(Equal(4)) + }) + }) + + Context("the negative case", func() { + It("should poll the function and matcher", func() { + counter := 0 + arr := []int{} + a := New(AsyncAssertionTypeEventually, func() []int { + counter += 1 + if counter >= 10 { + arr = append(arr, 1) + } + return arr + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.ShouldNot(HaveLen(0)) + + Ω(arr).Should(HaveLen(1)) + Ω(failureMessage).Should(BeZero()) + }) + + It("should timeout when the matcher errors", func() { + a := New(AsyncAssertionTypeEventually, func() interface{} { + return 0 //this should cause the matcher to error + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.ShouldNot(HaveLen(0), "My description %d", 2) + + Ω(failureMessage).Should(ContainSubstring("Timed out after")) + Ω(failureMessage).Should(ContainSubstring("Error:")) + Ω(failureMessage).Should(ContainSubstring("My description 2")) + Ω(callerSkip).Should(Equal(4)) + }) + + It("should be able to timeout", func() { + a := New(AsyncAssertionTypeEventually, func() []int { + return []int{} + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.ShouldNot(HaveLen(0), "My description %d", 2) + + Ω(failureMessage).Should(ContainSubstring("Timed out after")) + Ω(failureMessage).Should(ContainSubstring("<[]int | len:0"), "Should pass the correct value to the matcher message formatter.") + Ω(failureMessage).Should(ContainSubstring("My description 2")) + Ω(callerSkip).Should(Equal(4)) + }) + }) + + Context("with a function that returns multiple values", func() { + It("should eventually succeed if the additional arguments are nil", func() { + i := 0 + Eventually(func() (int, error) { + i++ + return i, nil + }).Should(Equal(10)) + }) + + It("should eventually timeout if the additional arguments are not nil", func() { + i := 0 + a := New(AsyncAssertionTypeEventually, func() (int, error) { + i++ + return i, errors.New("bam") + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + a.Should(Equal(2)) + + Ω(failureMessage).Should(ContainSubstring("Timed out after")) + Ω(failureMessage).Should(ContainSubstring("Error:")) + Ω(failureMessage).Should(ContainSubstring("bam")) + Ω(callerSkip).Should(Equal(4)) + }) + }) + }) + + Describe("Consistently", func() { + Describe("The positive case", func() { + Context("when the matcher consistently passes for the duration", func() { + It("should pass", func() { + calls := 0 + a := New(AsyncAssertionTypeConsistently, func() string { + calls++ + return "foo" + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.Should(Equal("foo")) + Ω(calls).Should(Equal(10)) + Ω(failureMessage).Should(BeZero()) + }) + }) + + Context("when the matcher fails at some point", func() { + It("should fail", func() { + calls := 0 + a := New(AsyncAssertionTypeConsistently, func() interface{} { + calls++ + if calls > 9 { + return "bar" + } + return "foo" + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.Should(Equal("foo")) + Ω(failureMessage).Should(ContainSubstring("to equal")) + Ω(callerSkip).Should(Equal(4)) + }) + }) + + Context("when the matcher errors at some point", func() { + It("should fail", func() { + calls := 0 + a := New(AsyncAssertionTypeConsistently, func() interface{} { + calls++ + if calls > 5 { + return 3 + } + return []int{1, 2, 3} + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.Should(HaveLen(3)) + Ω(failureMessage).Should(ContainSubstring("HaveLen matcher expects")) + Ω(callerSkip).Should(Equal(4)) + }) + }) + }) + + Describe("The negative case", func() { + Context("when the matcher consistently passes for the duration", func() { + It("should pass", func() { + c := make(chan bool) + a := New(AsyncAssertionTypeConsistently, c, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.ShouldNot(Receive()) + Ω(failureMessage).Should(BeZero()) + }) + }) + + Context("when the matcher fails at some point", func() { + It("should fail", func() { + c := make(chan bool) + go func() { + time.Sleep(time.Duration(100 * time.Millisecond)) + c <- true + }() + + a := New(AsyncAssertionTypeConsistently, c, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.ShouldNot(Receive()) + Ω(failureMessage).Should(ContainSubstring("not to receive anything")) + }) + }) + + Context("when the matcher errors at some point", func() { + It("should fail", func() { + calls := 0 + a := New(AsyncAssertionTypeConsistently, func() interface{} { + calls++ + return calls + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + + a.ShouldNot(BeNumerically(">", 5)) + Ω(failureMessage).Should(ContainSubstring("not to be >")) + Ω(callerSkip).Should(Equal(4)) + }) + }) + }) + + Context("with a function that returns multiple values", func() { + It("should consistently succeed if the additional arguments are nil", func() { + i := 2 + Consistently(func() (int, error) { + i++ + return i, nil + }).Should(BeNumerically(">=", 2)) + }) + + It("should eventually timeout if the additional arguments are not nil", func() { + i := 2 + a := New(AsyncAssertionTypeEventually, func() (int, error) { + i++ + return i, errors.New("bam") + }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) + a.Should(BeNumerically(">=", 2)) + + Ω(failureMessage).Should(ContainSubstring("Error:")) + Ω(failureMessage).Should(ContainSubstring("bam")) + Ω(callerSkip).Should(Equal(4)) + }) + }) + }) + + Context("when passed a function with the wrong # or arguments & returns", func() { + It("should panic", func() { + Ω(func() { + New(AsyncAssertionTypeEventually, func() {}, fakeFailHandler, 0, 0, 1) + }).Should(Panic()) + + Ω(func() { + New(AsyncAssertionTypeEventually, func(a string) int { return 0 }, fakeFailHandler, 0, 0, 1) + }).Should(Panic()) + + Ω(func() { + New(AsyncAssertionTypeEventually, func() int { return 0 }, fakeFailHandler, 0, 0, 1) + }).ShouldNot(Panic()) + + Ω(func() { + New(AsyncAssertionTypeEventually, func() (int, error) { return 0, nil }, fakeFailHandler, 0, 0, 1) + }).ShouldNot(Panic()) + }) + }) + + Describe("bailing early", func() { + Context("when actual is a value", func() { + It("Eventually should bail out and fail early if the matcher says to", func() { + c := make(chan bool) + close(c) + + t := time.Now() + failures := InterceptGomegaFailures(func() { + Eventually(c, 0.1).Should(Receive()) + }) + Ω(time.Since(t)).Should(BeNumerically("<", 90*time.Millisecond)) + + Ω(failures).Should(HaveLen(1)) + }) + }) + + Context("when actual is a function", func() { + It("should never bail early", func() { + c := make(chan bool) + close(c) + + t := time.Now() + failures := InterceptGomegaFailures(func() { + Eventually(func() chan bool { + return c + }, 0.1).Should(Receive()) + }) + Ω(time.Since(t)).Should(BeNumerically(">=", 90*time.Millisecond)) + + Ω(failures).Should(HaveLen(1)) + }) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/internal/fakematcher/fake_matcher.go b/vendor/github.com/onsi/gomega/internal/fakematcher/fake_matcher.go new file mode 100644 index 0000000..6e351a7 --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/fakematcher/fake_matcher.go @@ -0,0 +1,23 @@ +package fakematcher + +import "fmt" + +type FakeMatcher struct { + ReceivedActual interface{} + MatchesToReturn bool + ErrToReturn error +} + +func (matcher *FakeMatcher) Match(actual interface{}) (bool, error) { + matcher.ReceivedActual = actual + + return matcher.MatchesToReturn, matcher.ErrToReturn +} + +func (matcher *FakeMatcher) FailureMessage(actual interface{}) string { + return fmt.Sprintf("positive: %v", actual) +} + +func (matcher *FakeMatcher) NegatedFailureMessage(actual interface{}) string { + return fmt.Sprintf("negative: %v", actual) +} diff --git a/vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go b/vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go new file mode 100644 index 0000000..7871fd4 --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go @@ -0,0 +1,40 @@ +package testingtsupport + +import ( + "regexp" + "runtime/debug" + "strings" + + "github.com/onsi/gomega/types" +) + +type gomegaTestingT interface { + Errorf(format string, args ...interface{}) +} + +func BuildTestingTGomegaFailHandler(t gomegaTestingT) types.GomegaFailHandler { + return func(message string, callerSkip ...int) { + skip := 1 + if len(callerSkip) > 0 { + skip = callerSkip[0] + } + stackTrace := pruneStack(string(debug.Stack()), skip) + t.Errorf("\n%s\n%s", stackTrace, message) + } +} + +func pruneStack(fullStackTrace string, skip int) string { + stack := strings.Split(fullStackTrace, "\n") + if len(stack) > 2*(skip+1) { + stack = stack[2*(skip+1):] + } + prunedStack := []string{} + re := regexp.MustCompile(`\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`) + for i := 0; i < len(stack)/2; i++ { + if !re.Match([]byte(stack[i*2])) { + prunedStack = append(prunedStack, stack[i*2]) + prunedStack = append(prunedStack, stack[i*2+1]) + } + } + return strings.Join(prunedStack, "\n") +} diff --git a/vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support_test.go b/vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support_test.go new file mode 100644 index 0000000..b9fbd6c --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support_test.go @@ -0,0 +1,12 @@ +package testingtsupport_test + +import ( + . "github.com/onsi/gomega" + + "testing" +) + +func TestTestingT(t *testing.T) { + RegisterTestingT(t) + Ω(true).Should(BeTrue()) +} diff --git a/vendor/github.com/onsi/gomega/matchers.go b/vendor/github.com/onsi/gomega/matchers.go new file mode 100644 index 0000000..7834ad1 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers.go @@ -0,0 +1,293 @@ +package gomega + +import ( + "time" + + "github.com/onsi/gomega/matchers" + "github.com/onsi/gomega/types" +) + +//Equal uses reflect.DeepEqual to compare actual with expected. Equal is strict about +//types when performing comparisons. +//It is an error for both actual and expected to be nil. Use BeNil() instead. +func Equal(expected interface{}) types.GomegaMatcher { + return &matchers.EqualMatcher{ + Expected: expected, + } +} + +//BeEquivalentTo is more lax than Equal, allowing equality between different types. +//This is done by converting actual to have the type of expected before +//attempting equality with reflect.DeepEqual. +//It is an error for actual and expected to be nil. Use BeNil() instead. +func BeEquivalentTo(expected interface{}) types.GomegaMatcher { + return &matchers.BeEquivalentToMatcher{ + Expected: expected, + } +} + +//BeNil succeeds if actual is nil +func BeNil() types.GomegaMatcher { + return &matchers.BeNilMatcher{} +} + +//BeTrue succeeds if actual is true +func BeTrue() types.GomegaMatcher { + return &matchers.BeTrueMatcher{} +} + +//BeFalse succeeds if actual is false +func BeFalse() types.GomegaMatcher { + return &matchers.BeFalseMatcher{} +} + +//HaveOccurred succeeds if actual is a non-nil error +//The typical Go error checking pattern looks like: +// err := SomethingThatMightFail() +// Ω(err).ShouldNot(HaveOccurred()) +func HaveOccurred() types.GomegaMatcher { + return &matchers.HaveOccurredMatcher{} +} + +//MatchError succeeds if actual is a non-nil error that matches the passed in string/error. +// +//These are valid use-cases: +// Ω(err).Should(MatchError("an error")) //asserts that err.Error() == "an error" +// Ω(err).Should(MatchError(SomeError)) //asserts that err == SomeError (via reflect.DeepEqual) +// +//It is an error for err to be nil or an object that does not implement the Error interface +func MatchError(expected interface{}) types.GomegaMatcher { + return &matchers.MatchErrorMatcher{ + Expected: expected, + } +} + +//BeClosed succeeds if actual is a closed channel. +//It is an error to pass a non-channel to BeClosed, it is also an error to pass nil +// +//In order to check whether or not the channel is closed, Gomega must try to read from the channel +//(even in the `ShouldNot(BeClosed())` case). You should keep this in mind if you wish to make subsequent assertions about +//values coming down the channel. +// +//Also, if you are testing that a *buffered* channel is closed you must first read all values out of the channel before +//asserting that it is closed (it is not possible to detect that a buffered-channel has been closed until all its buffered values are read). +// +//Finally, as a corollary: it is an error to check whether or not a send-only channel is closed. +func BeClosed() types.GomegaMatcher { + return &matchers.BeClosedMatcher{} +} + +//Receive succeeds if there is a value to be received on actual. +//Actual must be a channel (and cannot be a send-only channel) -- anything else is an error. +// +//Receive returns immediately and never blocks: +// +//- If there is nothing on the channel `c` then Ω(c).Should(Receive()) will fail and Ω(c).ShouldNot(Receive()) will pass. +// +//- If the channel `c` is closed then *both* Ω(c).Should(Receive()) and Ω(c).ShouldNot(Receive()) will error. +// +//- If there is something on the channel `c` ready to be read, then Ω(c).Should(Receive()) will pass and Ω(c).ShouldNot(Receive()) will fail. +// +//If you have a go-routine running in the background that will write to channel `c` you can: +// Eventually(c).Should(Receive()) +// +//This will timeout if nothing gets sent to `c` (you can modify the timeout interval as you normally do with `Eventually`) +// +//A similar use-case is to assert that no go-routine writes to a channel (for a period of time). You can do this with `Consistently`: +// Consistently(c).ShouldNot(Receive()) +// +//You can pass `Receive` a matcher. If you do so, it will match the received object against the matcher. For example: +// Ω(c).Should(Receive(Equal("foo"))) +// +//When given a matcher, `Receive` will always fail if there is nothing to be received on the channel. +// +//Passing Receive a matcher is especially useful when paired with Eventually: +// +// Eventually(c).Should(Receive(ContainSubstring("bar"))) +// +//will repeatedly attempt to pull values out of `c` until a value matching "bar" is received. +// +//Finally, if you want to have a reference to the value *sent* to the channel you can pass the `Receive` matcher a pointer to a variable of the appropriate type: +// var myThing thing +// Eventually(thingChan).Should(Receive(&myThing)) +// Ω(myThing.Sprocket).Should(Equal("foo")) +// Ω(myThing.IsValid()).Should(BeTrue()) +func Receive(args ...interface{}) types.GomegaMatcher { + var arg interface{} + if len(args) > 0 { + arg = args[0] + } + + return &matchers.ReceiveMatcher{ + Arg: arg, + } +} + +//BeSent succeeds if a value can be sent to actual. +//Actual must be a channel (and cannot be a receive-only channel) that can sent the type of the value passed into BeSent -- anything else is an error. +//In addition, actual must not be closed. +// +//BeSent never blocks: +// +//- If the channel `c` is not ready to receive then Ω(c).Should(BeSent("foo")) will fail immediately +//- If the channel `c` is eventually ready to receive then Eventually(c).Should(BeSent("foo")) will succeed.. presuming the channel becomes ready to receive before Eventually's timeout +//- If the channel `c` is closed then Ω(c).Should(BeSent("foo")) and Ω(c).ShouldNot(BeSent("foo")) will both fail immediately +// +//Of course, the value is actually sent to the channel. The point of `BeSent` is less to make an assertion about the availability of the channel (which is typically an implementation detail that your test should not be concerned with). +//Rather, the point of `BeSent` is to make it possible to easily and expressively write tests that can timeout on blocked channel sends. +func BeSent(arg interface{}) types.GomegaMatcher { + return &matchers.BeSentMatcher{ + Arg: arg, + } +} + +//MatchRegexp succeeds if actual is a string or stringer that matches the +//passed-in regexp. Optional arguments can be provided to construct a regexp +//via fmt.Sprintf(). +func MatchRegexp(regexp string, args ...interface{}) types.GomegaMatcher { + return &matchers.MatchRegexpMatcher{ + Regexp: regexp, + Args: args, + } +} + +//ContainSubstring succeeds if actual is a string or stringer that contains the +//passed-in regexp. Optional arguments can be provided to construct the substring +//via fmt.Sprintf(). +func ContainSubstring(substr string, args ...interface{}) types.GomegaMatcher { + return &matchers.ContainSubstringMatcher{ + Substr: substr, + Args: args, + } +} + +//MatchJSON succeeds if actual is a string or stringer of JSON that matches +//the expected JSON. The JSONs are decoded and the resulting objects are compared via +//reflect.DeepEqual so things like key-ordering and whitespace shouldn't matter. +func MatchJSON(json interface{}) types.GomegaMatcher { + return &matchers.MatchJSONMatcher{ + JSONToMatch: json, + } +} + +//BeEmpty succeeds if actual is empty. Actual must be of type string, array, map, chan, or slice. +func BeEmpty() types.GomegaMatcher { + return &matchers.BeEmptyMatcher{} +} + +//HaveLen succeeds if actual has the passed-in length. Actual must be of type string, array, map, chan, or slice. +func HaveLen(count int) types.GomegaMatcher { + return &matchers.HaveLenMatcher{ + Count: count, + } +} + +//BeZero succeeds if actual is the zero value for its type or if actual is nil. +func BeZero() types.GomegaMatcher { + return &matchers.BeZeroMatcher{} +} + +//ContainElement succeeds if actual contains the passed in element. +//By default ContainElement() uses Equal() to perform the match, however a +//matcher can be passed in instead: +// Ω([]string{"Foo", "FooBar"}).Should(ContainElement(ContainSubstring("Bar"))) +// +//Actual must be an array, slice or map. +//For maps, ContainElement searches through the map's values. +func ContainElement(element interface{}) types.GomegaMatcher { + return &matchers.ContainElementMatcher{ + Element: element, + } +} + +//ConsistOf succeeds if actual contains preciely the elements passed into the matcher. The ordering of the elements does not matter. +//By default ConsistOf() uses Equal() to match the elements, however custom matchers can be passed in instead. Here are some examples: +// +// Ω([]string{"Foo", "FooBar"}).Should(ConsistOf("FooBar", "Foo")) +// Ω([]string{"Foo", "FooBar"}).Should(ConsistOf(ContainSubstring("Bar"), "Foo")) +// Ω([]string{"Foo", "FooBar"}).Should(ConsistOf(ContainSubstring("Foo"), ContainSubstring("Foo"))) +// +//Actual must be an array, slice or map. For maps, ConsistOf matches against the map's values. +// +//You typically pass variadic arguments to ConsistOf (as in the examples above). However, if you need to pass in a slice you can provided that it +//is the only element passed in to ConsistOf: +// +// Ω([]string{"Foo", "FooBar"}).Should(ConsistOf([]string{"FooBar", "Foo"})) +// +//Note that Go's type system does not allow you to write this as ConsistOf([]string{"FooBar", "Foo"}...) as []string and []interface{} are different types - hence the need for this special rule. + +func ConsistOf(elements ...interface{}) types.GomegaMatcher { + return &matchers.ConsistOfMatcher{ + Elements: elements, + } +} + +//HaveKey succeeds if actual is a map with the passed in key. +//By default HaveKey uses Equal() to perform the match, however a +//matcher can be passed in instead: +// Ω(map[string]string{"Foo": "Bar", "BazFoo": "Duck"}).Should(HaveKey(MatchRegexp(`.+Foo$`))) +func HaveKey(key interface{}) types.GomegaMatcher { + return &matchers.HaveKeyMatcher{ + Key: key, + } +} + +//HaveKeyWithValue succeeds if actual is a map with the passed in key and value. +//By default HaveKeyWithValue uses Equal() to perform the match, however a +//matcher can be passed in instead: +// Ω(map[string]string{"Foo": "Bar", "BazFoo": "Duck"}).Should(HaveKeyWithValue("Foo", "Bar")) +// Ω(map[string]string{"Foo": "Bar", "BazFoo": "Duck"}).Should(HaveKeyWithValue(MatchRegexp(`.+Foo$`), "Bar")) +func HaveKeyWithValue(key interface{}, value interface{}) types.GomegaMatcher { + return &matchers.HaveKeyWithValueMatcher{ + Key: key, + Value: value, + } +} + +//BeNumerically performs numerical assertions in a type-agnostic way. +//Actual and expected should be numbers, though the specific type of +//number is irrelevant (floa32, float64, uint8, etc...). +// +//There are six, self-explanatory, supported comparators: +// Ω(1.0).Should(BeNumerically("==", 1)) +// Ω(1.0).Should(BeNumerically("~", 0.999, 0.01)) +// Ω(1.0).Should(BeNumerically(">", 0.9)) +// Ω(1.0).Should(BeNumerically(">=", 1.0)) +// Ω(1.0).Should(BeNumerically("<", 3)) +// Ω(1.0).Should(BeNumerically("<=", 1.0)) +func BeNumerically(comparator string, compareTo ...interface{}) types.GomegaMatcher { + return &matchers.BeNumericallyMatcher{ + Comparator: comparator, + CompareTo: compareTo, + } +} + +//BeTemporally compares time.Time's like BeNumerically +//Actual and expected must be time.Time. The comparators are the same as for BeNumerically +// Ω(time.Now()).Should(BeTemporally(">", time.Time{})) +// Ω(time.Now()).Should(BeTemporally("~", time.Now(), time.Second)) +func BeTemporally(comparator string, compareTo time.Time, threshold ...time.Duration) types.GomegaMatcher { + return &matchers.BeTemporallyMatcher{ + Comparator: comparator, + CompareTo: compareTo, + Threshold: threshold, + } +} + +//BeAssignableToTypeOf succeeds if actual is assignable to the type of expected. +//It will return an error when one of the values is nil. +// Ω(0).Should(BeAssignableToTypeOf(0)) // Same values +// Ω(5).Should(BeAssignableToTypeOf(-1)) // different values same type +// Ω("foo").Should(BeAssignableToTypeOf("bar")) // different values same type +// Ω(struct{ Foo string }{}).Should(BeAssignableToTypeOf(struct{ Foo string }{})) +func BeAssignableToTypeOf(expected interface{}) types.GomegaMatcher { + return &matchers.AssignableToTypeOfMatcher{ + Expected: expected, + } +} + +//Panic succeeds if actual is a function that, when invoked, panics. +//Actual must be a function that takes no arguments and returns no results. +func Panic() types.GomegaMatcher { + return &matchers.PanicMatcher{} +} diff --git a/vendor/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher.go b/vendor/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher.go new file mode 100644 index 0000000..7f8897b --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher.go @@ -0,0 +1,30 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "reflect" +) + +type AssignableToTypeOfMatcher struct { + Expected interface{} +} + +func (matcher *AssignableToTypeOfMatcher) Match(actual interface{}) (success bool, err error) { + if actual == nil || matcher.Expected == nil { + return false, fmt.Errorf("Refusing to compare to .") + } + + actualType := reflect.TypeOf(actual) + expectedType := reflect.TypeOf(matcher.Expected) + + return actualType.AssignableTo(expectedType), nil +} + +func (matcher *AssignableToTypeOfMatcher) FailureMessage(actual interface{}) string { + return format.Message(actual, fmt.Sprintf("to be assignable to the type: %T", matcher.Expected)) +} + +func (matcher *AssignableToTypeOfMatcher) NegatedFailureMessage(actual interface{}) string { + return format.Message(actual, fmt.Sprintf("not to be assignable to the type: %T", matcher.Expected)) +} diff --git a/vendor/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher_test.go new file mode 100644 index 0000000..d2280e0 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher_test.go @@ -0,0 +1,30 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("AssignableToTypeOf", func() { + Context("When asserting assignability between types", func() { + It("should do the right thing", func() { + Ω(0).Should(BeAssignableToTypeOf(0)) + Ω(5).Should(BeAssignableToTypeOf(-1)) + Ω("foo").Should(BeAssignableToTypeOf("bar")) + Ω(struct{ Foo string }{}).Should(BeAssignableToTypeOf(struct{ Foo string }{})) + + Ω(0).ShouldNot(BeAssignableToTypeOf("bar")) + Ω(5).ShouldNot(BeAssignableToTypeOf(struct{ Foo string }{})) + Ω("foo").ShouldNot(BeAssignableToTypeOf(42)) + }) + }) + + Context("When asserting nil values", func() { + It("should error", func() { + success, err := (&AssignableToTypeOfMatcher{Expected: nil}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/be_closed_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_closed_matcher.go new file mode 100644 index 0000000..c1b4995 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_closed_matcher.go @@ -0,0 +1,45 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "reflect" +) + +type BeClosedMatcher struct { +} + +func (matcher *BeClosedMatcher) Match(actual interface{}) (success bool, err error) { + if !isChan(actual) { + return false, fmt.Errorf("BeClosed matcher expects a channel. Got:\n%s", format.Object(actual, 1)) + } + + channelType := reflect.TypeOf(actual) + channelValue := reflect.ValueOf(actual) + + if channelType.ChanDir() == reflect.SendDir { + return false, fmt.Errorf("BeClosed matcher cannot determine if a send-only channel is closed or open. Got:\n%s", format.Object(actual, 1)) + } + + winnerIndex, _, open := reflect.Select([]reflect.SelectCase{ + reflect.SelectCase{Dir: reflect.SelectRecv, Chan: channelValue}, + reflect.SelectCase{Dir: reflect.SelectDefault}, + }) + + var closed bool + if winnerIndex == 0 { + closed = !open + } else if winnerIndex == 1 { + closed = false + } + + return closed, nil +} + +func (matcher *BeClosedMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to be closed") +} + +func (matcher *BeClosedMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to be open") +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_closed_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/be_closed_matcher_test.go new file mode 100644 index 0000000..b2c40c9 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_closed_matcher_test.go @@ -0,0 +1,70 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("BeClosedMatcher", func() { + Context("when passed a channel", func() { + It("should do the right thing", func() { + openChannel := make(chan bool) + Ω(openChannel).ShouldNot(BeClosed()) + + var openReaderChannel <-chan bool + openReaderChannel = openChannel + Ω(openReaderChannel).ShouldNot(BeClosed()) + + closedChannel := make(chan bool) + close(closedChannel) + + Ω(closedChannel).Should(BeClosed()) + + var closedReaderChannel <-chan bool + closedReaderChannel = closedChannel + Ω(closedReaderChannel).Should(BeClosed()) + }) + }) + + Context("when passed a send-only channel", func() { + It("should error", func() { + openChannel := make(chan bool) + var openWriterChannel chan<- bool + openWriterChannel = openChannel + + success, err := (&BeClosedMatcher{}).Match(openWriterChannel) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + closedChannel := make(chan bool) + close(closedChannel) + + var closedWriterChannel chan<- bool + closedWriterChannel = closedChannel + + success, err = (&BeClosedMatcher{}).Match(closedWriterChannel) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + }) + }) + + Context("when passed something else", func() { + It("should error", func() { + var nilChannel chan bool + + success, err := (&BeClosedMatcher{}).Match(nilChannel) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&BeClosedMatcher{}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&BeClosedMatcher{}).Match(7) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/be_empty_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_empty_matcher.go new file mode 100644 index 0000000..55bdd7d --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_empty_matcher.go @@ -0,0 +1,26 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" +) + +type BeEmptyMatcher struct { +} + +func (matcher *BeEmptyMatcher) Match(actual interface{}) (success bool, err error) { + length, ok := lengthOf(actual) + if !ok { + return false, fmt.Errorf("BeEmpty matcher expects a string/array/map/channel/slice. Got:\n%s", format.Object(actual, 1)) + } + + return length == 0, nil +} + +func (matcher *BeEmptyMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to be empty") +} + +func (matcher *BeEmptyMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to be empty") +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_empty_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/be_empty_matcher_test.go new file mode 100644 index 0000000..541c1b9 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_empty_matcher_test.go @@ -0,0 +1,52 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("BeEmpty", func() { + Context("when passed a supported type", func() { + It("should do the right thing", func() { + Ω("").Should(BeEmpty()) + Ω(" ").ShouldNot(BeEmpty()) + + Ω([0]int{}).Should(BeEmpty()) + Ω([1]int{1}).ShouldNot(BeEmpty()) + + Ω([]int{}).Should(BeEmpty()) + Ω([]int{1}).ShouldNot(BeEmpty()) + + Ω(map[string]int{}).Should(BeEmpty()) + Ω(map[string]int{"a": 1}).ShouldNot(BeEmpty()) + + c := make(chan bool, 1) + Ω(c).Should(BeEmpty()) + c <- true + Ω(c).ShouldNot(BeEmpty()) + }) + }) + + Context("when passed a correctly typed nil", func() { + It("should be true", func() { + var nilSlice []int + Ω(nilSlice).Should(BeEmpty()) + + var nilMap map[int]string + Ω(nilMap).Should(BeEmpty()) + }) + }) + + Context("when passed an unsupported type", func() { + It("should error", func() { + success, err := (&BeEmptyMatcher{}).Match(0) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&BeEmptyMatcher{}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/be_equivalent_to_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_equivalent_to_matcher.go new file mode 100644 index 0000000..32a0c31 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_equivalent_to_matcher.go @@ -0,0 +1,33 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "reflect" +) + +type BeEquivalentToMatcher struct { + Expected interface{} +} + +func (matcher *BeEquivalentToMatcher) Match(actual interface{}) (success bool, err error) { + if actual == nil && matcher.Expected == nil { + return false, fmt.Errorf("Both actual and expected must not be nil.") + } + + convertedActual := actual + + if actual != nil && matcher.Expected != nil && reflect.TypeOf(actual).ConvertibleTo(reflect.TypeOf(matcher.Expected)) { + convertedActual = reflect.ValueOf(actual).Convert(reflect.TypeOf(matcher.Expected)).Interface() + } + + return reflect.DeepEqual(convertedActual, matcher.Expected), nil +} + +func (matcher *BeEquivalentToMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to be equivalent to", matcher.Expected) +} + +func (matcher *BeEquivalentToMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to be equivalent to", matcher.Expected) +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_equivalent_to_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/be_equivalent_to_matcher_test.go new file mode 100644 index 0000000..def5104 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_equivalent_to_matcher_test.go @@ -0,0 +1,50 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("BeEquivalentTo", func() { + Context("when asserting that nil is equivalent to nil", func() { + It("should error", func() { + success, err := (&BeEquivalentToMatcher{Expected: nil}).Match(nil) + + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("When asserting on nil", func() { + It("should do the right thing", func() { + Ω("foo").ShouldNot(BeEquivalentTo(nil)) + Ω(nil).ShouldNot(BeEquivalentTo(3)) + Ω([]int{1, 2}).ShouldNot(BeEquivalentTo(nil)) + }) + }) + + Context("When asserting on type aliases", func() { + It("should the right thing", func() { + Ω(StringAlias("foo")).Should(BeEquivalentTo("foo")) + Ω("foo").Should(BeEquivalentTo(StringAlias("foo"))) + Ω(StringAlias("foo")).ShouldNot(BeEquivalentTo("bar")) + Ω("foo").ShouldNot(BeEquivalentTo(StringAlias("bar"))) + }) + }) + + Context("When asserting on numbers", func() { + It("should convert actual to expected and do the right thing", func() { + Ω(5).Should(BeEquivalentTo(5)) + Ω(5.0).Should(BeEquivalentTo(5.0)) + Ω(5).Should(BeEquivalentTo(5.0)) + + Ω(5).ShouldNot(BeEquivalentTo("5")) + Ω(5).ShouldNot(BeEquivalentTo(3)) + + //Here be dragons! + Ω(5.1).Should(BeEquivalentTo(5)) + Ω(5).ShouldNot(BeEquivalentTo(5.1)) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/be_false_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_false_matcher.go new file mode 100644 index 0000000..0b224cb --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_false_matcher.go @@ -0,0 +1,25 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" +) + +type BeFalseMatcher struct { +} + +func (matcher *BeFalseMatcher) Match(actual interface{}) (success bool, err error) { + if !isBool(actual) { + return false, fmt.Errorf("Expected a boolean. Got:\n%s", format.Object(actual, 1)) + } + + return actual == false, nil +} + +func (matcher *BeFalseMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to be false") +} + +func (matcher *BeFalseMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to be false") +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_false_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/be_false_matcher_test.go new file mode 100644 index 0000000..3965a2c --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_false_matcher_test.go @@ -0,0 +1,20 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("BeFalse", func() { + It("should handle true and false correctly", func() { + Ω(true).ShouldNot(BeFalse()) + Ω(false).Should(BeFalse()) + }) + + It("should only support booleans", func() { + success, err := (&BeFalseMatcher{}).Match("foo") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/be_nil_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_nil_matcher.go new file mode 100644 index 0000000..7ee84fe --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_nil_matcher.go @@ -0,0 +1,18 @@ +package matchers + +import "github.com/onsi/gomega/format" + +type BeNilMatcher struct { +} + +func (matcher *BeNilMatcher) Match(actual interface{}) (success bool, err error) { + return isNil(actual), nil +} + +func (matcher *BeNilMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to be nil") +} + +func (matcher *BeNilMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to be nil") +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_nil_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/be_nil_matcher_test.go new file mode 100644 index 0000000..7533253 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_nil_matcher_test.go @@ -0,0 +1,28 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("BeNil", func() { + It("should succeed when passed nil", func() { + Ω(nil).Should(BeNil()) + }) + + It("should succeed when passed a typed nil", func() { + var a []int + Ω(a).Should(BeNil()) + }) + + It("should succeed when passing nil pointer", func() { + var f *struct{} + Ω(f).Should(BeNil()) + }) + + It("should not succeed when not passed nil", func() { + Ω(0).ShouldNot(BeNil()) + Ω(false).ShouldNot(BeNil()) + Ω("").ShouldNot(BeNil()) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/be_numerically_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_numerically_matcher.go new file mode 100644 index 0000000..52f83fe --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_numerically_matcher.go @@ -0,0 +1,119 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "math" +) + +type BeNumericallyMatcher struct { + Comparator string + CompareTo []interface{} +} + +func (matcher *BeNumericallyMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, fmt.Sprintf("to be %s", matcher.Comparator), matcher.CompareTo[0]) +} + +func (matcher *BeNumericallyMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, fmt.Sprintf("not to be %s", matcher.Comparator), matcher.CompareTo[0]) +} + +func (matcher *BeNumericallyMatcher) Match(actual interface{}) (success bool, err error) { + if len(matcher.CompareTo) == 0 || len(matcher.CompareTo) > 2 { + return false, fmt.Errorf("BeNumerically requires 1 or 2 CompareTo arguments. Got:\n%s", format.Object(matcher.CompareTo, 1)) + } + if !isNumber(actual) { + return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(actual, 1)) + } + if !isNumber(matcher.CompareTo[0]) { + return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[0], 1)) + } + if len(matcher.CompareTo) == 2 && !isNumber(matcher.CompareTo[1]) { + return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[0], 1)) + } + + switch matcher.Comparator { + case "==", "~", ">", ">=", "<", "<=": + default: + return false, fmt.Errorf("Unknown comparator: %s", matcher.Comparator) + } + + if isFloat(actual) || isFloat(matcher.CompareTo[0]) { + var secondOperand float64 = 1e-8 + if len(matcher.CompareTo) == 2 { + secondOperand = toFloat(matcher.CompareTo[1]) + } + success = matcher.matchFloats(toFloat(actual), toFloat(matcher.CompareTo[0]), secondOperand) + } else if isInteger(actual) { + var secondOperand int64 = 0 + if len(matcher.CompareTo) == 2 { + secondOperand = toInteger(matcher.CompareTo[1]) + } + success = matcher.matchIntegers(toInteger(actual), toInteger(matcher.CompareTo[0]), secondOperand) + } else if isUnsignedInteger(actual) { + var secondOperand uint64 = 0 + if len(matcher.CompareTo) == 2 { + secondOperand = toUnsignedInteger(matcher.CompareTo[1]) + } + success = matcher.matchUnsignedIntegers(toUnsignedInteger(actual), toUnsignedInteger(matcher.CompareTo[0]), secondOperand) + } else { + return false, fmt.Errorf("Failed to compare:\n%s\n%s:\n%s", format.Object(actual, 1), matcher.Comparator, format.Object(matcher.CompareTo[0], 1)) + } + + return success, nil +} + +func (matcher *BeNumericallyMatcher) matchIntegers(actual, compareTo, threshold int64) (success bool) { + switch matcher.Comparator { + case "==", "~": + diff := actual - compareTo + return -threshold <= diff && diff <= threshold + case ">": + return (actual > compareTo) + case ">=": + return (actual >= compareTo) + case "<": + return (actual < compareTo) + case "<=": + return (actual <= compareTo) + } + return false +} + +func (matcher *BeNumericallyMatcher) matchUnsignedIntegers(actual, compareTo, threshold uint64) (success bool) { + switch matcher.Comparator { + case "==", "~": + if actual < compareTo { + actual, compareTo = compareTo, actual + } + return actual-compareTo <= threshold + case ">": + return (actual > compareTo) + case ">=": + return (actual >= compareTo) + case "<": + return (actual < compareTo) + case "<=": + return (actual <= compareTo) + } + return false +} + +func (matcher *BeNumericallyMatcher) matchFloats(actual, compareTo, threshold float64) (success bool) { + switch matcher.Comparator { + case "~": + return math.Abs(actual-compareTo) <= threshold + case "==": + return (actual == compareTo) + case ">": + return (actual > compareTo) + case ">=": + return (actual >= compareTo) + case "<": + return (actual < compareTo) + case "<=": + return (actual <= compareTo) + } + return false +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_numerically_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/be_numerically_matcher_test.go new file mode 100644 index 0000000..43fdb1f --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_numerically_matcher_test.go @@ -0,0 +1,148 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("BeNumerically", func() { + Context("when passed a number", func() { + It("should support ==", func() { + Ω(uint32(5)).Should(BeNumerically("==", 5)) + Ω(float64(5.0)).Should(BeNumerically("==", 5)) + Ω(int8(5)).Should(BeNumerically("==", 5)) + }) + + It("should not have false positives", func() { + Ω(5.1).ShouldNot(BeNumerically("==", 5)) + Ω(5).ShouldNot(BeNumerically("==", 5.1)) + }) + + It("should support >", func() { + Ω(uint32(5)).Should(BeNumerically(">", 4)) + Ω(float64(5.0)).Should(BeNumerically(">", 4.9)) + Ω(int8(5)).Should(BeNumerically(">", 4)) + + Ω(uint32(5)).ShouldNot(BeNumerically(">", 5)) + Ω(float64(5.0)).ShouldNot(BeNumerically(">", 5.0)) + Ω(int8(5)).ShouldNot(BeNumerically(">", 5)) + }) + + It("should support <", func() { + Ω(uint32(5)).Should(BeNumerically("<", 6)) + Ω(float64(5.0)).Should(BeNumerically("<", 5.1)) + Ω(int8(5)).Should(BeNumerically("<", 6)) + + Ω(uint32(5)).ShouldNot(BeNumerically("<", 5)) + Ω(float64(5.0)).ShouldNot(BeNumerically("<", 5.0)) + Ω(int8(5)).ShouldNot(BeNumerically("<", 5)) + }) + + It("should support >=", func() { + Ω(uint32(5)).Should(BeNumerically(">=", 4)) + Ω(float64(5.0)).Should(BeNumerically(">=", 4.9)) + Ω(int8(5)).Should(BeNumerically(">=", 4)) + + Ω(uint32(5)).Should(BeNumerically(">=", 5)) + Ω(float64(5.0)).Should(BeNumerically(">=", 5.0)) + Ω(int8(5)).Should(BeNumerically(">=", 5)) + + Ω(uint32(5)).ShouldNot(BeNumerically(">=", 6)) + Ω(float64(5.0)).ShouldNot(BeNumerically(">=", 5.1)) + Ω(int8(5)).ShouldNot(BeNumerically(">=", 6)) + }) + + It("should support <=", func() { + Ω(uint32(5)).Should(BeNumerically("<=", 6)) + Ω(float64(5.0)).Should(BeNumerically("<=", 5.1)) + Ω(int8(5)).Should(BeNumerically("<=", 6)) + + Ω(uint32(5)).Should(BeNumerically("<=", 5)) + Ω(float64(5.0)).Should(BeNumerically("<=", 5.0)) + Ω(int8(5)).Should(BeNumerically("<=", 5)) + + Ω(uint32(5)).ShouldNot(BeNumerically("<=", 4)) + Ω(float64(5.0)).ShouldNot(BeNumerically("<=", 4.9)) + Ω(int8(5)).Should(BeNumerically("<=", 5)) + }) + + Context("when passed ~", func() { + Context("when passed a float", func() { + Context("and there is no precision parameter", func() { + It("should default to 1e-8", func() { + Ω(5.00000001).Should(BeNumerically("~", 5.00000002)) + Ω(5.00000001).ShouldNot(BeNumerically("~", 5.0000001)) + }) + }) + + Context("and there is a precision parameter", func() { + It("should use the precision parameter", func() { + Ω(5.1).Should(BeNumerically("~", 5.19, 0.1)) + Ω(5.1).Should(BeNumerically("~", 5.01, 0.1)) + Ω(5.1).ShouldNot(BeNumerically("~", 5.22, 0.1)) + Ω(5.1).ShouldNot(BeNumerically("~", 4.98, 0.1)) + }) + }) + }) + + Context("when passed an int/uint", func() { + Context("and there is no precision parameter", func() { + It("should just do strict equality", func() { + Ω(5).Should(BeNumerically("~", 5)) + Ω(5).ShouldNot(BeNumerically("~", 6)) + Ω(uint(5)).ShouldNot(BeNumerically("~", 6)) + }) + }) + + Context("and there is a precision parameter", func() { + It("should use precision paramter", func() { + Ω(5).Should(BeNumerically("~", 6, 2)) + Ω(5).ShouldNot(BeNumerically("~", 8, 2)) + Ω(uint(5)).Should(BeNumerically("~", 6, 1)) + }) + }) + }) + }) + }) + + Context("when passed a non-number", func() { + It("should error", func() { + success, err := (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{5}}).Match("foo") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&BeNumericallyMatcher{Comparator: "=="}).Match(5) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&BeNumericallyMatcher{Comparator: "~", CompareTo: []interface{}{3.0, "foo"}}).Match(5.0) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{"bar"}}).Match(5) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{"bar"}}).Match("foo") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{nil}}).Match(0) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&BeNumericallyMatcher{Comparator: "==", CompareTo: []interface{}{0}}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when passed an unsupported comparator", func() { + It("should error", func() { + success, err := (&BeNumericallyMatcher{Comparator: "!=", CompareTo: []interface{}{5}}).Match(4) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/be_sent_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_sent_matcher.go new file mode 100644 index 0000000..d7c3223 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_sent_matcher.go @@ -0,0 +1,71 @@ +package matchers + +import ( + "fmt" + "reflect" + + "github.com/onsi/gomega/format" +) + +type BeSentMatcher struct { + Arg interface{} + channelClosed bool +} + +func (matcher *BeSentMatcher) Match(actual interface{}) (success bool, err error) { + if !isChan(actual) { + return false, fmt.Errorf("BeSent expects a channel. Got:\n%s", format.Object(actual, 1)) + } + + channelType := reflect.TypeOf(actual) + channelValue := reflect.ValueOf(actual) + + if channelType.ChanDir() == reflect.RecvDir { + return false, fmt.Errorf("BeSent matcher cannot be passed a receive-only channel. Got:\n%s", format.Object(actual, 1)) + } + + argType := reflect.TypeOf(matcher.Arg) + assignable := argType.AssignableTo(channelType.Elem()) + + if !assignable { + return false, fmt.Errorf("Cannot pass:\n%s to the channel:\n%s\nThe types don't match.", format.Object(matcher.Arg, 1), format.Object(actual, 1)) + } + + argValue := reflect.ValueOf(matcher.Arg) + + defer func() { + if e := recover(); e != nil { + success = false + err = fmt.Errorf("Cannot send to a closed channel") + matcher.channelClosed = true + } + }() + + winnerIndex, _, _ := reflect.Select([]reflect.SelectCase{ + reflect.SelectCase{Dir: reflect.SelectSend, Chan: channelValue, Send: argValue}, + reflect.SelectCase{Dir: reflect.SelectDefault}, + }) + + var didSend bool + if winnerIndex == 0 { + didSend = true + } + + return didSend, nil +} + +func (matcher *BeSentMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to send:", matcher.Arg) +} + +func (matcher *BeSentMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to send:", matcher.Arg) +} + +func (matcher *BeSentMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { + if !isChan(actual) { + return false + } + + return !matcher.channelClosed +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_sent_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/be_sent_matcher_test.go new file mode 100644 index 0000000..381c2b4 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_sent_matcher_test.go @@ -0,0 +1,106 @@ +package matchers_test + +import ( + "time" + . "github.com/onsi/gomega/matchers" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("BeSent", func() { + Context("when passed a channel and a matching type", func() { + Context("when the channel is ready to receive", func() { + It("should succeed and send the value down the channel", func() { + c := make(chan string) + d := make(chan string) + go func() { + val := <-c + d <- val + }() + + time.Sleep(10 * time.Millisecond) + + Ω(c).Should(BeSent("foo")) + Eventually(d).Should(Receive(Equal("foo"))) + }) + + It("should succeed (with a buffered channel)", func() { + c := make(chan string, 1) + Ω(c).Should(BeSent("foo")) + Ω(<-c).Should(Equal("foo")) + }) + }) + + Context("when the channel is not ready to receive", func() { + It("should fail and not send down the channel", func() { + c := make(chan string) + Ω(c).ShouldNot(BeSent("foo")) + Consistently(c).ShouldNot(Receive()) + }) + }) + + Context("when the channel is eventually ready to receive", func() { + It("should succeed", func() { + c := make(chan string) + d := make(chan string) + go func() { + time.Sleep(30 * time.Millisecond) + val := <-c + d <- val + }() + + Eventually(c).Should(BeSent("foo")) + Eventually(d).Should(Receive(Equal("foo"))) + }) + }) + + Context("when the channel is closed", func() { + It("should error", func() { + c := make(chan string) + close(c) + success, err := (&BeSentMatcher{Arg: "foo"}).Match(c) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + + It("should short-circuit Eventually", func() { + c := make(chan string) + close(c) + + t := time.Now() + failures := InterceptGomegaFailures(func() { + Eventually(c, 10.0).Should(BeSent("foo")) + }) + Ω(failures).Should(HaveLen(1)) + Ω(time.Since(t)).Should(BeNumerically("<", time.Second)) + }) + }) + }) + + Context("when passed a channel and a non-matching type", func() { + It("should error", func() { + success, err := (&BeSentMatcher{Arg: "foo"}).Match(make(chan int, 1)) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when passed a receive-only channel", func() { + It("should error", func() { + var c <-chan string + c = make(chan string, 1) + success, err := (&BeSentMatcher{Arg: "foo"}).Match(c) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when passed a nonchannel", func() { + It("should error", func() { + success, err := (&BeSentMatcher{Arg: "foo"}).Match("bar") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/be_temporally_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_temporally_matcher.go new file mode 100644 index 0000000..abda4eb --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_temporally_matcher.go @@ -0,0 +1,65 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "time" +) + +type BeTemporallyMatcher struct { + Comparator string + CompareTo time.Time + Threshold []time.Duration +} + +func (matcher *BeTemporallyMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, fmt.Sprintf("to be %s", matcher.Comparator), matcher.CompareTo) +} + +func (matcher *BeTemporallyMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, fmt.Sprintf("not to be %s", matcher.Comparator), matcher.CompareTo) +} + +func (matcher *BeTemporallyMatcher) Match(actual interface{}) (bool, error) { + // predicate to test for time.Time type + isTime := func(t interface{}) bool { + _, ok := t.(time.Time) + return ok + } + + if !isTime(actual) { + return false, fmt.Errorf("Expected a time.Time. Got:\n%s", format.Object(actual, 1)) + } + + switch matcher.Comparator { + case "==", "~", ">", ">=", "<", "<=": + default: + return false, fmt.Errorf("Unknown comparator: %s", matcher.Comparator) + } + + var threshold = time.Millisecond + if len(matcher.Threshold) == 1 { + threshold = matcher.Threshold[0] + } + + return matcher.matchTimes(actual.(time.Time), matcher.CompareTo, threshold), nil +} + +func (matcher *BeTemporallyMatcher) matchTimes(actual, compareTo time.Time, threshold time.Duration) (success bool) { + switch matcher.Comparator { + case "==": + return actual.Equal(compareTo) + case "~": + diff := actual.Sub(compareTo) + return -threshold <= diff && diff <= threshold + case ">": + return actual.After(compareTo) + case ">=": + return !actual.Before(compareTo) + case "<": + return actual.Before(compareTo) + case "<=": + return !actual.After(compareTo) + } + return false +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_temporally_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/be_temporally_matcher_test.go new file mode 100644 index 0000000..feb33e5 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_temporally_matcher_test.go @@ -0,0 +1,98 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" + "time" +) + +var _ = Describe("BeTemporally", func() { + + var t0, t1, t2 time.Time + BeforeEach(func() { + t0 = time.Now() + t1 = t0.Add(time.Second) + t2 = t0.Add(-time.Second) + }) + + Context("When comparing times", func() { + + It("should support ==", func() { + Ω(t0).Should(BeTemporally("==", t0)) + Ω(t1).ShouldNot(BeTemporally("==", t0)) + Ω(t0).ShouldNot(BeTemporally("==", t1)) + Ω(t0).ShouldNot(BeTemporally("==", time.Time{})) + }) + + It("should support >", func() { + Ω(t0).Should(BeTemporally(">", t2)) + Ω(t0).ShouldNot(BeTemporally(">", t0)) + Ω(t2).ShouldNot(BeTemporally(">", t0)) + }) + + It("should support <", func() { + Ω(t0).Should(BeTemporally("<", t1)) + Ω(t0).ShouldNot(BeTemporally("<", t0)) + Ω(t1).ShouldNot(BeTemporally("<", t0)) + }) + + It("should support >=", func() { + Ω(t0).Should(BeTemporally(">=", t2)) + Ω(t0).Should(BeTemporally(">=", t0)) + Ω(t0).ShouldNot(BeTemporally(">=", t1)) + }) + + It("should support <=", func() { + Ω(t0).Should(BeTemporally("<=", t1)) + Ω(t0).Should(BeTemporally("<=", t0)) + Ω(t0).ShouldNot(BeTemporally("<=", t2)) + }) + + Context("when passed ~", func() { + Context("and there is no precision parameter", func() { + BeforeEach(func() { + t1 = t0.Add(time.Millisecond / 2) + t2 = t0.Add(-2 * time.Millisecond) + }) + It("should approximate", func() { + Ω(t0).Should(BeTemporally("~", t0)) + Ω(t0).Should(BeTemporally("~", t1)) + Ω(t0).ShouldNot(BeTemporally("~", t2)) + }) + }) + + Context("and there is a precision parameter", func() { + BeforeEach(func() { + t2 = t0.Add(3 * time.Second) + }) + It("should use precision paramter", func() { + d := 2 * time.Second + Ω(t0).Should(BeTemporally("~", t0, d)) + Ω(t0).Should(BeTemporally("~", t1, d)) + Ω(t0).ShouldNot(BeTemporally("~", t2, d)) + }) + }) + }) + }) + + Context("when passed a non-time", func() { + It("should error", func() { + success, err := (&BeTemporallyMatcher{Comparator: "==", CompareTo: t0}).Match("foo") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&BeTemporallyMatcher{Comparator: "=="}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when passed an unsupported comparator", func() { + It("should error", func() { + success, err := (&BeTemporallyMatcher{Comparator: "!=", CompareTo: t0}).Match(t2) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/be_true_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_true_matcher.go new file mode 100644 index 0000000..1275e5f --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_true_matcher.go @@ -0,0 +1,25 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" +) + +type BeTrueMatcher struct { +} + +func (matcher *BeTrueMatcher) Match(actual interface{}) (success bool, err error) { + if !isBool(actual) { + return false, fmt.Errorf("Expected a boolean. Got:\n%s", format.Object(actual, 1)) + } + + return actual.(bool), nil +} + +func (matcher *BeTrueMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to be true") +} + +func (matcher *BeTrueMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to be true") +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_true_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/be_true_matcher_test.go new file mode 100644 index 0000000..ca32e56 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_true_matcher_test.go @@ -0,0 +1,20 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("BeTrue", func() { + It("should handle true and false correctly", func() { + Ω(true).Should(BeTrue()) + Ω(false).ShouldNot(BeTrue()) + }) + + It("should only support booleans", func() { + success, err := (&BeTrueMatcher{}).Match("foo") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/be_zero_matcher.go b/vendor/github.com/onsi/gomega/matchers/be_zero_matcher.go new file mode 100644 index 0000000..b39c914 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_zero_matcher.go @@ -0,0 +1,27 @@ +package matchers + +import ( + "github.com/onsi/gomega/format" + "reflect" +) + +type BeZeroMatcher struct { +} + +func (matcher *BeZeroMatcher) Match(actual interface{}) (success bool, err error) { + if actual == nil { + return true, nil + } + zeroValue := reflect.Zero(reflect.TypeOf(actual)).Interface() + + return reflect.DeepEqual(zeroValue, actual), nil + +} + +func (matcher *BeZeroMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to be zero-valued") +} + +func (matcher *BeZeroMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to be zero-valued") +} diff --git a/vendor/github.com/onsi/gomega/matchers/be_zero_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/be_zero_matcher_test.go new file mode 100644 index 0000000..8ec3643 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/be_zero_matcher_test.go @@ -0,0 +1,30 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("BeZero", func() { + It("should succeed if the passed in object is the zero value for its type", func() { + Ω(nil).Should(BeZero()) + + Ω("").Should(BeZero()) + Ω(" ").ShouldNot(BeZero()) + + Ω(0).Should(BeZero()) + Ω(1).ShouldNot(BeZero()) + + Ω(0.0).Should(BeZero()) + Ω(0.1).ShouldNot(BeZero()) + + // Ω([]int{}).Should(BeZero()) + Ω([]int{1}).ShouldNot(BeZero()) + + // Ω(map[string]int{}).Should(BeZero()) + Ω(map[string]int{"a": 1}).ShouldNot(BeZero()) + + Ω(myCustomType{}).Should(BeZero()) + Ω(myCustomType{s: "a"}).ShouldNot(BeZero()) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/consist_of.go b/vendor/github.com/onsi/gomega/matchers/consist_of.go new file mode 100644 index 0000000..7b0e088 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/consist_of.go @@ -0,0 +1,80 @@ +package matchers + +import ( + "fmt" + "reflect" + + "github.com/onsi/gomega/format" + "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph" +) + +type ConsistOfMatcher struct { + Elements []interface{} +} + +func (matcher *ConsistOfMatcher) Match(actual interface{}) (success bool, err error) { + if !isArrayOrSlice(actual) && !isMap(actual) { + return false, fmt.Errorf("ConsistOf matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1)) + } + + elements := matcher.Elements + if len(matcher.Elements) == 1 && isArrayOrSlice(matcher.Elements[0]) { + elements = []interface{}{} + value := reflect.ValueOf(matcher.Elements[0]) + for i := 0; i < value.Len(); i++ { + elements = append(elements, value.Index(i).Interface()) + } + } + + matchers := []interface{}{} + for _, element := range elements { + matcher, isMatcher := element.(omegaMatcher) + if !isMatcher { + matcher = &EqualMatcher{Expected: element} + } + matchers = append(matchers, matcher) + } + + values := matcher.valuesOf(actual) + + if len(values) != len(matchers) { + return false, nil + } + + neighbours := func(v, m interface{}) (bool, error) { + match, err := m.(omegaMatcher).Match(v) + return match && err == nil, nil + } + + bipartiteGraph, err := bipartitegraph.NewBipartiteGraph(values, matchers, neighbours) + if err != nil { + return false, err + } + + return len(bipartiteGraph.LargestMatching()) == len(values), nil +} + +func (matcher *ConsistOfMatcher) valuesOf(actual interface{}) []interface{} { + value := reflect.ValueOf(actual) + values := []interface{}{} + if isMap(actual) { + keys := value.MapKeys() + for i := 0; i < value.Len(); i++ { + values = append(values, value.MapIndex(keys[i]).Interface()) + } + } else { + for i := 0; i < value.Len(); i++ { + values = append(values, value.Index(i).Interface()) + } + } + + return values +} + +func (matcher *ConsistOfMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to consist of", matcher.Elements) +} + +func (matcher *ConsistOfMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to consist of", matcher.Elements) +} diff --git a/vendor/github.com/onsi/gomega/matchers/consist_of_test.go b/vendor/github.com/onsi/gomega/matchers/consist_of_test.go new file mode 100644 index 0000000..0b230e3 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/consist_of_test.go @@ -0,0 +1,75 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("ConsistOf", func() { + Context("with a slice", func() { + It("should do the right thing", func() { + Ω([]string{"foo", "bar", "baz"}).Should(ConsistOf("foo", "bar", "baz")) + Ω([]string{"foo", "bar", "baz"}).Should(ConsistOf("foo", "bar", "baz")) + Ω([]string{"foo", "bar", "baz"}).Should(ConsistOf("baz", "bar", "foo")) + Ω([]string{"foo", "bar", "baz"}).ShouldNot(ConsistOf("baz", "bar", "foo", "foo")) + Ω([]string{"foo", "bar", "baz"}).ShouldNot(ConsistOf("baz", "foo")) + }) + }) + + Context("with an array", func() { + It("should do the right thing", func() { + Ω([3]string{"foo", "bar", "baz"}).Should(ConsistOf("foo", "bar", "baz")) + Ω([3]string{"foo", "bar", "baz"}).Should(ConsistOf("baz", "bar", "foo")) + Ω([3]string{"foo", "bar", "baz"}).ShouldNot(ConsistOf("baz", "bar", "foo", "foo")) + Ω([3]string{"foo", "bar", "baz"}).ShouldNot(ConsistOf("baz", "foo")) + }) + }) + + Context("with a map", func() { + It("should apply to the values", func() { + Ω(map[int]string{1: "foo", 2: "bar", 3: "baz"}).Should(ConsistOf("foo", "bar", "baz")) + Ω(map[int]string{1: "foo", 2: "bar", 3: "baz"}).Should(ConsistOf("baz", "bar", "foo")) + Ω(map[int]string{1: "foo", 2: "bar", 3: "baz"}).ShouldNot(ConsistOf("baz", "bar", "foo", "foo")) + Ω(map[int]string{1: "foo", 2: "bar", 3: "baz"}).ShouldNot(ConsistOf("baz", "foo")) + }) + + }) + + Context("with anything else", func() { + It("should error", func() { + failures := InterceptGomegaFailures(func() { + Ω("foo").Should(ConsistOf("f", "o", "o")) + }) + + Ω(failures).Should(HaveLen(1)) + }) + }) + + Context("when passed matchers", func() { + It("should pass if the matchers pass", func() { + Ω([]string{"foo", "bar", "baz"}).Should(ConsistOf("foo", MatchRegexp("^ba"), "baz")) + Ω([]string{"foo", "bar", "baz"}).ShouldNot(ConsistOf("foo", MatchRegexp("^ba"))) + Ω([]string{"foo", "bar", "baz"}).ShouldNot(ConsistOf("foo", MatchRegexp("^ba"), MatchRegexp("foo"))) + Ω([]string{"foo", "bar", "baz"}).Should(ConsistOf("foo", MatchRegexp("^ba"), MatchRegexp("^ba"))) + Ω([]string{"foo", "bar", "baz"}).ShouldNot(ConsistOf("foo", MatchRegexp("^ba"), MatchRegexp("turducken"))) + }) + + It("should not depend on the order of the matchers", func() { + Ω([][]int{[]int{1, 2}, []int{2}}).Should(ConsistOf(ContainElement(1), ContainElement(2))) + Ω([][]int{[]int{1, 2}, []int{2}}).Should(ConsistOf(ContainElement(2), ContainElement(1))) + }) + + Context("when a matcher errors", func() { + It("should soldier on", func() { + Ω([]string{"foo", "bar", "baz"}).ShouldNot(ConsistOf(BeFalse(), "foo", "bar")) + Ω([]interface{}{"foo", "bar", false}).Should(ConsistOf(BeFalse(), ContainSubstring("foo"), "bar")) + }) + }) + }) + + Context("when passed exactly one argument, and that argument is a slice", func() { + It("should match against the elements of that argument", func() { + Ω([]string{"foo", "bar", "baz"}).Should(ConsistOf([]string{"foo", "bar", "baz"})) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/contain_element_matcher.go b/vendor/github.com/onsi/gomega/matchers/contain_element_matcher.go new file mode 100644 index 0000000..014a20f --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/contain_element_matcher.go @@ -0,0 +1,53 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "reflect" +) + +type ContainElementMatcher struct { + Element interface{} +} + +func (matcher *ContainElementMatcher) Match(actual interface{}) (success bool, err error) { + if !isArrayOrSlice(actual) && !isMap(actual) { + return false, fmt.Errorf("ContainElement matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1)) + } + + elemMatcher, elementIsMatcher := matcher.Element.(omegaMatcher) + if !elementIsMatcher { + elemMatcher = &EqualMatcher{Expected: matcher.Element} + } + + value := reflect.ValueOf(actual) + var keys []reflect.Value + if isMap(actual) { + keys = value.MapKeys() + } + for i := 0; i < value.Len(); i++ { + var success bool + var err error + if isMap(actual) { + success, err = elemMatcher.Match(value.MapIndex(keys[i]).Interface()) + } else { + success, err = elemMatcher.Match(value.Index(i).Interface()) + } + if err != nil { + return false, fmt.Errorf("ContainElement's element matcher failed with:\n\t%s", err.Error()) + } + if success { + return true, nil + } + } + + return false, nil +} + +func (matcher *ContainElementMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to contain element matching", matcher.Element) +} + +func (matcher *ContainElementMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to contain element matching", matcher.Element) +} diff --git a/vendor/github.com/onsi/gomega/matchers/contain_element_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/contain_element_matcher_test.go new file mode 100644 index 0000000..4d29eeb --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/contain_element_matcher_test.go @@ -0,0 +1,72 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("ContainElement", func() { + Context("when passed a supported type", func() { + Context("and expecting a non-matcher", func() { + It("should do the right thing", func() { + Ω([2]int{1, 2}).Should(ContainElement(2)) + Ω([2]int{1, 2}).ShouldNot(ContainElement(3)) + + Ω([]int{1, 2}).Should(ContainElement(2)) + Ω([]int{1, 2}).ShouldNot(ContainElement(3)) + + Ω(map[string]int{"foo": 1, "bar": 2}).Should(ContainElement(2)) + Ω(map[int]int{3: 1, 4: 2}).ShouldNot(ContainElement(3)) + + arr := make([]myCustomType, 2) + arr[0] = myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"a", "b"}} + arr[1] = myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"a", "c"}} + Ω(arr).Should(ContainElement(myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"a", "b"}})) + Ω(arr).ShouldNot(ContainElement(myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"b", "c"}})) + }) + }) + + Context("and expecting a matcher", func() { + It("should pass each element through the matcher", func() { + Ω([]int{1, 2, 3}).Should(ContainElement(BeNumerically(">=", 3))) + Ω([]int{1, 2, 3}).ShouldNot(ContainElement(BeNumerically(">", 3))) + Ω(map[string]int{"foo": 1, "bar": 2}).Should(ContainElement(BeNumerically(">=", 2))) + Ω(map[string]int{"foo": 1, "bar": 2}).ShouldNot(ContainElement(BeNumerically(">", 2))) + }) + + It("should fail if the matcher ever fails", func() { + actual := []interface{}{1, 2, "3", 4} + success, err := (&ContainElementMatcher{Element: BeNumerically(">=", 3)}).Match(actual) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + }) + + Context("when passed a correctly typed nil", func() { + It("should operate succesfully on the passed in value", func() { + var nilSlice []int + Ω(nilSlice).ShouldNot(ContainElement(1)) + + var nilMap map[int]string + Ω(nilMap).ShouldNot(ContainElement("foo")) + }) + }) + + Context("when passed an unsupported type", func() { + It("should error", func() { + success, err := (&ContainElementMatcher{Element: 0}).Match(0) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&ContainElementMatcher{Element: 0}).Match("abc") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&ContainElementMatcher{Element: 0}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/contain_substring_matcher.go b/vendor/github.com/onsi/gomega/matchers/contain_substring_matcher.go new file mode 100644 index 0000000..2e76089 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/contain_substring_matcher.go @@ -0,0 +1,37 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "strings" +) + +type ContainSubstringMatcher struct { + Substr string + Args []interface{} +} + +func (matcher *ContainSubstringMatcher) Match(actual interface{}) (success bool, err error) { + actualString, ok := toString(actual) + if !ok { + return false, fmt.Errorf("ContainSubstring matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1)) + } + + return strings.Contains(actualString, matcher.stringToMatch()), nil +} + +func (matcher *ContainSubstringMatcher) stringToMatch() string { + stringToMatch := matcher.Substr + if len(matcher.Args) > 0 { + stringToMatch = fmt.Sprintf(matcher.Substr, matcher.Args...) + } + return stringToMatch +} + +func (matcher *ContainSubstringMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to contain substring", matcher.stringToMatch()) +} + +func (matcher *ContainSubstringMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to contain substring", matcher.stringToMatch()) +} diff --git a/vendor/github.com/onsi/gomega/matchers/contain_substring_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/contain_substring_matcher_test.go new file mode 100644 index 0000000..6935168 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/contain_substring_matcher_test.go @@ -0,0 +1,36 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("ContainSubstringMatcher", func() { + Context("when actual is a string", func() { + It("should match against the string", func() { + Ω("Marvelous").Should(ContainSubstring("rve")) + Ω("Marvelous").ShouldNot(ContainSubstring("boo")) + }) + }) + + Context("when the matcher is called with multiple arguments", func() { + It("should pass the string and arguments to sprintf", func() { + Ω("Marvelous3").Should(ContainSubstring("velous%d", 3)) + }) + }) + + Context("when actual is a stringer", func() { + It("should call the stringer and match agains the returned string", func() { + Ω(&myStringer{a: "Abc3"}).Should(ContainSubstring("bc3")) + }) + }) + + Context("when actual is neither a string nor a stringer", func() { + It("should error", func() { + success, err := (&ContainSubstringMatcher{Substr: "2"}).Match(2) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/equal_matcher.go b/vendor/github.com/onsi/gomega/matchers/equal_matcher.go new file mode 100644 index 0000000..9f8f80a --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/equal_matcher.go @@ -0,0 +1,26 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "reflect" +) + +type EqualMatcher struct { + Expected interface{} +} + +func (matcher *EqualMatcher) Match(actual interface{}) (success bool, err error) { + if actual == nil && matcher.Expected == nil { + return false, fmt.Errorf("Refusing to compare to .") + } + return reflect.DeepEqual(actual, matcher.Expected), nil +} + +func (matcher *EqualMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to equal", matcher.Expected) +} + +func (matcher *EqualMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to equal", matcher.Expected) +} diff --git a/vendor/github.com/onsi/gomega/matchers/equal_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/equal_matcher_test.go new file mode 100644 index 0000000..ef0d137 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/equal_matcher_test.go @@ -0,0 +1,44 @@ +package matchers_test + +import ( + "errors" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("Equal", func() { + Context("when asserting that nil equals nil", func() { + It("should error", func() { + success, err := (&EqualMatcher{Expected: nil}).Match(nil) + + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("When asserting equality between objects", func() { + It("should do the right thing", func() { + Ω(5).Should(Equal(5)) + Ω(5.0).Should(Equal(5.0)) + + Ω(5).ShouldNot(Equal("5")) + Ω(5).ShouldNot(Equal(5.0)) + Ω(5).ShouldNot(Equal(3)) + + Ω("5").Should(Equal("5")) + Ω([]int{1, 2}).Should(Equal([]int{1, 2})) + Ω([]int{1, 2}).ShouldNot(Equal([]int{2, 1})) + Ω(map[string]string{"a": "b", "c": "d"}).Should(Equal(map[string]string{"a": "b", "c": "d"})) + Ω(map[string]string{"a": "b", "c": "d"}).ShouldNot(Equal(map[string]string{"a": "b", "c": "e"})) + Ω(errors.New("foo")).Should(Equal(errors.New("foo"))) + Ω(errors.New("foo")).ShouldNot(Equal(errors.New("bar"))) + + Ω(myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"a", "b"}}).Should(Equal(myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"a", "b"}})) + Ω(myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"a", "b"}}).ShouldNot(Equal(myCustomType{s: "bar", n: 3, f: 2.0, arr: []string{"a", "b"}})) + Ω(myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"a", "b"}}).ShouldNot(Equal(myCustomType{s: "foo", n: 2, f: 2.0, arr: []string{"a", "b"}})) + Ω(myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"a", "b"}}).ShouldNot(Equal(myCustomType{s: "foo", n: 3, f: 3.0, arr: []string{"a", "b"}})) + Ω(myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"a", "b"}}).ShouldNot(Equal(myCustomType{s: "foo", n: 3, f: 2.0, arr: []string{"a", "b", "c"}})) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/have_key_matcher.go b/vendor/github.com/onsi/gomega/matchers/have_key_matcher.go new file mode 100644 index 0000000..5701ba6 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_key_matcher.go @@ -0,0 +1,53 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "reflect" +) + +type HaveKeyMatcher struct { + Key interface{} +} + +func (matcher *HaveKeyMatcher) Match(actual interface{}) (success bool, err error) { + if !isMap(actual) { + return false, fmt.Errorf("HaveKey matcher expects a map. Got:%s", format.Object(actual, 1)) + } + + keyMatcher, keyIsMatcher := matcher.Key.(omegaMatcher) + if !keyIsMatcher { + keyMatcher = &EqualMatcher{Expected: matcher.Key} + } + + keys := reflect.ValueOf(actual).MapKeys() + for i := 0; i < len(keys); i++ { + success, err := keyMatcher.Match(keys[i].Interface()) + if err != nil { + return false, fmt.Errorf("HaveKey's key matcher failed with:\n%s%s", format.Indent, err.Error()) + } + if success { + return true, nil + } + } + + return false, nil +} + +func (matcher *HaveKeyMatcher) FailureMessage(actual interface{}) (message string) { + switch matcher.Key.(type) { + case omegaMatcher: + return format.Message(actual, "to have key matching", matcher.Key) + default: + return format.Message(actual, "to have key", matcher.Key) + } +} + +func (matcher *HaveKeyMatcher) NegatedFailureMessage(actual interface{}) (message string) { + switch matcher.Key.(type) { + case omegaMatcher: + return format.Message(actual, "not to have key matching", matcher.Key) + default: + return format.Message(actual, "not to have key", matcher.Key) + } +} diff --git a/vendor/github.com/onsi/gomega/matchers/have_key_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/have_key_matcher_test.go new file mode 100644 index 0000000..c663e30 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_key_matcher_test.go @@ -0,0 +1,73 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("HaveKey", func() { + var ( + stringKeys map[string]int + intKeys map[int]string + objKeys map[*myCustomType]string + + customA *myCustomType + customB *myCustomType + ) + BeforeEach(func() { + stringKeys = map[string]int{"foo": 2, "bar": 3} + intKeys = map[int]string{2: "foo", 3: "bar"} + + customA = &myCustomType{s: "a", n: 2, f: 2.3, arr: []string{"ice", "cream"}} + customB = &myCustomType{s: "b", n: 4, f: 3.1, arr: []string{"cake"}} + objKeys = map[*myCustomType]string{customA: "aardvark", customB: "kangaroo"} + }) + + Context("when passed a map", func() { + It("should do the right thing", func() { + Ω(stringKeys).Should(HaveKey("foo")) + Ω(stringKeys).ShouldNot(HaveKey("baz")) + + Ω(intKeys).Should(HaveKey(2)) + Ω(intKeys).ShouldNot(HaveKey(4)) + + Ω(objKeys).Should(HaveKey(customA)) + Ω(objKeys).Should(HaveKey(&myCustomType{s: "b", n: 4, f: 3.1, arr: []string{"cake"}})) + Ω(objKeys).ShouldNot(HaveKey(&myCustomType{s: "b", n: 4, f: 3.1, arr: []string{"apple", "pie"}})) + }) + }) + + Context("when passed a correctly typed nil", func() { + It("should operate succesfully on the passed in value", func() { + var nilMap map[int]string + Ω(nilMap).ShouldNot(HaveKey("foo")) + }) + }) + + Context("when the passed in key is actually a matcher", func() { + It("should pass each element through the matcher", func() { + Ω(stringKeys).Should(HaveKey(ContainSubstring("oo"))) + Ω(stringKeys).ShouldNot(HaveKey(ContainSubstring("foobar"))) + }) + + It("should fail if the matcher ever fails", func() { + actual := map[int]string{1: "a", 3: "b", 2: "c"} + success, err := (&HaveKeyMatcher{Key: ContainSubstring("ar")}).Match(actual) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when passed something that is not a map", func() { + It("should error", func() { + success, err := (&HaveKeyMatcher{Key: "foo"}).Match([]string{"foo"}) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&HaveKeyMatcher{Key: "foo"}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go b/vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go new file mode 100644 index 0000000..464ac18 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go @@ -0,0 +1,73 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "reflect" +) + +type HaveKeyWithValueMatcher struct { + Key interface{} + Value interface{} +} + +func (matcher *HaveKeyWithValueMatcher) Match(actual interface{}) (success bool, err error) { + if !isMap(actual) { + return false, fmt.Errorf("HaveKeyWithValue matcher expects a map. Got:%s", format.Object(actual, 1)) + } + + keyMatcher, keyIsMatcher := matcher.Key.(omegaMatcher) + if !keyIsMatcher { + keyMatcher = &EqualMatcher{Expected: matcher.Key} + } + + valueMatcher, valueIsMatcher := matcher.Value.(omegaMatcher) + if !valueIsMatcher { + valueMatcher = &EqualMatcher{Expected: matcher.Value} + } + + keys := reflect.ValueOf(actual).MapKeys() + for i := 0; i < len(keys); i++ { + success, err := keyMatcher.Match(keys[i].Interface()) + if err != nil { + return false, fmt.Errorf("HaveKeyWithValue's key matcher failed with:\n%s%s", format.Indent, err.Error()) + } + if success { + actualValue := reflect.ValueOf(actual).MapIndex(keys[i]) + success, err := valueMatcher.Match(actualValue.Interface()) + if err != nil { + return false, fmt.Errorf("HaveKeyWithValue's value matcher failed with:\n%s%s", format.Indent, err.Error()) + } + return success, nil + } + } + + return false, nil +} + +func (matcher *HaveKeyWithValueMatcher) FailureMessage(actual interface{}) (message string) { + str := "to have {key: value}" + if _, ok := matcher.Key.(omegaMatcher); ok { + str += " matching" + } else if _, ok := matcher.Value.(omegaMatcher); ok { + str += " matching" + } + + expect := make(map[interface{}]interface{}, 1) + expect[matcher.Key] = matcher.Value + return format.Message(actual, str, expect) +} + +func (matcher *HaveKeyWithValueMatcher) NegatedFailureMessage(actual interface{}) (message string) { + kStr := "not to have key" + if _, ok := matcher.Key.(omegaMatcher); ok { + kStr = "not to have key matching" + } + + vStr := "or that key's value not be" + if _, ok := matcher.Value.(omegaMatcher); ok { + vStr = "or to have that key's value not matching" + } + + return format.Message(actual, kStr, matcher.Key, vStr, matcher.Value) +} diff --git a/vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher_test.go new file mode 100644 index 0000000..06a2242 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher_test.go @@ -0,0 +1,82 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("HaveKeyWithValue", func() { + var ( + stringKeys map[string]int + intKeys map[int]string + objKeys map[*myCustomType]*myCustomType + + customA *myCustomType + customB *myCustomType + ) + BeforeEach(func() { + stringKeys = map[string]int{"foo": 2, "bar": 3} + intKeys = map[int]string{2: "foo", 3: "bar"} + + customA = &myCustomType{s: "a", n: 2, f: 2.3, arr: []string{"ice", "cream"}} + customB = &myCustomType{s: "b", n: 4, f: 3.1, arr: []string{"cake"}} + objKeys = map[*myCustomType]*myCustomType{customA: customA, customB: customA} + }) + + Context("when passed a map", func() { + It("should do the right thing", func() { + Ω(stringKeys).Should(HaveKeyWithValue("foo", 2)) + Ω(stringKeys).ShouldNot(HaveKeyWithValue("foo", 1)) + Ω(stringKeys).ShouldNot(HaveKeyWithValue("baz", 2)) + Ω(stringKeys).ShouldNot(HaveKeyWithValue("baz", 1)) + + Ω(intKeys).Should(HaveKeyWithValue(2, "foo")) + Ω(intKeys).ShouldNot(HaveKeyWithValue(4, "foo")) + Ω(intKeys).ShouldNot(HaveKeyWithValue(2, "baz")) + + Ω(objKeys).Should(HaveKeyWithValue(customA, customA)) + Ω(objKeys).Should(HaveKeyWithValue(&myCustomType{s: "b", n: 4, f: 3.1, arr: []string{"cake"}}, &myCustomType{s: "a", n: 2, f: 2.3, arr: []string{"ice", "cream"}})) + Ω(objKeys).ShouldNot(HaveKeyWithValue(&myCustomType{s: "b", n: 4, f: 3.1, arr: []string{"apple", "pie"}}, customA)) + }) + }) + + Context("when passed a correctly typed nil", func() { + It("should operate succesfully on the passed in value", func() { + var nilMap map[int]string + Ω(nilMap).ShouldNot(HaveKeyWithValue("foo", "bar")) + }) + }) + + Context("when the passed in key or value is actually a matcher", func() { + It("should pass each element through the matcher", func() { + Ω(stringKeys).Should(HaveKeyWithValue(ContainSubstring("oo"), 2)) + Ω(intKeys).Should(HaveKeyWithValue(2, ContainSubstring("oo"))) + Ω(stringKeys).ShouldNot(HaveKeyWithValue(ContainSubstring("foobar"), 2)) + }) + + It("should fail if the matcher ever fails", func() { + actual := map[int]string{1: "a", 3: "b", 2: "c"} + success, err := (&HaveKeyWithValueMatcher{Key: ContainSubstring("ar"), Value: 2}).Match(actual) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + otherActual := map[string]int{"a": 1, "b": 2, "c": 3} + success, err = (&HaveKeyWithValueMatcher{Key: "a", Value: ContainSubstring("1")}).Match(otherActual) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when passed something that is not a map", func() { + It("should error", func() { + success, err := (&HaveKeyWithValueMatcher{Key: "foo", Value: "bar"}).Match([]string{"foo"}) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&HaveKeyWithValueMatcher{Key: "foo", Value: "bar"}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/have_len_matcher.go b/vendor/github.com/onsi/gomega/matchers/have_len_matcher.go new file mode 100644 index 0000000..a183775 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_len_matcher.go @@ -0,0 +1,27 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" +) + +type HaveLenMatcher struct { + Count int +} + +func (matcher *HaveLenMatcher) Match(actual interface{}) (success bool, err error) { + length, ok := lengthOf(actual) + if !ok { + return false, fmt.Errorf("HaveLen matcher expects a string/array/map/channel/slice. Got:\n%s", format.Object(actual, 1)) + } + + return length == matcher.Count, nil +} + +func (matcher *HaveLenMatcher) FailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("Expected\n%s\nto have length %d", format.Object(actual, 1), matcher.Count) +} + +func (matcher *HaveLenMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("Expected\n%s\nnot to have length %d", format.Object(actual, 1), matcher.Count) +} diff --git a/vendor/github.com/onsi/gomega/matchers/have_len_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/have_len_matcher_test.go new file mode 100644 index 0000000..1e6aa69 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_len_matcher_test.go @@ -0,0 +1,53 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("HaveLen", func() { + Context("when passed a supported type", func() { + It("should do the right thing", func() { + Ω("").Should(HaveLen(0)) + Ω("AA").Should(HaveLen(2)) + + Ω([0]int{}).Should(HaveLen(0)) + Ω([2]int{1, 2}).Should(HaveLen(2)) + + Ω([]int{}).Should(HaveLen(0)) + Ω([]int{1, 2, 3}).Should(HaveLen(3)) + + Ω(map[string]int{}).Should(HaveLen(0)) + Ω(map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}).Should(HaveLen(4)) + + c := make(chan bool, 3) + Ω(c).Should(HaveLen(0)) + c <- true + c <- true + Ω(c).Should(HaveLen(2)) + }) + }) + + Context("when passed a correctly typed nil", func() { + It("should operate succesfully on the passed in value", func() { + var nilSlice []int + Ω(nilSlice).Should(HaveLen(0)) + + var nilMap map[int]string + Ω(nilMap).Should(HaveLen(0)) + }) + }) + + Context("when passed an unsupported type", func() { + It("should error", func() { + success, err := (&HaveLenMatcher{Count: 0}).Match(0) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&HaveLenMatcher{Count: 0}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/have_occurred_matcher.go b/vendor/github.com/onsi/gomega/matchers/have_occurred_matcher.go new file mode 100644 index 0000000..b5095f1 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_occurred_matcher.go @@ -0,0 +1,29 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" +) + +type HaveOccurredMatcher struct { +} + +func (matcher *HaveOccurredMatcher) Match(actual interface{}) (success bool, err error) { + if actual == nil { + return false, nil + } + + if isError(actual) { + return true, nil + } + + return false, fmt.Errorf("Expected an error. Got:\n%s", format.Object(actual, 1)) +} + +func (matcher *HaveOccurredMatcher) FailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("Expected an error to have occured. Got:\n%s", format.Object(actual, 1)) +} + +func (matcher *HaveOccurredMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("Expected error:\n%s\n%s\n%s", format.Object(actual, 1), format.IndentString(actual.(error).Error(), 1), "not to have occurred") +} diff --git a/vendor/github.com/onsi/gomega/matchers/have_occurred_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/have_occurred_matcher_test.go new file mode 100644 index 0000000..ef971aa --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_occurred_matcher_test.go @@ -0,0 +1,28 @@ +package matchers_test + +import ( + "errors" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("HaveOccurred", func() { + It("should succeed if matching an error", func() { + Ω(errors.New("Foo")).Should(HaveOccurred()) + }) + + It("should not succeed with nil", func() { + Ω(nil).ShouldNot(HaveOccurred()) + }) + + It("should only support errors and nil", func() { + success, err := (&HaveOccurredMatcher{}).Match("foo") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&HaveOccurredMatcher{}).Match("") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/match_error_matcher.go b/vendor/github.com/onsi/gomega/matchers/match_error_matcher.go new file mode 100644 index 0000000..ad00fef --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/match_error_matcher.go @@ -0,0 +1,41 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "reflect" +) + +type MatchErrorMatcher struct { + Expected interface{} +} + +func (matcher *MatchErrorMatcher) Match(actual interface{}) (success bool, err error) { + if isNil(actual) { + return false, fmt.Errorf("Expected an error, got nil") + } + + if !isError(actual) { + return false, fmt.Errorf("Expected an error. Got:\n%s", format.Object(actual, 1)) + } + + actualErr := actual.(error) + + if isString(matcher.Expected) { + return reflect.DeepEqual(actualErr.Error(), matcher.Expected), nil + } + + if isError(matcher.Expected) { + return reflect.DeepEqual(actualErr, matcher.Expected), nil + } + + return false, fmt.Errorf("MatchError must be passed an error or string. Got:\n%s", format.Object(matcher.Expected, 1)) +} + +func (matcher *MatchErrorMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to match error", matcher.Expected) +} + +func (matcher *MatchErrorMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to match error", matcher.Expected) +} diff --git a/vendor/github.com/onsi/gomega/matchers/match_error_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/match_error_matcher_test.go new file mode 100644 index 0000000..b331e2f --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/match_error_matcher_test.go @@ -0,0 +1,80 @@ +package matchers_test + +import ( + "errors" + "fmt" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +type CustomError struct { +} + +func (c CustomError) Error() string { + return "an error" +} + +var _ = Describe("MatchErrorMatcher", func() { + Context("When asserting against an error", func() { + It("should succeed when matching with an error", func() { + err := errors.New("an error") + fmtErr := fmt.Errorf("an error") + customErr := CustomError{} + + Ω(err).Should(MatchError(errors.New("an error"))) + Ω(err).ShouldNot(MatchError(errors.New("another error"))) + + Ω(fmtErr).Should(MatchError(errors.New("an error"))) + Ω(customErr).Should(MatchError(CustomError{})) + }) + + It("should succeed when matching with a string", func() { + err := errors.New("an error") + fmtErr := fmt.Errorf("an error") + customErr := CustomError{} + + Ω(err).Should(MatchError("an error")) + Ω(err).ShouldNot(MatchError("another error")) + + Ω(fmtErr).Should(MatchError("an error")) + Ω(customErr).Should(MatchError("an error")) + }) + + It("should fail when passed anything else", func() { + actualErr := errors.New("an error") + _, err := (&MatchErrorMatcher{ + Expected: []byte("an error"), + }).Match(actualErr) + Ω(err).Should(HaveOccurred()) + + _, err = (&MatchErrorMatcher{ + Expected: 3, + }).Match(actualErr) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when passed nil", func() { + It("should fail", func() { + _, err := (&MatchErrorMatcher{ + Expected: "an error", + }).Match(nil) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when passed a non-error", func() { + It("should fail", func() { + _, err := (&MatchErrorMatcher{ + Expected: "an error", + }).Match("an error") + Ω(err).Should(HaveOccurred()) + + _, err = (&MatchErrorMatcher{ + Expected: "an error", + }).Match(3) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/match_json_matcher.go b/vendor/github.com/onsi/gomega/matchers/match_json_matcher.go new file mode 100644 index 0000000..bedf851 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/match_json_matcher.go @@ -0,0 +1,61 @@ +package matchers + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/onsi/gomega/format" + "reflect" +) + +type MatchJSONMatcher struct { + JSONToMatch interface{} +} + +func (matcher *MatchJSONMatcher) Match(actual interface{}) (success bool, err error) { + actualString, expectedString, err := matcher.prettyPrint(actual) + if err != nil { + return false, err + } + + var aval interface{} + var eval interface{} + + // this is guarded by prettyPrint + json.Unmarshal([]byte(actualString), &aval) + json.Unmarshal([]byte(expectedString), &eval) + + return reflect.DeepEqual(aval, eval), nil +} + +func (matcher *MatchJSONMatcher) FailureMessage(actual interface{}) (message string) { + actualString, expectedString, _ := matcher.prettyPrint(actual) + return format.Message(actualString, "to match JSON of", expectedString) +} + +func (matcher *MatchJSONMatcher) NegatedFailureMessage(actual interface{}) (message string) { + actualString, expectedString, _ := matcher.prettyPrint(actual) + return format.Message(actualString, "not to match JSON of", expectedString) +} + +func (matcher *MatchJSONMatcher) prettyPrint(actual interface{}) (actualFormatted, expectedFormatted string, err error) { + actualString, aok := toString(actual) + expectedString, eok := toString(matcher.JSONToMatch) + + if !(aok && eok) { + return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1)) + } + + abuf := new(bytes.Buffer) + ebuf := new(bytes.Buffer) + + if err := json.Indent(abuf, []byte(actualString), "", " "); err != nil { + return "", "", err + } + + if err := json.Indent(ebuf, []byte(expectedString), "", " "); err != nil { + return "", "", err + } + + return actualString, expectedString, nil +} diff --git a/vendor/github.com/onsi/gomega/matchers/match_json_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/match_json_matcher_test.go new file mode 100644 index 0000000..c1924ba --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/match_json_matcher_test.go @@ -0,0 +1,59 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("MatchJSONMatcher", func() { + Context("When passed stringifiables", func() { + It("should succeed if the JSON matches", func() { + Ω("{}").Should(MatchJSON("{}")) + Ω(`{"a":1}`).Should(MatchJSON(`{"a":1}`)) + Ω(`{ + "a":1 + }`).Should(MatchJSON(`{"a":1}`)) + Ω(`{"a":1, "b":2}`).Should(MatchJSON(`{"b":2, "a":1}`)) + Ω(`{"a":1}`).ShouldNot(MatchJSON(`{"b":2, "a":1}`)) + }) + + It("should work with byte arrays", func() { + Ω([]byte("{}")).Should(MatchJSON([]byte("{}"))) + Ω("{}").Should(MatchJSON([]byte("{}"))) + Ω([]byte("{}")).Should(MatchJSON("{}")) + }) + }) + + Context("when either side is not valid JSON", func() { + It("should error", func() { + success, err := (&MatchJSONMatcher{JSONToMatch: `oops`}).Match(`{}`) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&MatchJSONMatcher{JSONToMatch: `{}`}).Match(`oops`) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when either side is neither a string nor a stringer", func() { + It("should error", func() { + success, err := (&MatchJSONMatcher{JSONToMatch: "{}"}).Match(2) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&MatchJSONMatcher{JSONToMatch: 2}).Match("{}") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&MatchJSONMatcher{JSONToMatch: nil}).Match("{}") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&MatchJSONMatcher{JSONToMatch: 2}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/match_regexp_matcher.go b/vendor/github.com/onsi/gomega/matchers/match_regexp_matcher.go new file mode 100644 index 0000000..7ca79a1 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/match_regexp_matcher.go @@ -0,0 +1,42 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "regexp" +) + +type MatchRegexpMatcher struct { + Regexp string + Args []interface{} +} + +func (matcher *MatchRegexpMatcher) Match(actual interface{}) (success bool, err error) { + actualString, ok := toString(actual) + if !ok { + return false, fmt.Errorf("RegExp matcher requires a string or stringer.\nGot:%s", format.Object(actual, 1)) + } + + match, err := regexp.Match(matcher.regexp(), []byte(actualString)) + if err != nil { + return false, fmt.Errorf("RegExp match failed to compile with error:\n\t%s", err.Error()) + } + + return match, nil +} + +func (matcher *MatchRegexpMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to match regular expression", matcher.regexp()) +} + +func (matcher *MatchRegexpMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to match regular expression", matcher.regexp()) +} + +func (matcher *MatchRegexpMatcher) regexp() string { + re := matcher.Regexp + if len(matcher.Args) > 0 { + re = fmt.Sprintf(matcher.Regexp, matcher.Args...) + } + return re +} diff --git a/vendor/github.com/onsi/gomega/matchers/match_regexp_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/match_regexp_matcher_test.go new file mode 100644 index 0000000..bb521cc --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/match_regexp_matcher_test.go @@ -0,0 +1,44 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("MatchRegexp", func() { + Context("when actual is a string", func() { + It("should match against the string", func() { + Ω(" a2!bla").Should(MatchRegexp(`\d!`)) + Ω(" a2!bla").ShouldNot(MatchRegexp(`[A-Z]`)) + }) + }) + + Context("when actual is a stringer", func() { + It("should call the stringer and match agains the returned string", func() { + Ω(&myStringer{a: "Abc3"}).Should(MatchRegexp(`[A-Z][a-z]+\d`)) + }) + }) + + Context("when the matcher is called with multiple arguments", func() { + It("should pass the string and arguments to sprintf", func() { + Ω(" a23!bla").Should(MatchRegexp(`\d%d!`, 3)) + }) + }) + + Context("when actual is neither a string nor a stringer", func() { + It("should error", func() { + success, err := (&MatchRegexpMatcher{Regexp: `\d`}).Match(2) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when the passed in regexp fails to compile", func() { + It("should error", func() { + success, err := (&MatchRegexpMatcher{Regexp: "("}).Match("Foo") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/matcher_tests_suite_test.go b/vendor/github.com/onsi/gomega/matchers/matcher_tests_suite_test.go new file mode 100644 index 0000000..4bc6d9d --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/matcher_tests_suite_test.go @@ -0,0 +1,29 @@ +package matchers_test + +import ( + "testing" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +type myStringer struct { + a string +} + +func (s *myStringer) String() string { + return s.a +} + +type StringAlias string + +type myCustomType struct { + s string + n int + f float32 + arr []string +} + +func Test(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Gomega") +} diff --git a/vendor/github.com/onsi/gomega/matchers/panic_matcher.go b/vendor/github.com/onsi/gomega/matchers/panic_matcher.go new file mode 100644 index 0000000..75ab251 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/panic_matcher.go @@ -0,0 +1,42 @@ +package matchers + +import ( + "fmt" + "github.com/onsi/gomega/format" + "reflect" +) + +type PanicMatcher struct{} + +func (matcher *PanicMatcher) Match(actual interface{}) (success bool, err error) { + if actual == nil { + return false, fmt.Errorf("PanicMatcher expects a non-nil actual.") + } + + actualType := reflect.TypeOf(actual) + if actualType.Kind() != reflect.Func { + return false, fmt.Errorf("PanicMatcher expects a function. Got:\n%s", format.Object(actual, 1)) + } + if !(actualType.NumIn() == 0 && actualType.NumOut() == 0) { + return false, fmt.Errorf("PanicMatcher expects a function with no arguments and no return value. Got:\n%s", format.Object(actual, 1)) + } + + success = false + defer func() { + if e := recover(); e != nil { + success = true + } + }() + + reflect.ValueOf(actual).Call([]reflect.Value{}) + + return +} + +func (matcher *PanicMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to panic") +} + +func (matcher *PanicMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to panic") +} diff --git a/vendor/github.com/onsi/gomega/matchers/panic_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/panic_matcher_test.go new file mode 100644 index 0000000..17f3935 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/panic_matcher_test.go @@ -0,0 +1,36 @@ +package matchers_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +var _ = Describe("Panic", func() { + Context("when passed something that's not a function that takes zero arguments and returns nothing", func() { + It("should error", func() { + success, err := (&PanicMatcher{}).Match("foo") + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&PanicMatcher{}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&PanicMatcher{}).Match(func(foo string) {}) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&PanicMatcher{}).Match(func() string { return "bar" }) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when passed a function of the correct type", func() { + It("should call the function and pass if the function panics", func() { + Ω(func() { panic("ack!") }).Should(Panic()) + Ω(func() {}).ShouldNot(Panic()) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/receive_matcher.go b/vendor/github.com/onsi/gomega/matchers/receive_matcher.go new file mode 100644 index 0000000..78a2fbc --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/receive_matcher.go @@ -0,0 +1,116 @@ +package matchers + +import ( + "fmt" + "reflect" + + "github.com/onsi/gomega/format" +) + +type ReceiveMatcher struct { + Arg interface{} + receivedValue reflect.Value + channelClosed bool +} + +func (matcher *ReceiveMatcher) Match(actual interface{}) (success bool, err error) { + if !isChan(actual) { + return false, fmt.Errorf("ReceiveMatcher expects a channel. Got:\n%s", format.Object(actual, 1)) + } + + channelType := reflect.TypeOf(actual) + channelValue := reflect.ValueOf(actual) + + if channelType.ChanDir() == reflect.SendDir { + return false, fmt.Errorf("ReceiveMatcher matcher cannot be passed a send-only channel. Got:\n%s", format.Object(actual, 1)) + } + + var subMatcher omegaMatcher + var hasSubMatcher bool + + if matcher.Arg != nil { + subMatcher, hasSubMatcher = (matcher.Arg).(omegaMatcher) + if !hasSubMatcher { + argType := reflect.TypeOf(matcher.Arg) + if argType.Kind() != reflect.Ptr { + return false, fmt.Errorf("Cannot assign a value from the channel:\n%s\nTo:\n%s\nYou need to pass a pointer!", format.Object(actual, 1), format.Object(matcher.Arg, 1)) + } + + assignable := channelType.Elem().AssignableTo(argType.Elem()) + if !assignable { + return false, fmt.Errorf("Cannot assign a value from the channel:\n%s\nTo:\n%s", format.Object(actual, 1), format.Object(matcher.Arg, 1)) + } + } + } + + winnerIndex, value, open := reflect.Select([]reflect.SelectCase{ + reflect.SelectCase{Dir: reflect.SelectRecv, Chan: channelValue}, + reflect.SelectCase{Dir: reflect.SelectDefault}, + }) + + var closed bool + var didReceive bool + if winnerIndex == 0 { + closed = !open + didReceive = open + } + matcher.channelClosed = closed + + if closed { + return false, fmt.Errorf("ReceiveMatcher was given a closed channel:\n%s", format.Object(actual, 1)) + } + + if hasSubMatcher { + if didReceive { + matcher.receivedValue = value + return subMatcher.Match(matcher.receivedValue.Interface()) + } else { + return false, nil + } + } + + if didReceive { + if matcher.Arg != nil { + outValue := reflect.ValueOf(matcher.Arg) + reflect.Indirect(outValue).Set(value) + } + + return true, nil + } else { + return false, nil + } +} + +func (matcher *ReceiveMatcher) FailureMessage(actual interface{}) (message string) { + subMatcher, hasSubMatcher := (matcher.Arg).(omegaMatcher) + + if hasSubMatcher { + if matcher.receivedValue.IsValid() { + return subMatcher.FailureMessage(matcher.receivedValue.Interface()) + } + return "When passed a matcher, ReceiveMatcher's channel *must* receive something." + } else { + return format.Message(actual, "to receive something") + } +} + +func (matcher *ReceiveMatcher) NegatedFailureMessage(actual interface{}) (message string) { + subMatcher, hasSubMatcher := (matcher.Arg).(omegaMatcher) + + if hasSubMatcher { + if matcher.receivedValue.IsValid() { + return subMatcher.NegatedFailureMessage(matcher.receivedValue.Interface()) + } + return "When passed a matcher, ReceiveMatcher's channel *must* receive something." + } else { + return format.Message(actual, "not to receive anything") + } +} + +func (matcher *ReceiveMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { + if !isChan(actual) { + return false + } + + return !matcher.channelClosed +} diff --git a/vendor/github.com/onsi/gomega/matchers/receive_matcher_test.go b/vendor/github.com/onsi/gomega/matchers/receive_matcher_test.go new file mode 100644 index 0000000..23179dc --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/receive_matcher_test.go @@ -0,0 +1,284 @@ +package matchers_test + +import ( + "time" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/matchers" +) + +type kungFuActor interface { + DrunkenMaster() bool +} + +type jackie struct { + name string +} + +func (j *jackie) DrunkenMaster() bool { + return true +} + +var _ = Describe("ReceiveMatcher", func() { + Context("with no argument", func() { + Context("for a buffered channel", func() { + It("should succeed", func() { + channel := make(chan bool, 1) + + Ω(channel).ShouldNot(Receive()) + + channel <- true + + Ω(channel).Should(Receive()) + }) + }) + + Context("for an unbuffered channel", func() { + It("should succeed (eventually)", func() { + channel := make(chan bool) + + Ω(channel).ShouldNot(Receive()) + + go func() { + time.Sleep(10 * time.Millisecond) + channel <- true + }() + + Eventually(channel).Should(Receive()) + }) + }) + }) + + Context("with a pointer argument", func() { + Context("of the correct type", func() { + It("should write the value received on the channel to the pointer", func() { + channel := make(chan int, 1) + + var value int + + Ω(channel).ShouldNot(Receive(&value)) + Ω(value).Should(BeZero()) + + channel <- 17 + + Ω(channel).Should(Receive(&value)) + Ω(value).Should(Equal(17)) + }) + }) + + Context("to various types of objects", func() { + It("should work", func() { + //channels of strings + stringChan := make(chan string, 1) + stringChan <- "foo" + + var s string + Ω(stringChan).Should(Receive(&s)) + Ω(s).Should(Equal("foo")) + + //channels of slices + sliceChan := make(chan []bool, 1) + sliceChan <- []bool{true, true, false} + + var sl []bool + Ω(sliceChan).Should(Receive(&sl)) + Ω(sl).Should(Equal([]bool{true, true, false})) + + //channels of channels + chanChan := make(chan chan bool, 1) + c := make(chan bool) + chanChan <- c + + var receivedC chan bool + Ω(chanChan).Should(Receive(&receivedC)) + Ω(receivedC).Should(Equal(c)) + + //channels of interfaces + jackieChan := make(chan kungFuActor, 1) + aJackie := &jackie{name: "Jackie Chan"} + jackieChan <- aJackie + + var theJackie kungFuActor + Ω(jackieChan).Should(Receive(&theJackie)) + Ω(theJackie).Should(Equal(aJackie)) + }) + }) + + Context("of the wrong type", func() { + It("should error", func() { + channel := make(chan int) + var incorrectType bool + + success, err := (&ReceiveMatcher{Arg: &incorrectType}).Match(channel) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + var notAPointer int + success, err = (&ReceiveMatcher{Arg: notAPointer}).Match(channel) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + }) + + Context("with a matcher", func() { + It("should defer to the underlying matcher", func() { + intChannel := make(chan int, 1) + intChannel <- 3 + Ω(intChannel).Should(Receive(Equal(3))) + + intChannel <- 2 + Ω(intChannel).ShouldNot(Receive(Equal(3))) + + stringChannel := make(chan []string, 1) + stringChannel <- []string{"foo", "bar", "baz"} + Ω(stringChannel).Should(Receive(ContainElement(ContainSubstring("fo")))) + + stringChannel <- []string{"foo", "bar", "baz"} + Ω(stringChannel).ShouldNot(Receive(ContainElement(ContainSubstring("archipelago")))) + }) + + It("should defer to the underlying matcher for the message", func() { + matcher := Receive(Equal(3)) + channel := make(chan int, 1) + channel <- 2 + matcher.Match(channel) + Ω(matcher.FailureMessage(channel)).Should(MatchRegexp(`Expected\s+: 2\s+to equal\s+: 3`)) + + channel <- 3 + matcher.Match(channel) + Ω(matcher.NegatedFailureMessage(channel)).Should(MatchRegexp(`Expected\s+: 3\s+not to equal\s+: 3`)) + }) + + It("should work just fine with Eventually", func() { + stringChannel := make(chan string) + + go func() { + time.Sleep(5 * time.Millisecond) + stringChannel <- "A" + time.Sleep(5 * time.Millisecond) + stringChannel <- "B" + }() + + Eventually(stringChannel).Should(Receive(Equal("B"))) + }) + + Context("if the matcher errors", func() { + It("should error", func() { + channel := make(chan int, 1) + channel <- 3 + success, err := (&ReceiveMatcher{Arg: ContainSubstring("three")}).Match(channel) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("if nothing is received", func() { + It("should fail", func() { + channel := make(chan int, 1) + success, err := (&ReceiveMatcher{Arg: Equal(1)}).Match(channel) + Ω(success).Should(BeFalse()) + Ω(err).ShouldNot(HaveOccurred()) + }) + }) + }) + + Context("When actual is a *closed* channel", func() { + Context("for a buffered channel", func() { + It("should work until it hits the end of the buffer", func() { + channel := make(chan bool, 1) + channel <- true + + close(channel) + + Ω(channel).Should(Receive()) + + success, err := (&ReceiveMatcher{}).Match(channel) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("for an unbuffered channel", func() { + It("should error", func() { + channel := make(chan bool) + close(channel) + + success, err := (&ReceiveMatcher{}).Match(channel) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + }) + + Context("When actual is a send-only channel", func() { + It("should error", func() { + channel := make(chan bool) + + var writerChannel chan<- bool + writerChannel = channel + + success, err := (&ReceiveMatcher{}).Match(writerChannel) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Context("when acutal is a non-channel", func() { + It("should error", func() { + var nilChannel chan bool + + success, err := (&ReceiveMatcher{}).Match(nilChannel) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&ReceiveMatcher{}).Match(nil) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + + success, err = (&ReceiveMatcher{}).Match(3) + Ω(success).Should(BeFalse()) + Ω(err).Should(HaveOccurred()) + }) + }) + + Describe("when used with eventually and a custom matcher", func() { + It("should return the matcher's error when a failing value is received on the channel, instead of the must receive something failure", func() { + failures := InterceptGomegaFailures(func() { + c := make(chan string, 0) + Eventually(c, 0.01).Should(Receive(Equal("hello"))) + }) + Ω(failures[0]).Should(ContainSubstring("When passed a matcher, ReceiveMatcher's channel *must* receive something.")) + + failures = InterceptGomegaFailures(func() { + c := make(chan string, 1) + c <- "hi" + Eventually(c, 0.01).Should(Receive(Equal("hello"))) + }) + Ω(failures[0]).Should(ContainSubstring(": hello")) + }) + }) + + Describe("Bailing early", func() { + It("should bail early when passed a closed channel", func() { + c := make(chan bool) + close(c) + + t := time.Now() + failures := InterceptGomegaFailures(func() { + Eventually(c).Should(Receive()) + }) + Ω(time.Since(t)).Should(BeNumerically("<", 500*time.Millisecond)) + Ω(failures).Should(HaveLen(1)) + }) + + It("should bail early when passed a non-channel", func() { + t := time.Now() + failures := InterceptGomegaFailures(func() { + Eventually(3).Should(Receive()) + }) + Ω(time.Since(t)).Should(BeNumerically("<", 500*time.Millisecond)) + Ω(failures).Should(HaveLen(1)) + }) + }) +}) diff --git a/vendor/github.com/onsi/gomega/matchers/support/goraph/MIT.LICENSE b/vendor/github.com/onsi/gomega/matchers/support/goraph/MIT.LICENSE new file mode 100644 index 0000000..8edd817 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/support/goraph/MIT.LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2014 Amit Kumar Gupta + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraph.go b/vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraph.go new file mode 100644 index 0000000..119d21e --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraph.go @@ -0,0 +1,41 @@ +package bipartitegraph + +import "errors" +import "fmt" + +import . "github.com/onsi/gomega/matchers/support/goraph/node" +import . "github.com/onsi/gomega/matchers/support/goraph/edge" + +type BipartiteGraph struct { + Left NodeOrderedSet + Right NodeOrderedSet + Edges EdgeSet +} + +func NewBipartiteGraph(leftValues, rightValues []interface{}, neighbours func(interface{}, interface{}) (bool, error)) (*BipartiteGraph, error) { + left := NodeOrderedSet{} + for i, _ := range leftValues { + left = append(left, Node{i}) + } + + right := NodeOrderedSet{} + for j, _ := range rightValues { + right = append(right, Node{j + len(left)}) + } + + edges := EdgeSet{} + for i, leftValue := range leftValues { + for j, rightValue := range rightValues { + neighbours, err := neighbours(leftValue, rightValue) + if err != nil { + return nil, errors.New(fmt.Sprintf("error determining adjacency for %v and %v: %s", leftValue, rightValue, err.Error())) + } + + if neighbours { + edges = append(edges, Edge{left[i], right[j]}) + } + } + } + + return &BipartiteGraph{left, right, edges}, nil +} diff --git a/vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go b/vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go new file mode 100644 index 0000000..32529c5 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go @@ -0,0 +1,161 @@ +package bipartitegraph + +import . "github.com/onsi/gomega/matchers/support/goraph/node" +import . "github.com/onsi/gomega/matchers/support/goraph/edge" +import "github.com/onsi/gomega/matchers/support/goraph/util" + +func (bg *BipartiteGraph) LargestMatching() (matching EdgeSet) { + paths := bg.maximalDisjointSLAPCollection(matching) + + for len(paths) > 0 { + for _, path := range paths { + matching = matching.SymmetricDifference(path) + } + paths = bg.maximalDisjointSLAPCollection(matching) + } + + return +} + +func (bg *BipartiteGraph) maximalDisjointSLAPCollection(matching EdgeSet) (result []EdgeSet) { + guideLayers := bg.createSLAPGuideLayers(matching) + if len(guideLayers) == 0 { + return + } + + used := make(map[Node]bool) + + for _, u := range guideLayers[len(guideLayers)-1] { + slap, found := bg.findDisjointSLAP(u, matching, guideLayers, used) + if found { + for _, edge := range slap { + used[edge.Node1] = true + used[edge.Node2] = true + } + result = append(result, slap) + } + } + + return +} + +func (bg *BipartiteGraph) findDisjointSLAP( + start Node, + matching EdgeSet, + guideLayers []NodeOrderedSet, + used map[Node]bool, +) ([]Edge, bool) { + return bg.findDisjointSLAPHelper(start, EdgeSet{}, len(guideLayers)-1, matching, guideLayers, used) +} + +func (bg *BipartiteGraph) findDisjointSLAPHelper( + currentNode Node, + currentSLAP EdgeSet, + currentLevel int, + matching EdgeSet, + guideLayers []NodeOrderedSet, + used map[Node]bool, +) (EdgeSet, bool) { + used[currentNode] = true + + if currentLevel == 0 { + return currentSLAP, true + } + + for _, nextNode := range guideLayers[currentLevel-1] { + if used[nextNode] { + continue + } + + edge, found := bg.Edges.FindByNodes(currentNode, nextNode) + if !found { + continue + } + + if matching.Contains(edge) == util.Odd(currentLevel) { + continue + } + + currentSLAP = append(currentSLAP, edge) + slap, found := bg.findDisjointSLAPHelper(nextNode, currentSLAP, currentLevel-1, matching, guideLayers, used) + if found { + return slap, true + } + currentSLAP = currentSLAP[:len(currentSLAP)-1] + } + + used[currentNode] = false + return nil, false +} + +func (bg *BipartiteGraph) createSLAPGuideLayers(matching EdgeSet) (guideLayers []NodeOrderedSet) { + used := make(map[Node]bool) + currentLayer := NodeOrderedSet{} + + for _, node := range bg.Left { + if matching.Free(node) { + used[node] = true + currentLayer = append(currentLayer, node) + } + } + + if len(currentLayer) == 0 { + return []NodeOrderedSet{} + } else { + guideLayers = append(guideLayers, currentLayer) + } + + done := false + + for !done { + lastLayer := currentLayer + currentLayer = NodeOrderedSet{} + + if util.Odd(len(guideLayers)) { + for _, leftNode := range lastLayer { + for _, rightNode := range bg.Right { + if used[rightNode] { + continue + } + + edge, found := bg.Edges.FindByNodes(leftNode, rightNode) + if !found || matching.Contains(edge) { + continue + } + + currentLayer = append(currentLayer, rightNode) + used[rightNode] = true + + if matching.Free(rightNode) { + done = true + } + } + } + } else { + for _, rightNode := range lastLayer { + for _, leftNode := range bg.Left { + if used[leftNode] { + continue + } + + edge, found := bg.Edges.FindByNodes(leftNode, rightNode) + if !found || !matching.Contains(edge) { + continue + } + + currentLayer = append(currentLayer, leftNode) + used[leftNode] = true + } + } + + } + + if len(currentLayer) == 0 { + return []NodeOrderedSet{} + } else { + guideLayers = append(guideLayers, currentLayer) + } + } + + return +} diff --git a/vendor/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go b/vendor/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go new file mode 100644 index 0000000..4fd15cc --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go @@ -0,0 +1,61 @@ +package edge + +import . "github.com/onsi/gomega/matchers/support/goraph/node" + +type Edge struct { + Node1 Node + Node2 Node +} + +type EdgeSet []Edge + +func (ec EdgeSet) Free(node Node) bool { + for _, e := range ec { + if e.Node1 == node || e.Node2 == node { + return false + } + } + + return true +} + +func (ec EdgeSet) Contains(edge Edge) bool { + for _, e := range ec { + if e == edge { + return true + } + } + + return false +} + +func (ec EdgeSet) FindByNodes(node1, node2 Node) (Edge, bool) { + for _, e := range ec { + if (e.Node1 == node1 && e.Node2 == node2) || (e.Node1 == node2 && e.Node2 == node1) { + return e, true + } + } + + return Edge{}, false +} + +func (ec EdgeSet) SymmetricDifference(ec2 EdgeSet) EdgeSet { + edgesToInclude := make(map[Edge]bool) + + for _, e := range ec { + edgesToInclude[e] = true + } + + for _, e := range ec2 { + edgesToInclude[e] = !edgesToInclude[e] + } + + result := EdgeSet{} + for e, include := range edgesToInclude { + if include { + result = append(result, e) + } + } + + return result +} diff --git a/vendor/github.com/onsi/gomega/matchers/support/goraph/node/node.go b/vendor/github.com/onsi/gomega/matchers/support/goraph/node/node.go new file mode 100644 index 0000000..800c2ea --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/support/goraph/node/node.go @@ -0,0 +1,7 @@ +package node + +type Node struct { + Id int +} + +type NodeOrderedSet []Node diff --git a/vendor/github.com/onsi/gomega/matchers/support/goraph/util/util.go b/vendor/github.com/onsi/gomega/matchers/support/goraph/util/util.go new file mode 100644 index 0000000..a24cd27 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/support/goraph/util/util.go @@ -0,0 +1,7 @@ +package util + +import "math" + +func Odd(n int) bool { + return math.Mod(float64(n), 2.0) == 1.0 +} diff --git a/vendor/github.com/onsi/gomega/matchers/type_support.go b/vendor/github.com/onsi/gomega/matchers/type_support.go new file mode 100644 index 0000000..ef9b448 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/type_support.go @@ -0,0 +1,165 @@ +/* +Gomega matchers + +This package implements the Gomega matchers and does not typically need to be imported. +See the docs for Gomega for documentation on the matchers + +http://onsi.github.io/gomega/ +*/ +package matchers + +import ( + "fmt" + "reflect" +) + +type omegaMatcher interface { + Match(actual interface{}) (success bool, err error) + FailureMessage(actual interface{}) (message string) + NegatedFailureMessage(actual interface{}) (message string) +} + +func isBool(a interface{}) bool { + return reflect.TypeOf(a).Kind() == reflect.Bool +} + +func isNumber(a interface{}) bool { + if a == nil { + return false + } + kind := reflect.TypeOf(a).Kind() + return reflect.Int <= kind && kind <= reflect.Float64 +} + +func isInteger(a interface{}) bool { + kind := reflect.TypeOf(a).Kind() + return reflect.Int <= kind && kind <= reflect.Int64 +} + +func isUnsignedInteger(a interface{}) bool { + kind := reflect.TypeOf(a).Kind() + return reflect.Uint <= kind && kind <= reflect.Uint64 +} + +func isFloat(a interface{}) bool { + kind := reflect.TypeOf(a).Kind() + return reflect.Float32 <= kind && kind <= reflect.Float64 +} + +func toInteger(a interface{}) int64 { + if isInteger(a) { + return reflect.ValueOf(a).Int() + } else if isUnsignedInteger(a) { + return int64(reflect.ValueOf(a).Uint()) + } else if isFloat(a) { + return int64(reflect.ValueOf(a).Float()) + } else { + panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a)) + } +} + +func toUnsignedInteger(a interface{}) uint64 { + if isInteger(a) { + return uint64(reflect.ValueOf(a).Int()) + } else if isUnsignedInteger(a) { + return reflect.ValueOf(a).Uint() + } else if isFloat(a) { + return uint64(reflect.ValueOf(a).Float()) + } else { + panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a)) + } +} + +func toFloat(a interface{}) float64 { + if isInteger(a) { + return float64(reflect.ValueOf(a).Int()) + } else if isUnsignedInteger(a) { + return float64(reflect.ValueOf(a).Uint()) + } else if isFloat(a) { + return reflect.ValueOf(a).Float() + } else { + panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a)) + } +} + +func isError(a interface{}) bool { + _, ok := a.(error) + return ok +} + +func isChan(a interface{}) bool { + if isNil(a) { + return false + } + return reflect.TypeOf(a).Kind() == reflect.Chan +} + +func isMap(a interface{}) bool { + if a == nil { + return false + } + return reflect.TypeOf(a).Kind() == reflect.Map +} + +func isArrayOrSlice(a interface{}) bool { + if a == nil { + return false + } + switch reflect.TypeOf(a).Kind() { + case reflect.Array, reflect.Slice: + return true + default: + return false + } +} + +func isString(a interface{}) bool { + if a == nil { + return false + } + return reflect.TypeOf(a).Kind() == reflect.String +} + +func toString(a interface{}) (string, bool) { + aString, isString := a.(string) + if isString { + return aString, true + } + + aBytes, isBytes := a.([]byte) + if isBytes { + return string(aBytes), true + } + + aStringer, isStringer := a.(fmt.Stringer) + if isStringer { + return aStringer.String(), true + } + + return "", false +} + +func lengthOf(a interface{}) (int, bool) { + if a == nil { + return 0, false + } + switch reflect.TypeOf(a).Kind() { + case reflect.Map, reflect.Array, reflect.String, reflect.Chan, reflect.Slice: + return reflect.ValueOf(a).Len(), true + default: + return 0, false + } +} + +func isNil(a interface{}) bool { + if a == nil { + return true + } + + switch reflect.TypeOf(a).Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return reflect.ValueOf(a).IsNil() + } + + return false +} diff --git a/vendor/github.com/onsi/gomega/types/types.go b/vendor/github.com/onsi/gomega/types/types.go new file mode 100644 index 0000000..1c632ad --- /dev/null +++ b/vendor/github.com/onsi/gomega/types/types.go @@ -0,0 +1,17 @@ +package types + +type GomegaFailHandler func(message string, callerSkip ...int) + +//A simple *testing.T interface wrapper +type GomegaTestingT interface { + Errorf(format string, args ...interface{}) +} + +//All Gomega matchers must implement the GomegaMatcher interface +// +//For details on writing custom matchers, check out: http://onsi.github.io/gomega/#adding_your_own_matchers +type GomegaMatcher interface { + Match(actual interface{}) (success bool, err error) + FailureMessage(actual interface{}) (message string) + NegatedFailureMessage(actual interface{}) (message string) +} From 2327a4e3d2df16889f3c0c26a56c9b079c48ed28 Mon Sep 17 00:00:00 2001 From: Daniel Hess Date: Mon, 24 Oct 2016 21:51:18 +0000 Subject: [PATCH 3/4] travis yml formatting --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7e92878..a90c715 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: -- 1.2 -- tip + - 1.2 + - tip notifications: slack: secure: hxwTTgGH72xCs2DorDns8Q9jAoG9KU4v76t/8gmebOPXeLEp7znRRWS4Ep04vwGzFmAlntZLbgQuAjkFqz4P/oOcuSd5Ts0V+Rjyi2Wv7OrnoPSXBf6TjEmVLbJ87B8PzKBNwRMCD6FP0TTLw4c0v0VeQfvDwrE+Aj9Z9xXlS/Y= From 816dc534ef11e3ff92ed6f0a631a006de2d51675 Mon Sep 17 00:00:00 2001 From: Daniel Hess Date: Tue, 25 Oct 2016 00:58:14 +0000 Subject: [PATCH 4/4] adding specific go test action to travis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index a90c715..24b312c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,8 @@ language: go go: - 1.2 - tip +script: + - go test -v $(go list ./... | grep -v /vendor/) notifications: slack: secure: hxwTTgGH72xCs2DorDns8Q9jAoG9KU4v76t/8gmebOPXeLEp7znRRWS4Ep04vwGzFmAlntZLbgQuAjkFqz4P/oOcuSd5Ts0V+Rjyi2Wv7OrnoPSXBf6TjEmVLbJ87B8PzKBNwRMCD6FP0TTLw4c0v0VeQfvDwrE+Aj9Z9xXlS/Y=