From 0f06eb259592287a48b4aca216d6e7598319fde6 Mon Sep 17 00:00:00 2001 From: "Timothy St. Clair" Date: Tue, 3 May 2016 10:50:25 -0500 Subject: [PATCH] Update godep for ginkgo and gomega to fix file based exclusion. #24140 --- Godeps/Godeps.json | 8 +- .../src/github.com/onsi/ginkgo/.travis.yml | 2 +- .../github.com/onsi/ginkgo/config/config.go | 28 +++-- .../onsi/ginkgo/ginkgo/bootstrap_command.go | 21 +++- .../onsi/ginkgo/ginkgo/generate_command.go | 17 +-- .../ginkgo/ginkgo/testrunner/test_runner.go | 6 +- .../ginkgo/ginkgo/testsuite/test_suite.go | 7 +- .../ginkgo/testsuite/vendor_check_go15.go | 16 +++ .../ginkgo/testsuite/vendor_check_go16.go | 15 +++ .../onsi/ginkgo/internal/spec/specs.go | 22 +++- .../onsi/ginkgo/internal/suite/suite.go | 2 +- .../src/github.com/onsi/gomega/.gitignore | 2 + .../src/github.com/onsi/gomega/.travis.yml | 5 +- .../src/github.com/onsi/gomega/CHANGELOG.md | 10 ++ .../github.com/onsi/gomega/gexec/session.go | 2 +- .../github.com/onsi/gomega/ghttp/handlers.go | 118 +++++++++++++++++- .../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 | 47 ++++++- .../asyncassertion/async_assertion.go | 12 +- .../internal/oraclematcher/oracle_matcher.go | 25 ++++ .../src/github.com/onsi/gomega/matchers.go | 83 +++++++++++- .../github.com/onsi/gomega/matchers/and.go | 64 ++++++++++ .../matchers/assignable_to_type_of_matcher.go | 5 +- .../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_identical_to.go | 37 ++++++ .../matchers/contain_element_matcher.go | 9 +- .../onsi/gomega/matchers/equal_matcher.go | 5 +- .../onsi/gomega/matchers/have_cap_matcher.go | 28 +++++ .../gomega/matchers/have_occurred_matcher.go | 12 +- .../gomega/matchers/have_suffix_matcher.go | 2 +- .../gomega/matchers/match_json_matcher.go | 21 ++-- .../github.com/onsi/gomega/matchers/not.go | 30 +++++ .../src/github.com/onsi/gomega/matchers/or.go | 67 ++++++++++ .../onsi/gomega/matchers/succeed_matcher.go | 11 +- .../matchers/support/goraph/util/util.go | 4 +- .../onsi/gomega/matchers/type_support.go | 11 ++ .../onsi/gomega/matchers/with_transform.go | 72 +++++++++++ 41 files changed, 950 insertions(+), 89 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go15.go create mode 100644 Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go16.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/protobuf.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.pb.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.proto create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/internal/oraclematcher/oracle_matcher.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/and.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_directory.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_regular_file.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_an_existing_file.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_identical_to.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_cap_matcher.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/not.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/or.go create mode 100644 Godeps/_workspace/src/github.com/onsi/gomega/matchers/with_transform.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index fc7468db1bd..d28e5118f98 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -922,13 +922,13 @@ }, { "ImportPath": "github.com/onsi/ginkgo", - "Comment": "v1.2.0-42-g07d85e6", - "Rev": "07d85e6b10c4289c7d612f9b13f45ba36f66d55b" + "Comment": "v1.2.0-52-g2c2e9bb", + "Rev": "2c2e9bb47b4e44067024f29339588cac8b34dd12" }, { "ImportPath": "github.com/onsi/gomega", - "Comment": "v1.0-28-g8adf9e1", - "Rev": "8adf9e1730c55cdc590de7d49766cb2acc88d8f2" + "Comment": "v1.0-91-g7ce781e", + "Rev": "7ce781ea776b2fd506491011353bded2e40c8467" }, { "ImportPath": "github.com/opencontainers/runc/libcontainer", diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/.travis.yml b/Godeps/_workspace/src/github.com/onsi/ginkgo/.travis.yml index f0e67b84abe..b95cd2098e3 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/.travis.yml +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/.travis.yml @@ -1,8 +1,8 @@ language: go go: - - 1.3 - 1.4 - 1.5 + - 1.6 - tip install: diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/config/config.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/config/config.go index 7b22e34ad97..410f7b7ac25 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/config/config.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/config/config.go @@ -23,15 +23,16 @@ import ( const VERSION = "1.2.0" type GinkgoConfigType struct { - RandomSeed int64 - RandomizeAllSpecs bool - FocusString string - SkipString string - SkipMeasurements bool - FailOnPending bool - FailFast bool - EmitSpecProgress bool - DryRun bool + RandomSeed int64 + RandomizeAllSpecs bool + RegexScansFilePath bool + FocusString string + SkipString string + SkipMeasurements bool + FailOnPending bool + FailFast bool + EmitSpecProgress bool + DryRun bool ParallelNode int ParallelTotal int @@ -66,9 +67,14 @@ func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) { flagSet.BoolVar(&(GinkgoConfig.SkipMeasurements), prefix+"skipMeasurements", false, "If set, ginkgo will skip any measurement specs.") flagSet.BoolVar(&(GinkgoConfig.FailOnPending), prefix+"failOnPending", false, "If set, ginkgo will mark the test suite as failed if any specs are pending.") flagSet.BoolVar(&(GinkgoConfig.FailFast), prefix+"failFast", false, "If set, ginkgo will stop running a test suite after a failure occurs.") + flagSet.BoolVar(&(GinkgoConfig.DryRun), prefix+"dryRun", false, "If set, ginkgo will walk the test hierarchy without actually running anything. Best paired with -v.") + flagSet.StringVar(&(GinkgoConfig.FocusString), prefix+"focus", "", "If set, ginkgo will only run specs that match this regular expression.") flagSet.StringVar(&(GinkgoConfig.SkipString), prefix+"skip", "", "If set, ginkgo will only run specs that do not match this regular expression.") + + flagSet.BoolVar(&(GinkgoConfig.RegexScansFilePath), prefix+"regexScansFilePath", false, "If set, ginkgo regex matching also will look at the file path (code location).") + flagSet.BoolVar(&(GinkgoConfig.EmitSpecProgress), prefix+"progress", false, "If set, ginkgo will emit progress information as each spec runs to the GinkgoWriter.") if includeParallelFlags { @@ -142,6 +148,10 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor result = append(result, fmt.Sprintf("--%sparallel.synchost=%s", prefix, ginkgo.SyncHost)) } + if ginkgo.RegexScansFilePath { + result = append(result, fmt.Sprintf("--%sregexScansFilePath", prefix)) + } + if reporter.NoColor { result = append(result, fmt.Sprintf("--%snoColor", prefix)) } diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go index d804fe00ca2..361afc34c56 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go @@ -15,10 +15,11 @@ import ( ) func BuildBootstrapCommand() *Command { - var agouti, noDot bool + var agouti, noDot, internal bool flagSet := flag.NewFlagSet("bootstrap", flag.ExitOnError) flagSet.BoolVar(&agouti, "agouti", false, "If set, bootstrap will generate a bootstrap file for writing Agouti tests") flagSet.BoolVar(&noDot, "nodot", false, "If set, bootstrap will generate a bootstrap file that does not . import ginkgo and gomega") + flagSet.BoolVar(&internal, "internal", false, "If set, generate will generate a test file that uses the regular package name") return &Command{ Name: "bootstrap", @@ -29,12 +30,12 @@ func BuildBootstrapCommand() *Command { "Accepts the following flags:", }, Command: func(args []string, additionalArgs []string) { - generateBootstrap(agouti, noDot) + generateBootstrap(agouti, noDot, internal) }, } } -var bootstrapText = `package {{.Package}}_test +var bootstrapText = `package {{.Package}} import ( {{.GinkgoImport}} @@ -49,7 +50,7 @@ func Test{{.FormattedName}}(t *testing.T) { } ` -var agoutiBootstrapText = `package {{.Package}}_test +var agoutiBootstrapText = `package {{.Package}} import ( {{.GinkgoImport}} @@ -115,6 +116,14 @@ func prettifyPackageName(name string) string { return name } +func determinePackageName(name string, internal bool) string { + if internal { + return name + } + + return name + "_test" +} + func fileExists(path string) bool { _, err := os.Stat(path) if err == nil { @@ -123,10 +132,10 @@ func fileExists(path string) bool { return false } -func generateBootstrap(agouti bool, noDot bool) { +func generateBootstrap(agouti, noDot, internal bool) { packageName, bootstrapFilePrefix, formattedName := getPackageAndFormattedName() data := bootstrapData{ - Package: packageName, + Package: determinePackageName(packageName, internal), FormattedName: formattedName, GinkgoImport: `. "github.com/onsi/ginkgo"`, GomegaImport: `. "github.com/onsi/gomega"`, diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/generate_command.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/generate_command.go index 7dd3b4da261..95c3cf6851a 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/generate_command.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/generate_command.go @@ -10,10 +10,11 @@ import ( ) func BuildGenerateCommand() *Command { - var agouti, noDot bool + var agouti, noDot, internal bool flagSet := flag.NewFlagSet("generate", flag.ExitOnError) flagSet.BoolVar(&agouti, "agouti", false, "If set, generate will generate a test file for writing Agouti tests") flagSet.BoolVar(&noDot, "nodot", false, "If set, generate will generate a test file that does not . import ginkgo and gomega") + flagSet.BoolVar(&internal, "internal", false, "If set, generate will generate a test file that uses the regular package name") return &Command{ Name: "generate", @@ -25,12 +26,12 @@ func BuildGenerateCommand() *Command { "Accepts the following flags:", }, Command: func(args []string, additionalArgs []string) { - generateSpec(args, agouti, noDot) + generateSpec(args, agouti, noDot, internal) }, } } -var specText = `package {{.Package}}_test +var specText = `package {{.Package}} import ( . "{{.PackageImportPath}}" @@ -77,9 +78,9 @@ type specData struct { IncludeImports bool } -func generateSpec(args []string, agouti, noDot bool) { +func generateSpec(args []string, agouti, noDot, internal bool) { if len(args) == 0 { - err := generateSpecForSubject("", agouti, noDot) + err := generateSpecForSubject("", agouti, noDot, internal) if err != nil { fmt.Println(err.Error()) fmt.Println("") @@ -91,7 +92,7 @@ func generateSpec(args []string, agouti, noDot bool) { var failed bool for _, arg := range args { - err := generateSpecForSubject(arg, agouti, noDot) + err := generateSpecForSubject(arg, agouti, noDot, internal) if err != nil { failed = true fmt.Println(err.Error()) @@ -103,7 +104,7 @@ func generateSpec(args []string, agouti, noDot bool) { } } -func generateSpecForSubject(subject string, agouti, noDot bool) error { +func generateSpecForSubject(subject string, agouti, noDot, internal bool) error { packageName, specFilePrefix, formattedName := getPackageAndFormattedName() if subject != "" { subject = strings.Split(subject, ".go")[0] @@ -113,7 +114,7 @@ func generateSpecForSubject(subject string, agouti, noDot bool) error { } data := specData{ - Package: packageName, + Package: determinePackageName(packageName, internal), Subject: formattedName, PackageImportPath: getPackageImportPath(), IncludeImports: !noDot, diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go index a1e47ba18b3..cc9805c9900 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go @@ -72,7 +72,7 @@ func (t *TestRunner) CompileTo(path string) error { return nil } - args := []string{"test", "-c", "-i", "-o", path} + args := []string{"test", "-c", "-i", "-o", path, t.Suite.Path} if t.race { args = append(args, "-race") } @@ -88,8 +88,6 @@ func (t *TestRunner) CompileTo(path string) error { cmd := exec.Command("go", args...) - cmd.Dir = t.Suite.Path - output, err := cmd.CombinedOutput() if err != nil { @@ -101,7 +99,7 @@ func (t *TestRunner) CompileTo(path string) error { } if fileExists(path) == false { - compiledFile := filepath.Join(t.Suite.Path, t.Suite.PackageName+".test") + compiledFile := t.Suite.PackageName + ".test" if fileExists(compiledFile) { // seems like we are on an old go version that does not support the -o flag on go test // move the compiled test file to the desired location by hand diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go index 4ef3bc44ff1..3046ce519f6 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go @@ -4,7 +4,6 @@ import ( "errors" "io/ioutil" "os" - "path" "path/filepath" "regexp" "strings" @@ -49,11 +48,7 @@ func PrecompiledTestSuite(path string) (TestSuite, error) { func SuitesInDir(dir string, recurse bool) []TestSuite { suites := []TestSuite{} - // "This change will only be enabled if the go command is run with - // GO15VENDOREXPERIMENT=1 in its environment." - // c.f. the vendor-experiment proposal https://goo.gl/2ucMeC - vendorExperiment := os.Getenv("GO15VENDOREXPERIMENT") - if (vendorExperiment == "1") && path.Base(dir) == "vendor" { + if vendorExperimentCheck(dir) { return suites } diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go15.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go15.go new file mode 100644 index 00000000000..75f827a121b --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go15.go @@ -0,0 +1,16 @@ +// +build !go1.6 + +package testsuite + +import ( + "os" + "path" +) + +// "This change will only be enabled if the go command is run with +// GO15VENDOREXPERIMENT=1 in its environment." +// c.f. the vendor-experiment proposal https://goo.gl/2ucMeC +func vendorExperimentCheck(dir string) bool { + vendorExperiment := os.Getenv("GO15VENDOREXPERIMENT") + return vendorExperiment == "1" && path.Base(dir) == "vendor" +} diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go16.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go16.go new file mode 100644 index 00000000000..596e5e5c180 --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go16.go @@ -0,0 +1,15 @@ +// +build go1.6 + +package testsuite + +import ( + "os" + "path" +) + +// in 1.6 the vendor directory became the default go behaviour, so now +// check if its disabled. +func vendorExperimentCheck(dir string) bool { + vendorExperiment := os.Getenv("GO15VENDOREXPERIMENT") + return vendorExperiment != "0" && path.Base(dir) == "vendor" +} diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/spec/specs.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/spec/specs.go index 9c671e39f85..6f4fbd36250 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/spec/specs.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/spec/specs.go @@ -10,6 +10,7 @@ type Specs struct { specs []*Spec numberOfOriginalSpecs int hasProgrammaticFocus bool + RegexScansFilePath bool } func NewSpecs(specs []*Spec) *Specs { @@ -45,7 +46,7 @@ func (e *Specs) ApplyFocus(description string, focusString string, skipString st if focusString == "" && skipString == "" { e.applyProgrammaticFocus() } else { - e.applyRegExpFocus(description, focusString, skipString) + e.applyRegExpFocusAndSkip(description, focusString, skipString) } } @@ -67,12 +68,27 @@ func (e *Specs) applyProgrammaticFocus() { } } -func (e *Specs) applyRegExpFocus(description string, focusString string, skipString string) { +// toMatch returns a byte[] to be used by regex matchers. When adding new behaviours to the matching function, +// this is the place which we append to. +func (e *Specs) toMatch(description string, spec *Spec) []byte { + if e.RegexScansFilePath { + return []byte( + description + " " + + spec.ConcatenatedString() + " " + + spec.subject.CodeLocation().FileName) + } else { + return []byte( + description + " " + + spec.ConcatenatedString()) + } +} + +func (e *Specs) applyRegExpFocusAndSkip(description string, focusString string, skipString string) { for _, spec := range e.specs { matchesFocus := true matchesSkip := false - toMatch := []byte(description + " " + spec.ConcatenatedString()) + toMatch := e.toMatch(description, spec) if focusString != "" { focusFilter := regexp.MustCompile(focusString) diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/suite/suite.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/suite/suite.go index a054602f78b..949bd34f5e1 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/suite/suite.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/suite/suite.go @@ -71,6 +71,7 @@ func (suite *Suite) generateSpecs(description string, config config.GinkgoConfig } specs := spec.NewSpecs(specsSlice) + specs.RegexScansFilePath = config.RegexScansFilePath if config.RandomizeAllSpecs { specs.Shuffle(rand.New(rand.NewSource(config.RandomSeed))) @@ -85,7 +86,6 @@ func (suite *Suite) generateSpecs(description string, config config.GinkgoConfig if config.ParallelTotal > 1 { specs.TrimForParallelization(config.ParallelTotal, config.ParallelNode) } - return specs } diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore b/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore index 55145320362..720c13cba84 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore +++ b/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore @@ -1,3 +1,5 @@ .DS_Store *.test . +.idea +gomega.iml diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/.travis.yml b/Godeps/_workspace/src/github.com/onsi/gomega/.travis.yml index 2ecdf95a534..3b26c7c0b30 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/.travis.yml +++ b/Godeps/_workspace/src/github.com/onsi/gomega/.travis.yml @@ -1,7 +1,8 @@ language: go go: - - 1.3 - + - 1.5 + - tip + install: - go get -v ./... - go get github.com/onsi/ginkgo diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/CHANGELOG.md b/Godeps/_workspace/src/github.com/onsi/gomega/CHANGELOG.md index ea269cab1fc..0c5ede5d828 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/CHANGELOG.md +++ b/Godeps/_workspace/src/github.com/onsi/gomega/CHANGELOG.md @@ -8,6 +8,16 @@ Improvements: - 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) diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/gexec/session.go b/Godeps/_workspace/src/github.com/onsi/gomega/gexec/session.go index 460cfe58846..46e712235d8 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/gexec/session.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/gexec/session.go @@ -137,7 +137,7 @@ 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()) + EventuallyWithOffset(1, s, timeout...).Should(Exit()) return s } diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/handlers.go b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/handlers.go index 8c1668a3626..63ff6919ad3 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/handlers.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/handlers.go @@ -6,7 +6,10 @@ import ( "fmt" "io/ioutil" "net/http" + "net/url" + "reflect" + "github.com/golang/protobuf/proto" . "github.com/onsi/gomega" "github.com/onsi/gomega/types" ) @@ -36,7 +39,10 @@ func VerifyRequest(method string, path interface{}, rawQuery ...string) http.Han Ω(req.URL.Path).Should(Equal(path), "Path mismatch") } if len(rawQuery) > 0 { - Ω(req.URL.RawQuery).Should(Equal(rawQuery[0]), "RawQuery mismatch") + values, err := url.ParseQuery(rawQuery[0]) + Ω(err).ShouldNot(HaveOccurred(), "Expected RawQuery is malformed") + + Ω(req.URL.Query()).Should(Equal(values), "RawQuery mismatch") } } } @@ -84,6 +90,19 @@ 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 // @@ -112,6 +131,53 @@ func VerifyJSONRepresenting(object interface{}) http.HandlerFunc { ) } +//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 @@ -179,7 +245,17 @@ Also, RespondWithJSONEncoded can be given an optional http.Header. The 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...) + + 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) } /* @@ -192,14 +268,46 @@ 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 { +func RespondWithJSONEncodedPtr(statusCode *int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { - data, err := json.Marshal(*object) + data, err := json.Marshal(object) Ω(err).ShouldNot(HaveOccurred()) + var headers http.Header if len(optionalHeader) == 1 { - copyHeader(optionalHeader[0], w.Header()) + 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 new file mode 100644 index 00000000000..b2972bc9fb0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/protobuf.go @@ -0,0 +1,3 @@ +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 new file mode 100644 index 00000000000..c55a48448f2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.pb.go @@ -0,0 +1,55 @@ +// 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 new file mode 100644 index 00000000000..35b7145c247 --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/protobuf/simple_message.proto @@ -0,0 +1,9 @@ +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 index 4bbb2e41def..f5f537eb5e9 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/test_server.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/ghttp/test_server.go @@ -106,11 +106,14 @@ A more comprehensive example is available at https://onsi.github.io/gomega/#_tes package ghttp import ( + "fmt" + "io" "io/ioutil" "net/http" "net/http/httptest" "reflect" "regexp" + "strings" "sync" . "github.com/onsi/gomega" @@ -164,6 +167,11 @@ type Server struct { //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 @@ -208,9 +216,35 @@ func (s *Server) Close() { func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { s.writeLock.Lock() defer func() { - recover() + 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() @@ -315,6 +349,17 @@ func (s *Server) GetHandler(index int) http.HandlerFunc { return s.requestHandlers[index] } +func (s *Server) Reset() { + s.writeLock.Lock() + defer s.writeLock.Unlock() + + s.HTTPTestServer.CloseClientConnections() + s.calls = 0 + s.receivedRequests = nil + s.requestHandlers = nil + s.routedHandlers = nil +} + //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. 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 index 7bbec43b506..bce0853006a 100644 --- 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 @@ -6,6 +6,7 @@ import ( "reflect" "time" + "github.com/onsi/gomega/internal/oraclematcher" "github.com/onsi/gomega/types" ) @@ -86,21 +87,12 @@ func (assertion *AsyncAssertion) pollActual() (interface{}, error) { 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) + return oraclematcher.MatchMayChangeInTheFuture(matcher, value) } func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { 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 new file mode 100644 index 00000000000..66cad88a1fb --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/internal/oraclematcher/oracle_matcher.go @@ -0,0 +1,25 @@ +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/matchers.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers.go index 307f88a4fed..12d2b967f5b 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers.go @@ -26,6 +26,15 @@ func BeEquivalentTo(expected interface{}) types.GomegaMatcher { } } +//BeIdenticalTo uses the == operator to compare actual with expected. +//BeIdenticalTo is strict about types when performing comparisons. +//It is an error for both actual and expected to be nil. Use BeNil() instead. +func BeIdenticalTo(expected interface{}) types.GomegaMatcher { + return &matchers.BeIdenticalToMatcher{ + Expected: expected, + } +} + //BeNil succeeds if actual is nil func BeNil() types.GomegaMatcher { return &matchers.BeNilMatcher{} @@ -217,6 +226,13 @@ func HaveLen(count int) types.GomegaMatcher { } } +//HaveCap succeeds if actual has the passed-in capacity. Actual must be of type array, chan, or slice. +func HaveCap(count int) types.GomegaMatcher { + return &matchers.HaveCapMatcher{ + 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{} @@ -250,7 +266,6 @@ func ContainElement(element interface{}) types.GomegaMatcher { // Ω([]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, @@ -326,3 +341,69 @@ func BeAssignableToTypeOf(expected interface{}) types.GomegaMatcher { 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 new file mode 100644 index 00000000000..94c42a7db71 --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/and.go @@ -0,0 +1,64 @@ +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 index 7f8897b3c0d..89a1fc2116b 100644 --- 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 @@ -2,8 +2,9 @@ package matchers import ( "fmt" - "github.com/onsi/gomega/format" "reflect" + + "github.com/onsi/gomega/format" ) type AssignableToTypeOfMatcher struct { @@ -12,7 +13,7 @@ type AssignableToTypeOfMatcher struct { func (matcher *AssignableToTypeOfMatcher) Match(actual interface{}) (success bool, err error) { if actual == nil || matcher.Expected == nil { - return false, fmt.Errorf("Refusing to compare to .") + 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) 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 new file mode 100644 index 00000000000..7b6975e41e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_directory.go @@ -0,0 +1,54 @@ +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 new file mode 100644 index 00000000000..e239131fb61 --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_a_regular_file.go @@ -0,0 +1,54 @@ +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 new file mode 100644 index 00000000000..d42eba22344 --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_an_existing_file.go @@ -0,0 +1,38 @@ +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_identical_to.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_identical_to.go new file mode 100644 index 00000000000..fdcda4d1fb9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/be_identical_to.go @@ -0,0 +1,37 @@ +package matchers + +import ( + "fmt" + "runtime" + + "github.com/onsi/gomega/format" +) + +type BeIdenticalToMatcher struct { + Expected interface{} +} + +func (matcher *BeIdenticalToMatcher) Match(actual interface{}) (success bool, matchErr 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.") + } + + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + success = false + matchErr = nil + } + } + }() + + return actual == matcher.Expected, nil +} + +func (matcher *BeIdenticalToMatcher) FailureMessage(actual interface{}) string { + return format.Message(actual, "to be identical to", matcher.Expected) +} + +func (matcher *BeIdenticalToMatcher) NegatedFailureMessage(actual interface{}) string { + return format.Message(actual, "not to be identical to", matcher.Expected) +} 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 index 014a20ffb62..4159335d0d5 100644 --- 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 @@ -2,8 +2,9 @@ package matchers import ( "fmt" - "github.com/onsi/gomega/format" "reflect" + + "github.com/onsi/gomega/format" ) type ContainElementMatcher struct { @@ -25,6 +26,7 @@ func (matcher *ContainElementMatcher) Match(actual interface{}) (success bool, e if isMap(actual) { keys = value.MapKeys() } + var lastError error for i := 0; i < value.Len(); i++ { var success bool var err error @@ -34,14 +36,15 @@ func (matcher *ContainElementMatcher) Match(actual interface{}) (success bool, e 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()) + lastError = err + continue } if success { return true, nil } } - return false, nil + return false, lastError } func (matcher *ContainElementMatcher) FailureMessage(actual interface{}) (message string) { 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 index 9f8f80a8973..d186597379b 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/equal_matcher.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/equal_matcher.go @@ -2,8 +2,9 @@ package matchers import ( "fmt" - "github.com/onsi/gomega/format" "reflect" + + "github.com/onsi/gomega/format" ) type EqualMatcher struct { @@ -12,7 +13,7 @@ type EqualMatcher struct { 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 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 } diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_cap_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_cap_matcher.go new file mode 100644 index 00000000000..7ace93dc36d --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_cap_matcher.go @@ -0,0 +1,28 @@ +package matchers + +import ( + "fmt" + + "github.com/onsi/gomega/format" +) + +type HaveCapMatcher struct { + Count int +} + +func (matcher *HaveCapMatcher) Match(actual interface{}) (success bool, err error) { + length, ok := capOf(actual) + if !ok { + return false, fmt.Errorf("HaveCap matcher expects a array/channel/slice. Got:\n%s", format.Object(actual, 1)) + } + + return length == matcher.Count, nil +} + +func (matcher *HaveCapMatcher) FailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("Expected\n%s\nto have capacity %d", format.Object(actual, 1), matcher.Count) +} + +func (matcher *HaveCapMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("Expected\n%s\nnot to have capacity %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 index b5095f1147b..ebdd71786d8 100644 --- 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 @@ -2,6 +2,7 @@ package matchers import ( "fmt" + "github.com/onsi/gomega/format" ) @@ -9,19 +10,22 @@ type HaveOccurredMatcher struct { } func (matcher *HaveOccurredMatcher) Match(actual interface{}) (success bool, err error) { + // is purely nil? if actual == nil { return false, nil } - if isError(actual) { - return true, nil + // must be an 'error' type + if !isError(actual) { + return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1)) } - return false, fmt.Errorf("Expected an error. Got:\n%s", format.Object(actual, 1)) + // must be non-nil (or a pointer to a non-nil) + return !isNil(actual), nil } func (matcher *HaveOccurredMatcher) FailureMessage(actual interface{}) (message string) { - return fmt.Sprintf("Expected an error to have occured. Got:\n%s", format.Object(actual, 1)) + return fmt.Sprintf("Expected an error to have occurred. Got:\n%s", format.Object(actual, 1)) } func (matcher *HaveOccurredMatcher) NegatedFailureMessage(actual interface{}) (message string) { 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 index eb1b284da13..afc78fc9019 100644 --- 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 @@ -16,7 +16,7 @@ func (matcher *HaveSuffixMatcher) Match(actual interface{}) (success bool, err e 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 + return len(actualString) >= len(suffix) && actualString[len(actualString)-len(suffix):] == suffix, nil } func (matcher *HaveSuffixMatcher) suffix() string { 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 index bedf8510268..e61978a1de8 100644 --- 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 @@ -4,8 +4,9 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/onsi/gomega/format" "reflect" + + "github.com/onsi/gomega/format" ) type MatchJSONMatcher struct { @@ -39,23 +40,25 @@ func (matcher *MatchJSONMatcher) NegatedFailureMessage(actual interface{}) (mess } 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)) + actualString, ok := toString(actual) + if !ok { + return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1)) + } + expectedString, ok := toString(matcher.JSONToMatch) + if !ok { + return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string, stringer, or []byte. Got expected:\n%s", format.Object(matcher.JSONToMatch, 1)) } abuf := new(bytes.Buffer) ebuf := new(bytes.Buffer) if err := json.Indent(abuf, []byte(actualString), "", " "); err != nil { - return "", "", err + return "", "", fmt.Errorf("Actual '%s' should be valid JSON, but it is not.\nUnderlying error:%s", actualString, err) } if err := json.Indent(ebuf, []byte(expectedString), "", " "); err != nil { - return "", "", err + return "", "", fmt.Errorf("Expected '%s' should be valid JSON, but it is not.\nUnderlying error:%s", expectedString, err) } - return actualString, expectedString, nil + return abuf.String(), ebuf.String(), nil } diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/not.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/not.go new file mode 100644 index 00000000000..2c91670bd9b --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/not.go @@ -0,0 +1,30 @@ +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 new file mode 100644 index 00000000000..3bf7998001d --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/or.go @@ -0,0 +1,67 @@ +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/succeed_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go index c162b67b7bc..721ed5529bc 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go @@ -10,19 +10,22 @@ type SucceedMatcher struct { } func (matcher *SucceedMatcher) Match(actual interface{}) (success bool, err error) { + // is purely nil? if actual == nil { return true, nil } - if isError(actual) { - return false, nil + // must be an 'error' type + if !isError(actual) { + return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1)) } - return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1)) + // must be nil (or a pointer to a nil) + return isNil(actual), nil } func (matcher *SucceedMatcher) FailureMessage(actual interface{}) (message string) { - return fmt.Sprintf("Expected success, but got an error:\n%s", format.Object(actual, 1)) + 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) { 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 index a24cd275055..d76a1ee00a2 100644 --- 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 @@ -3,5 +3,5 @@ package util import "math" func Odd(n int) bool { - return math.Mod(float64(n), 2.0) == 1.0 -} + 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 index ef9b44835ea..04020f004e6 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/type_support.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/type_support.go @@ -150,6 +150,17 @@ func lengthOf(a interface{}) (int, bool) { return 0, false } } +func capOf(a interface{}) (int, bool) { + if a == nil { + return 0, false + } + switch reflect.TypeOf(a).Kind() { + case reflect.Array, reflect.Chan, reflect.Slice: + return reflect.ValueOf(a).Cap(), true + default: + return 0, false + } +} func isNil(a interface{}) bool { if a == nil { 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 new file mode 100644 index 00000000000..8e58d8a0fb7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/with_transform.go @@ -0,0 +1,72 @@ +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) +}